This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
The ${"\cTAINT"} raises the ire of 'use strict'.
[perl5.git] / lib / Benchmark.pm
index a7debd7..4d3b3e2 100644 (file)
@@ -2,17 +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
-
-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
+Benchmark - benchmark running times of Perl code
 
 =head1 SYNOPSIS
 
@@ -63,6 +53,17 @@ countit - see how many times a chunk of code runs in a given time
 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
@@ -99,34 +100,6 @@ if you use the Benchmark module:
 
 =over 10
 
-=item cmpthese ( COUT, CODEHASHREF, [ STYLE ] )
-
-=item cmpthese ( RESULTSHASHREF )
-
-Optionally calls timethese(), then outputs comparison chart.  This 
-chart is sorted from slowest to highest, 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().
-
-=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<not> 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 timeit(COUNT, CODE)
 
 Arguments: COUNT is the number of times to run the loop, and CODE is
@@ -187,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
@@ -226,6 +194,34 @@ Clear the cached time for COUNT rounds of the null loop.
 
 Clear all cached times.
 
+=item cmpthese ( COUT, CODEHASHREF, [ STYLE ] )
+
+=item cmpthese ( RESULTSHASHREF )
+
+Optionally calls timethese(), then outputs comparison chart.  This 
+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() (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<not> 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 ( )
 
 Disable caching of timings for the null loop. This will force Benchmark
@@ -237,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
@@ -269,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
@@ -293,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<lt> 0.
 
+=head1 SEE ALSO
+
+L<Devel::DProf> - a Perl code profiler
+
 =head1 AUTHORS
 
 Jarkko Hietaniemi <F<jhi@iki.fi>>, Tim Bunce <F<Tim.Bunce@ig.co.uk>>
@@ -324,8 +358,11 @@ sub _doeval { eval shift }
 use Carp;
 use Exporter;
 @ISA=(Exporter);
-@EXPORT=qw(cmpthese countit timeit timethis timethese timediff timestr);
-@EXPORT_OK=qw(clearcache clearallcache disablecache enablecache);
+@EXPORT=qw(timeit timethis timethese timediff timestr);
+@EXPORT_OK=qw(timesum cmpthese countit
+             clearcache clearallcache disablecache enablecache);
+
+$VERSION = 1.01;
 
 &init;
 
@@ -386,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;
 }
 
@@ -443,9 +480,7 @@ sub runloop {
     # 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);
@@ -517,7 +552,9 @@ sub countit {
        # accuracy since we're not couting these times.
        $n = int( $tpra * 1.05 * $n / $tc ); # Linear approximation.
        my $td = timeit($n, $code);
-       $tc = $td->[1] + $td->[2];
+       my $new_tc = $td->[1] + $td->[2];
+        # Make sure we are making progress.
+        $tc = $new_tc > 1.2 * $tc ? $new_tc : 1.2 * $tc;
     }
 
     # Now, do the 'for real' timing(s), repeating until we exceed
@@ -546,6 +583,7 @@ sub countit {
        $ttot = $utot + $stot;
        last if $ttot >= $tmax;
 
+        $ttot = 0.01 if $ttot < 0.01;
        my $r = $tmax / $ttot - 1; # Linear approximation.
        $n = int( $r * $ntot );
        $n = $nmin if $n < $nmin;
@@ -728,7 +766,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: