Mercurial > hg > octave-nkf
diff scripts/testfun/speed.m @ 5589:f812a0680d05
[project @ 2006-01-06 00:14:42 by jwe]
author | jwe |
---|---|
date | Fri, 06 Jan 2006 00:14:42 +0000 |
parents | |
children | 7e7ed81f5566 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/scripts/testfun/speed.m @@ -0,0 +1,270 @@ +## Copyright (C) 2000-2001 Paul Kienzle +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +## 02110-1301 USA + +## -*- texinfo -*- +## @deftypefn {Function File} {} speed (@var{f}, @var{init}, @var{max_n}, @var{f2}, @var{tol}, @var{err}) +## @deftypefnx {Function File} {@var{r} =} speed (@dots{}) +## +## Determine the execution time of an expression for various @var{n}. +## The @var{n} are log-spaced from 1 to @var{max_n}. For each @var{n}, +## an initialization expression is computed to create whatever data +## are needed for the test. Called without output arguments the data +## is presented graphically. Called with an output argument @var{r}, +## the speedup ratio is returned instead of displaying it graphically. +## +## @table @code +## @item @var{f} +## The expression to evaluate. +## +## @item @var{max_n} +## The maximum test length to run. Default value is 100. +## +## @item @var{init} +## Initialization expression for function argument values. Use @var{k} +## for the test number and @var{n} for the size of the test. This should +## compute values for all variables listed in args. Note that init will +## be evaluated first for k=0, so things which are constant throughout +## the test can be computed then. The default value is @code{@var{x} = +## randn (@var{n}, 1);}. +## +## @item @var{f2} +## An alternative expression to evaluate, so the speed of the two +## can be compared. Default is @code{[]}. +## +## @item @var{tol} +## If @var{tol} is @code{Inf}, then no comparison will be made between the +## results of expression @var{f} and expression @var{f2}. Otherwise, +## expression @var{f} should produce a value @var{v} and expression @var{f2} +## should produce a value @var{v2}, and these shall be compared using +## @code{assert(@var{v},@var{v2},@var{tol},@var{err})}. The default is +## @code{eps}. +## @end table +## +## Some global variables are also referenced. Choose values suitable to +## your machine and your work style. +## +## @table @code +## @item speed_test_plot +## If true, plot a nice speed comparison graph. Default is true. +## +## @item speed_test_numtests +## Number of vector lengths to test. The default is 25. +## @end table +## +## Some comments on the graphs. The line on the speedup ratio graph +## should be larger than 1 if your function is faster. The slope on +## the runtime graph shows you the O(f) speed characteristics. Where it +## is flat, execution time is O(1). Where it is sloping, execution time +## is O(n^m), with steeper slopes for larger @var{n}. Generally vectorizing +## a function will not change the slope of the run-time graph, but it +## will shift it relative to the original. +## +## A simple example is +## +## @example +## speed("strrep(s,x,y)", "s=blanks(n);x=' ';y='b';", 100) +## @end example +## +## A more complex example, if you had an original version of @code{xcorr} +## using for loops and another version using an FFT, you could compare the +## run speed for various lags as follows, or for a fixed lag with varying +## vector lengths as follows: +## +## @example +## speed("v=xcorr(x,n)", "x=rand(128,1);", 100, ... +## "v2=xcorr_orig(x,n)", 100*eps,'rel') +## speed("v=xcorr(x,15)", "x=rand(20+n,1);", 100, ... +## "v2=xcorr_orig(x,n)", 100*eps,'rel') +## @end example +## +## Assuming one of the two versions is in @var{xcorr_orig}, this would +## would compare their speed and their output values. Note that the +## FFT version is not exact, so we specify an acceptable tolerance on +## the comparison @code{100*eps}, and the errors should be computed +## relatively, as @code{abs((@var{x} - @var{y})./@var{y})} rather than +## absolutely as @code{abs(@var{x} - @var{y})}. +## +## Type @code{example('speed')} to see some real examples. Note for +## obscure reasons, you can't run examples 1 and 2 directly using +## @code{demo('speed')}. Instead use, @code{eval(example('speed',1))} +## and @code{eval(example('speed',2))}. +## @end deftypefn + +## TODO: consider two dimensional speedup surfaces for functions like kron. +function __ratio_r = speed (__f1, __init, __max_n, __f2, __tol, __err) + if nargin < 1 || nargin > 6, + usage("speed_test(f, init, max_n, f2, tol, err)"); + endif + if nargin < 2 || isempty(__init), + __init = "x = randn(n, 1);"; + endif + if nargin < 3 || isempty(__max_n), __max_n = 100; endif + if nargin < 4, __f2 = []; endif + if nargin < 5 || isempty(__tol), __tol = eps; endif + if nargin < 6 || isempty(__err), __err = []; endif + + global speed_test_plot = 1; + global speed_test_numtests = 25; + + __test_n = uniq(round(logspace(0,log10(__max_n),speed_test_numtests))); + __torig = __tnew = zeros (size(__test_n)) ; + + disp (["testing..........", __f1, "\ninit: ", __init]); + + ## make sure the functions are freshly loaded by evaluating them at + ## test_n(1); firt have to initialize the args though. + n=1; k=0; + eval ([__init, ";"]); + if !isempty(__f2), eval ([__f2, ";"]); endif + eval ([__f1, ";"]); + + ## run the tests + for k=1:length(__test_n) + if (k > 1) + n=__test_n(k); + eval ([__init, ";"]); + endif + + printf ("n%i=%i ",k, n) ; fflush(1); + + eval (["__t=time();", __f1, "; __v1=ans; __t = time()-__t;"]); + if (__t < 0.25) + eval (["__t2=time();", __f1, "; __t2 = time()-__t2;"]); + eval (["__t3=time();", __f1, "; __t3 = time()-__t3;"]); + __t = min([__t,__t2,__t3]); + endif + __tnew(k) = __t; + + if !isempty(__f2) + eval (["__t=time();", __f2, "; __v2=ans; __t = time()-__t;"]); + if (__t < 0.25) + eval (["__t2=time();", __f2, "; __t2 = time()-__t2;"]); + eval (["__t3=time();", __f2, "; __t3 = time()-__t3;"]); + endif + __torig(k) = __t; + if !isinf(__tol) + assert(__v1,__v2,__tol,__err); + endif + endif + + end + + if !isempty(__f2), + # Don't keep zero times + idx = find ( __tnew>sqrt(eps) & __torig>sqrt(eps) ) ; + ratio = mean (__torig(idx) ./ __tnew(idx)); + if (nargout == 1) + __ratio_r = ratio; + else + printf ("\nmean runtime ratio of %s / %s : %g\n", __f2, __f1, ratio); + endif + else + if (nargout == 1) + _ratio_r = mean(__tnew); + else + printf ("\nmean runtime: %g\n", mean(__tnew)); + endif + endif + + if (speed_test_plot && nargout == 0 && !isempty(__f2)) + + subplot(121); + xlabel("test length"); + title (__f1); + ylabel("speedup ratio"); + semilogx ( __test_n(idx), __torig(idx)./__tnew(idx) , + ["-*r;", strrep(__f1,";","."), "/", strrep(__f2,";","."), ";"], + __test_n(idx), __tnew(idx)./__torig(idx) , + ["-*g;", strrep(__f2,";","."), "/", strrep(__f1,";","."), ";"]); + subplot (122); + + ## convert best execution time to milliseconds. + __torig = 1000*__torig; + __tnew = 1000*__tnew; + + ylabel ("best execution time (ms)"); + title (["init: ", __init]); + loglog ( __test_n (idx), __tnew (idx), ["*-g;", strrep(__f1,";","."), ";" ], + __test_n (idx), __torig (idx), ["*-r;", strrep(__f2,";","."), ";"]) + title (""); xlabel (""); ylabel (""); oneplot(); + elseif (speed_test_plot && nargout == 0) + __tnew = 1000*__tnew; + xlabel("test length"); + ylabel ("best execution time (ms)"); + title ([__f1, " init: ", __init]); + loglog ( __test_n, __tnew, "*-g;;"); + title (""); xlabel (""); ylabel (""); oneplot(); + endif + +endfunction + +%!demo if 1 +%! function x = build_orig(n) +%! ## extend the target vector on the fly +%! for i=0:n-1, x([1:10]+i*10) = 1:10; endfor +%! endfunction +%! function x = build(n) +%! ## preallocate the target vector +%! x = zeros(1, n*10); +%! try +%! if (prefer_column_vectors), x = x.'; endif +%! catch +%! end +%! for i=0:n-1, x([1:10]+i*10) = 1:10; endfor +%! endfunction +%! +%! disp("-----------------------"); +%! type build_orig; +%! disp("-----------------------"); +%! type build; +%! disp("-----------------------"); +%! +%! disp("Preallocated vector test.\nThis takes a little while..."); +%! speed('build', 'build_orig', 1000, 'v=n;'); +%! clear build build_orig +%! disp("Note how much faster it is to pre-allocate a vector."); +%! disp("Notice the peak speedup ratio."); +%! clear build build_orig +%! endif + +%!demo if 1 +%! function x = build_orig(n) +%! for i=0:n-1, x([1:10]+i*10) = 1:10; endfor +%! endfunction +%! function x = build(n) +%! idx = [1:10]'; +%! x = idx(:,ones(1,n)); +%! x = reshape(x, 1, n*10); +%! try +%! if (prefer_column_vectors), x = x.'; endif +%! catch +%! end +%! endfunction +%! +%! disp("-----------------------"); +%! type build_orig; +%! disp("-----------------------"); +%! type build; +%! disp("-----------------------"); +%! +%! disp("Vectorized test. This takes a little while..."); +%! speed('build', 'build_orig', 1000, 'v=n;'); +%! clear build build_orig +%! disp("-----------------------"); +%! disp("This time, the for loop is done away with entirely."); +%! disp("Notice how much bigger the speedup is then in example 1."); +%! endif