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 :)