X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FBenchmark.pm;h=3c10a5bc523b4e312f8c038e8869e7d83b053956;hb=8c99d73ee7ce90de2561496f683f3850d1269e1d;hp=f4a81495a0eee6451d056b012c575cb0eb09397c;hpb=3c6312e9cc02e5b354acc85db6d5532c134bd61a;p=p5sagit%2Fp5-mst-13.2.git diff --git a/lib/Benchmark.pm b/lib/Benchmark.pm index f4a8149..3c10a5b 100644 --- a/lib/Benchmark.pm +++ b/lib/Benchmark.pm @@ -2,13 +2,7 @@ package Benchmark; =head1 NAME -Benchmark - benchmark running times of code - -timethis - run a chunk of code several times - -timethese - run several chunks of code several times - -timeit - run a chunk of code and see how long it goes +Benchmark - benchmark running times of Perl code =head1 SYNOPSIS @@ -26,14 +20,50 @@ timeit - run a chunk of code and see how long it goes 'Name2' => sub { ...code2... }, }); + # cmpthese can be used both ways as well + cmpthese($count, { + 'Name1' => '...code1...', + 'Name2' => '...code2...', + }); + + cmpthese($count, { + 'Name1' => sub { ...code1... }, + 'Name2' => sub { ...code2... }, + }); + + # ...or in two stages + $results = timethese($count, + { + 'Name1' => sub { ...code1... }, + 'Name2' => sub { ...code2... }, + }, + 'none' + ); + cmpthese( $results ) ; + $t = timeit($count, '...other code...') print "$count loops of other code took:",timestr($t),"\n"; + $t = countit($time, '...other code...') + $count = $t->iters ; + print "$count loops of other code took:",timestr($t),"\n"; + =head1 DESCRIPTION The Benchmark module encapsulates a number of routines to help you figure out how long it takes to execute some code. +timethis - run a chunk of code several times + +timethese - run several chunks of code several times + +cmpthese - print results of timethese as a comparison chart + +timeit - run a chunk of code and see how long it goes + +countit - see how many times a chunk of code runs in a given time + + =head2 Methods =over 10 @@ -57,6 +87,10 @@ Enables or disable debugging by setting the C<$Benchmark::Debug> flag: $t = timeit(10, ' 5 ** $Global '); debug Benchmark 0; +=item iters + +Returns the number of iterations. + =back =head2 Standard Exports @@ -126,11 +160,6 @@ Returns a hash of Benchmark objects, keyed by name. Returns the difference between two Benchmark times as a Benchmark object suitable for passing to timestr(). -=item timesum ( T1, T2 ) - -Returns the sum of two Benchmark times as a Benchmark object suitable -for passing to timestr(). - =item timestr ( TIMEDIFF, [ STYLE, [ FORMAT ] ] ) Returns a string that formats the times in the TIMEDIFF object in @@ -170,14 +199,28 @@ Clear all cached times. =item cmpthese ( RESULTSHASHREF ) Optionally calls timethese(), then outputs comparison chart. This -chart is sorted from slowest to highest, and shows the percent +chart is sorted from slowest to fastest, and shows the percent speed difference between each pair of tests. Can also be passed the data structure that timethese() returns: $results = timethese( .... ); cmpthese( $results ); -Returns the data structure returned by timethese(). +Returns the data structure returned by timethese() (or passed in). + +=item countit(TIME, CODE) + +Arguments: TIME is the minimum length of time to run CODE for, and CODE is +the code to run. CODE may be either a code reference or a string to +be eval'd; either way it will be run in the caller's package. + +TIME is I negative. countit() will run the loop many times to +calculate the speed of CODE before running it for TIME. The actual +time run for will usually be greater than TIME due to system clock +resolution, so it's best to look at the number of iterations divided +by the times that you are concerned with, not just the iterations. + +Returns: a Benchmark object. =item disablecache ( ) @@ -190,6 +233,11 @@ Enable caching of timings for the null loop. The time taken for COUNT rounds of the null loop will be calculated only once for each different COUNT used. +=item timesum ( T1, T2 ) + +Returns the sum of two Benchmark times as a Benchmark object suitable +for passing to timestr(). + =back =head1 NOTES @@ -197,7 +245,7 @@ different COUNT used. The data is stored as a list of values from the time and times functions: - ($real, $user, $system, $children_user, $children_system) + ($real, $user, $system, $children_user, $children_system, $iters) in seconds for the whole loop (not divided by the number of rounds). @@ -222,6 +270,35 @@ calls like these: Caching is off by default, as it can (usually slightly) decrease accuracy and does not usually noticably affect runtimes. +=head1 EXAMPLES + +For example, + + use Benchmark;$x=3;cmpthese(-5,{a=>sub{$x*$x},b=>sub{$x**2}}) + +outputs something like this: + + Benchmark: running a, b, each for at least 5 CPU seconds... + a: 10 wallclock secs ( 5.14 usr + 0.13 sys = 5.27 CPU) @ 3835055.60/s (n=20210743) + b: 5 wallclock secs ( 5.41 usr + 0.00 sys = 5.41 CPU) @ 1574944.92/s (n=8520452) + Rate b a + b 1574945/s -- -59% + a 3835056/s 144% -- + +while + + use Benchmark; + $x=3; + $r=timethese(-5,{a=>sub{$x*$x},b=>sub{$x**2}},'none'); + cmpthese($r); + +outputs something like this: + + Rate b a + b 1559428/s -- -62% + a 4152037/s 166% -- + + =head1 INHERITANCE Benchmark inherits from no other class, except of course @@ -230,7 +307,7 @@ for Exporter. =head1 CAVEATS Comparing eval'd strings with code references will give you -inaccurate results: a code reference will show a slower +inaccurate results: a code reference will show a slightly slower execution time than the equivalent eval'd string. The real time timing is done using time(2) and @@ -246,6 +323,10 @@ The system time of the null loop might be slightly more than the system time of the loop with the actual code and therefore the difference might end up being E 0. +=head1 SEE ALSO + +L - a Perl code profiler + =head1 AUTHORS Jarkko Hietaniemi >, Tim Bunce > @@ -263,7 +344,7 @@ functionality. September, 1999; by Barrie Slaymaker: math fixes and accuracy and efficiency tweaks. Added cmpthese(). A result is now returned from -timethese(). +timethese(). Exposed countit() (was runfor()). =cut @@ -277,8 +358,11 @@ sub _doeval { eval shift } use Carp; use Exporter; @ISA=(Exporter); -@EXPORT=qw(timeit timethis timethese timediff timesum timestr); -@EXPORT_OK=qw(clearcache clearallcache cmpthese disablecache enablecache); +@EXPORT=qw(timeit timethis timethese timediff timestr); +@EXPORT_OK=qw(timesum cmpthese countit + clearcache clearallcache disablecache enablecache); + +$VERSION = 1.00; &init; @@ -314,6 +398,7 @@ sub cpu_p { my($r,$pu,$ps,$cu,$cs) = @{$_[0]}; $pu+$ps ; } sub cpu_c { my($r,$pu,$ps,$cu,$cs) = @{$_[0]}; $cu+$cs ; } sub cpu_a { my($r,$pu,$ps,$cu,$cs) = @{$_[0]}; $pu+$ps+$cu+$cs ; } sub real { my($r,$pu,$ps,$cu,$cs) = @{$_[0]}; $r ; } +sub iters { $_[0]->[5] ; } sub timediff { my($a, $b) = @_; @@ -338,19 +423,19 @@ sub timestr { my @t = @$tr; warn "bad time value (@t)" unless @t==6; my($r, $pu, $ps, $cu, $cs, $n) = @t; - my($pt, $ct, $t) = ($tr->cpu_p, $tr->cpu_c, $tr->cpu_a); + my($pt, $ct, $tt) = ($tr->cpu_p, $tr->cpu_c, $tr->cpu_a); $f = $defaultfmt unless defined $f; # format a time in the required style, other formats may be added here $style ||= $defaultstyle; $style = ($ct>0) ? 'all' : 'noc' if $style eq 'auto'; my $s = "@t $style"; # default for unknown style $s=sprintf("%2d wallclock secs (%$f usr %$f sys + %$f cusr %$f csys = %$f CPU)", - @t,$t) if $style eq 'all'; + $r,$pu,$ps,$cu,$cs,$tt) if $style eq 'all'; $s=sprintf("%2d wallclock secs (%$f usr + %$f sys = %$f CPU)", $r,$pu,$ps,$pt) if $style eq 'noc'; $s=sprintf("%2d wallclock secs (%$f cusr + %$f csys = %$f CPU)", $r,$cu,$cs,$ct) if $style eq 'nop'; - $s .= sprintf(" @ %$f/s (n=$n)", $n / ( $pu + $ps )) if $n; + $s .= sprintf(" @ %$f/s (n=$n)", $n / ( $pu + $ps )) if $n && $pu+$ps; $s; } @@ -392,12 +477,10 @@ sub runloop { # -0.01, +0. If we don't wait, then it's more like -0.01, +0.01. This # may not seem important, but it significantly reduces the chances of # getting a too low initial $n in the initial, 'find the minimum' loop - # in &runfor. This, in turn, can reduce the number of calls to + # in &countit. This, in turn, can reduce the number of calls to # &runloop a lot, and thus reduce additive errors. my $tbase = Benchmark->new(0)->[1]; - do { - $t0 = Benchmark->new(0); - } while ( $t0->[1] == $tbase ); + while ( ( $t0 = Benchmark->new(0) )->[1] == $tbase ) {} ; &$subref; $t1 = Benchmark->new($n); $td = &timediff($t1, $t0); @@ -437,8 +520,8 @@ my $default_for = 3; my $min_for = 0.1; -sub runfor { - my ($code, $tmax) = @_; +sub countit { + my ( $tmax, $code ) = @_; if ( not defined $tmax or $tmax == 0 ) { $tmax = $default_for; @@ -446,7 +529,7 @@ sub runfor { $tmax = -$tmax; } - die "runfor(..., $tmax): timelimit cannot be less than $min_for.\n" + die "countit($tmax, ...): timelimit cannot be less than $min_for.\n" if $tmax < $min_for; my ($n, $tc); @@ -523,7 +606,7 @@ sub timethis{ $title = "timethis $n" unless defined $title; } else { $fort = n_to_for( $n ); - $t = runfor($code, $fort); + $t = countit( $fort, $code ); $title = "timethis for $fort" unless defined $title; $forn = $t->[-1]; } @@ -680,7 +763,7 @@ sub cmpthese{ sort { $$a <=> $$b } map { \$_ } @col_widths[2..$#col_widths]; my $max_width = ${$sorted_width_refs[-1]}; - my $total = 0; + my $total = @col_widths - 1 ; for ( @col_widths ) { $total += $_ } STRETCHER: