FastOp vs. alternatives

Hi igorians,

I recently was faced with the easy sounding task of setting an existing wave to zero. And doing it really fast.

I played around a bit and got some, at least for me, surprising results using the following code:
Function doStuff()

    variable i, timer, numRuns, waveSize
    numRuns = 10
    waveSize = 2^24

    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            WaveClear data
    endfor
    printf "Plain creation: %g\r", stopmstimer(timer)/1e6/numRuns

    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data = 0
            WaveClear data
    endfor
    printf "Init with value: %g\r", stopmstimer(timer)/1e6/numRuns

    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            FastOp data = 0
            WaveClear data
    endfor
    printf "FastOp: %g\r", stopmstimer(timer)/1e6/numRuns

    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            data[] = 0
            WaveClear data
    endfor
    printf "Plain assignment: %g\r", stopmstimer(timer)/1e6/numRuns

    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            // not sure how to set the complete wave just to zero
            MatrixOP/FREE data = data - data
            WaveClear data
    endfor
    printf "MatrixOP (single thread): %g\r", stopmstimer(timer)/1e6/numRuns

    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            // not sure how to set the complete wave just to zero
            MatrixOP/FREE/NTHR=0 data = data - data
            WaveClear data
    endfor
    printf "MatrixOP (multithreaded): %g\r", stopmstimer(timer)/1e6/numRuns

    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            Multithread data[] = 0
            WaveClear data
    endfor
    printf "Multithread assignment: %g\r", stopmstimer(timer)/1e6/numRuns
End


On my windows box using IP6 latest I get:
•dostuff() Plain creation: 0.0398167 Init with value: 0.212837 FastOp: 0.0620453 Plain assignment: 0.268874 MatrixOP (single thread): 0.12646 MatrixOP (multithreaded): 0.12627 Multithread assignment: 0.0722515

Is FastOp really the best choice for that?
I would have expected that the multithreaded solution is the fastest one.
Your tests leave much to be desired.

First MatrixOP: The multithreading in MatrixOP is not going to kick in because you are not working with multiple layers.

FastOP: I suggest that you avoid it.

Multithread is the best approach for this task.

A.G.
WaveMetrics, Inc.
I should have also added that in IP7 you may want to explore MatrixOP functions const() and zeroMat().

A.G.
WaveMetrics, Inc.
Thanks AG!

So I'll stick with Multithread for now.
Regarding MatrixOP you are of course right.

And the new MatrixOP functions are nice!
Here is an updated test function that includes MatrixOp zeroMat() (which requires Igor 7):
Function doStuff()
 
    variable i, timer, numRuns, waveSize
    numRuns = 10
    waveSize = 2^24
 
    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            WaveClear data
    endfor
    printf "Plain creation: %g\r", stopmstimer(timer)/1e6/numRuns
 
    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data = 0
            WaveClear data
    endfor
    printf "Init with value: %g\r", stopmstimer(timer)/1e6/numRuns
 
    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            FastOp data = 0
            WaveClear data
    endfor
    printf "FastOp: %g\r", stopmstimer(timer)/1e6/numRuns
 
    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            data[] = 0
            WaveClear data
    endfor
    printf "Plain assignment: %g\r", stopmstimer(timer)/1e6/numRuns
   
#if IgorVersion() >= 7
    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data       // Not actually necessary
            // not sure how to set the complete wave just to zero
            MatrixOP/FREE data = zeroMat(waveSize, 0, 4)    // REQUIRES IGOR 7
            WaveClear data
    endfor
    printf "MatrixOP (zeroMat): %g\r", stopmstimer(timer)/1e6/numRuns
#endif
   
    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            // not sure how to set the complete wave just to zero
            MatrixOP/FREE data = data * 0
            WaveClear data
    endfor
    printf "MatrixOP (single thread): %g\r", stopmstimer(timer)/1e6/numRuns
 
    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            Multithread data[] = 0
            WaveClear data
    endfor
    printf "Multithread assignment: %g\r", stopmstimer(timer)/1e6/numRuns
End


Here are the results on my Windows machine (64-bit release build of Igor 7):
•doStuff() Plain creation: 0.0446025 Init with value: 0.49112 FastOp: 0.0522403 Plain assignment: 0.491873 MatrixOP (zeroMat): 0.158381 MatrixOP (single thread): 0.167936 Multithread assignment: 0.0947317

WARNING: If you want to run the test yourself using Igor 7, you must download the nightly build dated May 14, 2015 or later. There was a bug in MatrixOP zeroMat prior to May 14, 2015 that could cause a crash when the test code above is executed.

One other note: the Make line in the test block for MatrixOp zeroMat is unnecessary, since zeroMat will create the output wave on its own. Removing that line makes the timing for the MatrixOp zeroMat block:
MatrixOP (zeroMat): 0.113347

I tried using MatrixOP const() as AG suggested but I haven't figured out yet how to get that function to return a wave whose type is double precision floating point.
aclight wrote:

I tried using MatrixOP const() as AG suggested but I haven't figured out yet how to get that function to return a wave whose type is double precision floating point.


Note that the following comments apply to IP7.

For example, you can create an int16 output this way:
 
MatrixOP/O aa=const(10,20,int16(someValue))

For DP:
MatrixOP/O aa=const(10,20,fp64(someValue))

Note that fp64() may not be necessary if the token "someValue" is already DP. For example,
MatrixOP/O aa=const(10,20,pi)

Here goes the code using MatrixOp const.

Function doStuff()
 
    variable i, timer, numRuns, waveSize
    numRuns = 10
    waveSize = 2^24
 
    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            WaveClear data
    endfor
    printf "Plain creation: %g\r", stopmstimer(timer)/1e6/numRuns
 
    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data = 0
            WaveClear data
    endfor
    printf "Init with value: %g\r", stopmstimer(timer)/1e6/numRuns
 
    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            FastOp data = 0
            WaveClear data
    endfor
    printf "FastOp: %g\r", stopmstimer(timer)/1e6/numRuns
 
    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            data[] = 0
            WaveClear data
    endfor
    printf "Plain assignment: %g\r", stopmstimer(timer)/1e6/numRuns
 
#if IgorVersion() >= 7
    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            MatrixOP/FREE data = const(waveSize, 0, fp64(0))
            WaveClear data
    endfor
    printf "MatrixOP (const): %g\r", stopmstimer(timer)/1e6/numRuns
#else
    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            // not sure how to set the complete wave just to zero
            MatrixOP/FREE data = data * 0
            WaveClear data
    endfor
    printf "MatrixOP (const using workaround): %g\r", stopmstimer(timer)/1e6/numRuns
#endif
 
    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            Multithread data[] = 0
            WaveClear data
    endfor
    printf "Multithread assignment: %g\r", stopmstimer(timer)/1e6/numRuns
End


This gives on my windows box with latest IP7:
•dostuff() Plain creation: 0.0398512 Init with value: 0.305097 FastOp: 0.0432722 Plain assignment: 0.306107 MatrixOP (const): 0.0908975 Multithread assignment: 0.0686955

So MatrixOP is gaining ground, altough FastOP still leads.
For IP7 your survey is incomplete as there are two more ways to skin this cat:
1. WaveTransform setZero data
2. WaveTransform /V=0 setConstant data

Oh, and your testing code should remove all "Make" instructions in MatrixOP cases.

A.G.
Igor wrote:
[...]
FastOP: I suggest that you avoid it.
[...]

A.G.
WaveMetrics, Inc.


What's wrong with FastOp?
Thanks for the hints AG.

With the updated code:
Function doStuff()
 
    variable i, timer, numRuns, waveSize
    numRuns = 10
    waveSize = 2^24
 
    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            WaveClear data
    endfor
    printf "Plain creation: %g\r", stopmstimer(timer)/1e6/numRuns
 
    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data = 0
            WaveClear data
    endfor
    printf "Init with value: %g\r", stopmstimer(timer)/1e6/numRuns
 
    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            FastOp data = 0
            WaveClear data
    endfor
    printf "FastOp: %g\r", stopmstimer(timer)/1e6/numRuns
 
    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            data[] = 0
            WaveClear data
    endfor
    printf "Plain assignment: %g\r", stopmstimer(timer)/1e6/numRuns
 
#if IgorVersion() >= 7
    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            MatrixOP/FREE data = const(waveSize, 0, fp64(0))
            WaveClear data
    endfor
    printf "MatrixOP (const): %g\r", stopmstimer(timer)/1e6/numRuns

    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            WaveTransform/O setZero data
            WaveClear data
    endfor
    printf "WaveTransform (setZero): %g\r", stopmstimer(timer)/1e6/numRuns

    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            WaveTransform/O/V=0 setConstant data
            WaveClear data
    endfor
    printf "WaveTransform (setConstant): %g\r", stopmstimer(timer)/1e6/numRuns
#endif
 
    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            Make/D/FREE/N=(waveSize) data
            Multithread data[] = 0
            WaveClear data
    endfor
    printf "Multithread assignment: %g\r", stopmstimer(timer)/1e6/numRuns
End


I now get
•doStuff() Plain creation: 0.037367 Init with value: 0.307956 FastOp: 0.0427034 Plain assignment: 0.306808 MatrixOP (const): 0.0913224 WaveTransform (setZero): 0.0473604 WaveTransform (setConstant): 0.0428477 Multithread assignment: 0.0675981

And so WaveTransform and FastOp are the current winners.
Note that all your tests assume a DP wave. Results may vary for other number types. Here is an example:

Function test()
 
    variable i, timer, numRuns, waveSize
    numRuns = 10
    waveSize = 2^24
 
    Make/B/U/FREE/N=(waveSize) data

    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            FastOp data = 0
    endfor
    printf "FastOp: %g\r", stopmstimer(timer)/1e6/numRuns
 
    timer = startmsTimer
    for(i = 0; i < numRuns; i += 1)
            WaveTransform setZero data
    endfor
    printf "WaveTransform setZero: %g\r", stopmstimer(timer)/1e6/numRuns
End


On my machine:
FastOp: 0.0385148
WaveTransform setZero: 0.00623636

... and yes, I did not write FastOp :)