1*0Sstevel@tonic-gate#!./perl -w 2*0Sstevel@tonic-gate 3*0Sstevel@tonic-gateBEGIN { 4*0Sstevel@tonic-gate chdir 't' if -d 't'; 5*0Sstevel@tonic-gate @INC = ('../lib'); 6*0Sstevel@tonic-gate} 7*0Sstevel@tonic-gate 8*0Sstevel@tonic-gateuse warnings; 9*0Sstevel@tonic-gateuse strict; 10*0Sstevel@tonic-gateuse vars qw($foo $bar $baz $ballast); 11*0Sstevel@tonic-gateuse Test::More tests => 193; 12*0Sstevel@tonic-gate 13*0Sstevel@tonic-gateuse Benchmark qw(:all); 14*0Sstevel@tonic-gate 15*0Sstevel@tonic-gatemy $delta = 0.4; 16*0Sstevel@tonic-gate 17*0Sstevel@tonic-gate# Some timing ballast 18*0Sstevel@tonic-gatesub fib { 19*0Sstevel@tonic-gate my $n = shift; 20*0Sstevel@tonic-gate return $n if $n < 2; 21*0Sstevel@tonic-gate fib($n-1) + fib($n-2); 22*0Sstevel@tonic-gate} 23*0Sstevel@tonic-gate$ballast = 15; 24*0Sstevel@tonic-gate 25*0Sstevel@tonic-gatemy $All_Pattern = 26*0Sstevel@tonic-gate qr/(\d+) +wallclock secs? +\( *(-?\d+\.\d\d) +usr +(-?\d+\.\d\d) +sys +\+ +(-?\d+\.\d\d) +cusr +(-?\d+\.\d\d) +csys += +(-?\d+\.\d\d) +CPU\)/; 27*0Sstevel@tonic-gatemy $Noc_Pattern = 28*0Sstevel@tonic-gate qr/(\d+) +wallclock secs? +\( *(-?\d+\.\d\d) +usr +\+ +(-?\d+\.\d\d) +sys += +(-?\d+\.\d\d) +CPU\)/; 29*0Sstevel@tonic-gatemy $Nop_Pattern = 30*0Sstevel@tonic-gate qr/(\d+) +wallclock secs? +\( *(-?\d+\.\d\d) +cusr +\+ +(-?\d+\.\d\d) +csys += +\d+\.\d\d +CPU\)/; 31*0Sstevel@tonic-gate# Please don't trust the matching parenthises to be useful in this :-) 32*0Sstevel@tonic-gatemy $Default_Pattern = qr/$All_Pattern|$Noc_Pattern/; 33*0Sstevel@tonic-gate 34*0Sstevel@tonic-gatemy $t0 = new Benchmark; 35*0Sstevel@tonic-gateisa_ok ($t0, 'Benchmark', "Ensure we can create a benchmark object"); 36*0Sstevel@tonic-gate 37*0Sstevel@tonic-gate# We use the benchmark object once we've done some work: 38*0Sstevel@tonic-gate 39*0Sstevel@tonic-gateisa_ok(timeit(5, sub {++$foo}), 'Benchmark', "timeit CODEREF"); 40*0Sstevel@tonic-gateis ($foo, 5, "benchmarked code was run 5 times"); 41*0Sstevel@tonic-gate 42*0Sstevel@tonic-gateisa_ok(timeit(5, '++$bar'), 'Benchmark', "timeit eval"); 43*0Sstevel@tonic-gateis ($bar, 5, "benchmarked code was run 5 times"); 44*0Sstevel@tonic-gate 45*0Sstevel@tonic-gateprint "# Burning CPU to benchmark things will take time...\n"; 46*0Sstevel@tonic-gate 47*0Sstevel@tonic-gate 48*0Sstevel@tonic-gate 49*0Sstevel@tonic-gate# We need to do something fairly slow in the coderef. 50*0Sstevel@tonic-gate# Same coderef. Same place in memory. 51*0Sstevel@tonic-gatemy $coderef = sub {$baz += fib($ballast)}; 52*0Sstevel@tonic-gate 53*0Sstevel@tonic-gate# The default is three. 54*0Sstevel@tonic-gate$baz = 0; 55*0Sstevel@tonic-gatemy $threesecs = countit(0, $coderef); 56*0Sstevel@tonic-gateisa_ok($threesecs, 'Benchmark', "countit 0, CODEREF"); 57*0Sstevel@tonic-gateisnt ($baz, 0, "benchmarked code was run"); 58*0Sstevel@tonic-gatemy $in_threesecs = $threesecs->iters; 59*0Sstevel@tonic-gateprint "# $in_threesecs iterations\n"; 60*0Sstevel@tonic-gateok ($in_threesecs > 0, "iters returned positive iterations"); 61*0Sstevel@tonic-gate 62*0Sstevel@tonic-gatemy $estimate = int (100 * $in_threesecs / 3) / 100; 63*0Sstevel@tonic-gateprint "# from the 3 second run estimate $estimate iterations in 1 second...\n"; 64*0Sstevel@tonic-gate$baz = 0; 65*0Sstevel@tonic-gatemy $onesec = countit(1, $coderef); 66*0Sstevel@tonic-gateisa_ok($onesec, 'Benchmark', "countit 1, CODEREF"); 67*0Sstevel@tonic-gateisnt ($baz, 0, "benchmarked code was run"); 68*0Sstevel@tonic-gatemy $in_onesec = $onesec->iters; 69*0Sstevel@tonic-gateprint "# $in_onesec iterations\n"; 70*0Sstevel@tonic-gateok ($in_onesec > 0, "iters returned positive iterations"); 71*0Sstevel@tonic-gate 72*0Sstevel@tonic-gate{ 73*0Sstevel@tonic-gate my $difference = $in_onesec - $estimate; 74*0Sstevel@tonic-gate my $actual = abs ($difference / $in_onesec); 75*0Sstevel@tonic-gate ok ($actual < $delta, "is $in_onesec within $delta of estimate ($estimate)"); 76*0Sstevel@tonic-gate print "# $in_onesec is between " . ($delta / 2) . 77*0Sstevel@tonic-gate " and $delta of estimate. Not that safe.\n" if $actual > $delta/2; 78*0Sstevel@tonic-gate} 79*0Sstevel@tonic-gate 80*0Sstevel@tonic-gate# I found that the eval'ed version was 3 times faster than the coderef. 81*0Sstevel@tonic-gate# (now it has a different ballast value) 82*0Sstevel@tonic-gate$baz = 0; 83*0Sstevel@tonic-gatemy $again = countit(1, '$baz += fib($ballast)'); 84*0Sstevel@tonic-gateisa_ok($onesec, 'Benchmark', "countit 1, eval"); 85*0Sstevel@tonic-gateisnt ($baz, 0, "benchmarked code was run"); 86*0Sstevel@tonic-gatemy $in_again = $again->iters; 87*0Sstevel@tonic-gateprint "# $in_again iterations\n"; 88*0Sstevel@tonic-gateok ($in_again > 0, "iters returned positive iterations"); 89*0Sstevel@tonic-gate 90*0Sstevel@tonic-gate 91*0Sstevel@tonic-gatemy $t1 = new Benchmark; 92*0Sstevel@tonic-gateisa_ok ($t1, 'Benchmark', "Create another benchmark object now we're finished"); 93*0Sstevel@tonic-gate 94*0Sstevel@tonic-gatemy $diff = timediff ($t1, $t0); 95*0Sstevel@tonic-gateisa_ok ($diff, 'Benchmark', "Get the time difference"); 96*0Sstevel@tonic-gateisa_ok (timesum ($t0, $t1), 'Benchmark', "check timesum"); 97*0Sstevel@tonic-gate 98*0Sstevel@tonic-gatemy $default = timestr ($diff); 99*0Sstevel@tonic-gateisnt ($default, '', 'timestr ($diff)'); 100*0Sstevel@tonic-gatemy $auto = timestr ($diff, 'auto'); 101*0Sstevel@tonic-gateis ($auto, $default, 'timestr ($diff, "auto") matches timestr ($diff)'); 102*0Sstevel@tonic-gate 103*0Sstevel@tonic-gate{ 104*0Sstevel@tonic-gate my $all = timestr ($diff, 'all'); 105*0Sstevel@tonic-gate like ($all, $All_Pattern, 'timestr ($diff, "all")'); 106*0Sstevel@tonic-gate print "# $all\n"; 107*0Sstevel@tonic-gate 108*0Sstevel@tonic-gate my ($wallclock, $usr, $sys, $cusr, $csys, $cpu) = $all =~ $All_Pattern; 109*0Sstevel@tonic-gate 110*0Sstevel@tonic-gate is (timestr ($diff, 'none'), '', "none supresses output"); 111*0Sstevel@tonic-gate 112*0Sstevel@tonic-gate my $noc = timestr ($diff, 'noc'); 113*0Sstevel@tonic-gate like ($noc, qr/$wallclock +wallclock secs? +\( *$usr +usr +\+ +$sys +sys += +$cpu +CPU\)/, 'timestr ($diff, "noc")'); 114*0Sstevel@tonic-gate 115*0Sstevel@tonic-gate my $nop = timestr ($diff, 'nop'); 116*0Sstevel@tonic-gate like ($nop, qr/$wallclock +wallclock secs? +\( *$cusr +cusr +\+ +$csys +csys += +\d+\.\d\d +CPU\)/, 'timestr ($diff, "nop")'); 117*0Sstevel@tonic-gate 118*0Sstevel@tonic-gate if ($auto eq $noc) { 119*0Sstevel@tonic-gate pass ('"auto" is "noc"'); 120*0Sstevel@tonic-gate } else { 121*0Sstevel@tonic-gate is ($auto, $all, '"auto" isn\'t "noc", so should be eq to "all"'); 122*0Sstevel@tonic-gate } 123*0Sstevel@tonic-gate 124*0Sstevel@tonic-gate like (timestr ($diff, 'all', 'E'), 125*0Sstevel@tonic-gate qr/(\d+) +wallclock secs? +\( *\d\.\d+E[-+]?\d\d\d? +usr +\d\.\d+E[-+]?\d\d\d? +sys +\+ +\d\.\d+E[-+]?\d\d\d? +cusr +\d\.\d+E[-+]?\d\d\d? +csys += +\d\.\d+E[-+]?\d\d\d? +CPU\)/, 'timestr ($diff, "all", "E") [sprintf format of "E"]'); 126*0Sstevel@tonic-gate} 127*0Sstevel@tonic-gate 128*0Sstevel@tonic-gatemy $out = tie *OUT, 'TieOut'; 129*0Sstevel@tonic-gate 130*0Sstevel@tonic-gatemy $iterations = 3; 131*0Sstevel@tonic-gate 132*0Sstevel@tonic-gate$foo = 0; 133*0Sstevel@tonic-gateselect(OUT); 134*0Sstevel@tonic-gatemy $got = timethis($iterations, sub {++$foo}); 135*0Sstevel@tonic-gateselect(STDOUT); 136*0Sstevel@tonic-gateisa_ok($got, 'Benchmark', "timethis CODEREF"); 137*0Sstevel@tonic-gateis ($foo, $iterations, "benchmarked code was run $iterations times"); 138*0Sstevel@tonic-gate 139*0Sstevel@tonic-gate$got = $out->read(); 140*0Sstevel@tonic-gatelike ($got, qr/^timethis $iterations/, 'default title'); 141*0Sstevel@tonic-gatelike ($got, $Default_Pattern, 'default format is all or noc'); 142*0Sstevel@tonic-gate 143*0Sstevel@tonic-gate$bar = 0; 144*0Sstevel@tonic-gateselect(OUT); 145*0Sstevel@tonic-gate$got = timethis($iterations, '++$bar'); 146*0Sstevel@tonic-gateselect(STDOUT); 147*0Sstevel@tonic-gateisa_ok($got, 'Benchmark', "timethis eval"); 148*0Sstevel@tonic-gateis ($bar, $iterations, "benchmarked code was run $iterations times"); 149*0Sstevel@tonic-gate 150*0Sstevel@tonic-gate$got = $out->read(); 151*0Sstevel@tonic-gatelike ($got, qr/^timethis $iterations/, 'default title'); 152*0Sstevel@tonic-gatelike ($got, $Default_Pattern, 'default format is all or noc'); 153*0Sstevel@tonic-gate 154*0Sstevel@tonic-gatemy $title = 'lies, damn lies and benchmarks'; 155*0Sstevel@tonic-gate$foo = 0; 156*0Sstevel@tonic-gateselect(OUT); 157*0Sstevel@tonic-gate$got = timethis($iterations, sub {++$foo}, $title); 158*0Sstevel@tonic-gateselect(STDOUT); 159*0Sstevel@tonic-gateisa_ok($got, 'Benchmark', "timethis with title"); 160*0Sstevel@tonic-gateis ($foo, $iterations, "benchmarked code was run $iterations times"); 161*0Sstevel@tonic-gate 162*0Sstevel@tonic-gate$got = $out->read(); 163*0Sstevel@tonic-gatelike ($got, qr/^$title:/, 'specify title'); 164*0Sstevel@tonic-gatelike ($got, $Default_Pattern, 'default format is all or noc'); 165*0Sstevel@tonic-gate 166*0Sstevel@tonic-gate# default is auto, which is all or noc. nop can never match the default 167*0Sstevel@tonic-gate$foo = 0; 168*0Sstevel@tonic-gateselect(OUT); 169*0Sstevel@tonic-gate$got = timethis($iterations, sub {++$foo}, $title, 'nop'); 170*0Sstevel@tonic-gateselect(STDOUT); 171*0Sstevel@tonic-gateisa_ok($got, 'Benchmark', "timethis with format"); 172*0Sstevel@tonic-gateis ($foo, $iterations, "benchmarked code was run $iterations times"); 173*0Sstevel@tonic-gate 174*0Sstevel@tonic-gate$got = $out->read(); 175*0Sstevel@tonic-gatelike ($got, qr/^$title:/, 'specify title'); 176*0Sstevel@tonic-gatelike ($got, $Nop_Pattern, 'specify format as nop'); 177*0Sstevel@tonic-gate 178*0Sstevel@tonic-gate{ 179*0Sstevel@tonic-gate $foo = 0; 180*0Sstevel@tonic-gate select(OUT); 181*0Sstevel@tonic-gate my $start = time; 182*0Sstevel@tonic-gate $got = timethis(-2, sub {$foo+= fib($ballast)}, $title, 'none'); 183*0Sstevel@tonic-gate my $end = time; 184*0Sstevel@tonic-gate select(STDOUT); 185*0Sstevel@tonic-gate isa_ok($got, 'Benchmark', 186*0Sstevel@tonic-gate "timethis, at least 2 seconds with format 'none'"); 187*0Sstevel@tonic-gate ok ($foo > 0, "benchmarked code was run"); 188*0Sstevel@tonic-gate ok ($end - $start > 1, "benchmarked code ran for over 1 second"); 189*0Sstevel@tonic-gate 190*0Sstevel@tonic-gate $got = $out->read(); 191*0Sstevel@tonic-gate # Remove any warnings about having too few iterations. 192*0Sstevel@tonic-gate $got =~ s/\(warning:[^\)]+\)//gs; 193*0Sstevel@tonic-gate $got =~ s/^[ \t\n]+//s; # Remove all the whitespace from the beginning 194*0Sstevel@tonic-gate 195*0Sstevel@tonic-gate is ($got, '', "format 'none' should suppress output"); 196*0Sstevel@tonic-gate} 197*0Sstevel@tonic-gate 198*0Sstevel@tonic-gate$foo = $bar = $baz = 0; 199*0Sstevel@tonic-gateselect(OUT); 200*0Sstevel@tonic-gate$got = timethese($iterations, { Foo => sub {++$foo}, Bar => '++$bar', 201*0Sstevel@tonic-gate Baz => sub {++$baz} }); 202*0Sstevel@tonic-gateselect(STDOUT); 203*0Sstevel@tonic-gateis(ref ($got), 'HASH', "timethese should return a hashref"); 204*0Sstevel@tonic-gateisa_ok($got->{Foo}, 'Benchmark', "Foo value"); 205*0Sstevel@tonic-gateisa_ok($got->{Bar}, 'Benchmark', "Bar value"); 206*0Sstevel@tonic-gateisa_ok($got->{Baz}, 'Benchmark', "Baz value"); 207*0Sstevel@tonic-gateeq_set([keys %$got], [qw(Foo Bar Baz)], 'should be exactly three objects'); 208*0Sstevel@tonic-gateis ($foo, $iterations, "Foo code was run $iterations times"); 209*0Sstevel@tonic-gateis ($bar, $iterations, "Bar code was run $iterations times"); 210*0Sstevel@tonic-gateis ($baz, $iterations, "Baz code was run $iterations times"); 211*0Sstevel@tonic-gate 212*0Sstevel@tonic-gate$got = $out->read(); 213*0Sstevel@tonic-gate# Remove any warnings about having too few iterations. 214*0Sstevel@tonic-gate$got =~ s/\(warning:[^\)]+\)//gs; 215*0Sstevel@tonic-gate 216*0Sstevel@tonic-gatelike ($got, qr/timing $iterations iterations of\s+Bar\W+Baz\W+Foo\W*?\.\.\./s, 217*0Sstevel@tonic-gate 'check title'); 218*0Sstevel@tonic-gate# Remove the title 219*0Sstevel@tonic-gate$got =~ s/.*\.\.\.//s; 220*0Sstevel@tonic-gatelike ($got, qr/\bBar\b.*\bBaz\b.*\bFoo\b/s, 'check output is in sorted order'); 221*0Sstevel@tonic-gatelike ($got, $Default_Pattern, 'should find default format somewhere'); 222*0Sstevel@tonic-gate 223*0Sstevel@tonic-gate 224*0Sstevel@tonic-gate{ # ensure 'use strict' does not leak from Benchmark.pm into benchmarked code 225*0Sstevel@tonic-gate no strict; 226*0Sstevel@tonic-gate select OUT; 227*0Sstevel@tonic-gate 228*0Sstevel@tonic-gate eval { 229*0Sstevel@tonic-gate timethese( 1, 230*0Sstevel@tonic-gate { undeclared_var => q{ $i++; $i-- }, 231*0Sstevel@tonic-gate symbolic_ref => q{ $bar = 42; 232*0Sstevel@tonic-gate $foo = 'bar'; 233*0Sstevel@tonic-gate $q = ${$foo} }, 234*0Sstevel@tonic-gate }, 235*0Sstevel@tonic-gate 'none' 236*0Sstevel@tonic-gate ); 237*0Sstevel@tonic-gate 238*0Sstevel@tonic-gate }; 239*0Sstevel@tonic-gate is( $@, '', q{no strict leakage in name => 'code'} ); 240*0Sstevel@tonic-gate 241*0Sstevel@tonic-gate eval { 242*0Sstevel@tonic-gate timethese( 1, 243*0Sstevel@tonic-gate { undeclared_var => sub { $i++; $i-- }, 244*0Sstevel@tonic-gate symbolic_ref => sub { $bar = 42; 245*0Sstevel@tonic-gate $foo = 'bar'; 246*0Sstevel@tonic-gate return ${$foo} }, 247*0Sstevel@tonic-gate }, 248*0Sstevel@tonic-gate 'none' 249*0Sstevel@tonic-gate ); 250*0Sstevel@tonic-gate }; 251*0Sstevel@tonic-gate is( $@, '', q{no strict leakage in name => sub { code }} ); 252*0Sstevel@tonic-gate 253*0Sstevel@tonic-gate # clear out buffer 254*0Sstevel@tonic-gate $out->read; 255*0Sstevel@tonic-gate} 256*0Sstevel@tonic-gate 257*0Sstevel@tonic-gate 258*0Sstevel@tonic-gatemy $code_to_test = { Foo => sub {$foo+=fib($ballast-2)}, 259*0Sstevel@tonic-gate Bar => sub {$bar+=fib($ballast)}}; 260*0Sstevel@tonic-gate# Keep these for later. 261*0Sstevel@tonic-gatemy $results; 262*0Sstevel@tonic-gate{ 263*0Sstevel@tonic-gate $foo = $bar = 0; 264*0Sstevel@tonic-gate select(OUT); 265*0Sstevel@tonic-gate my $start = times; 266*0Sstevel@tonic-gate $results = timethese(-0.1, $code_to_test, 'none'); 267*0Sstevel@tonic-gate my $end = times; 268*0Sstevel@tonic-gate select(STDOUT); 269*0Sstevel@tonic-gate 270*0Sstevel@tonic-gate is(ref ($results), 'HASH', "timethese should return a hashref"); 271*0Sstevel@tonic-gate isa_ok($results->{Foo}, 'Benchmark', "Foo value"); 272*0Sstevel@tonic-gate isa_ok($results->{Bar}, 'Benchmark', "Bar value"); 273*0Sstevel@tonic-gate eq_set([keys %$results], [qw(Foo Bar)], 'should be exactly two objects'); 274*0Sstevel@tonic-gate ok ($foo > 0, "Foo code was run"); 275*0Sstevel@tonic-gate ok ($bar > 0, "Bar code was run"); 276*0Sstevel@tonic-gate 277*0Sstevel@tonic-gate ok (($end - $start) > 0.1, "benchmarked code ran for over 0.1 seconds"); 278*0Sstevel@tonic-gate 279*0Sstevel@tonic-gate $got = $out->read(); 280*0Sstevel@tonic-gate # Remove any warnings about having too few iterations. 281*0Sstevel@tonic-gate $got =~ s/\(warning:[^\)]+\)//gs; 282*0Sstevel@tonic-gate is ($got =~ tr/ \t\n//c, 0, "format 'none' should suppress output"); 283*0Sstevel@tonic-gate} 284*0Sstevel@tonic-gatemy $graph_dissassembly = 285*0Sstevel@tonic-gate qr!^[ \t]+(\S+)[ \t]+(\w+)[ \t]+(\w+)[ \t]* # Title line 286*0Sstevel@tonic-gate \n[ \t]*(\w+)[ \t]+([0-9.]+(?:/s)?)[ \t]+(-+)[ \t]+(-?\d+%)[ \t]* 287*0Sstevel@tonic-gate \n[ \t]*(\w+)[ \t]+([0-9.]+(?:/s)?)[ \t]+(-?\d+%)[ \t]+(-+)[ \t]*$!xm; 288*0Sstevel@tonic-gate 289*0Sstevel@tonic-gatesub check_graph_consistency { 290*0Sstevel@tonic-gate my ( $ratetext, $slowc, $fastc, 291*0Sstevel@tonic-gate $slowr, $slowratet, $slowslow, $slowfastt, 292*0Sstevel@tonic-gate $fastr, $fastratet, $fastslowt, $fastfast) 293*0Sstevel@tonic-gate = @_; 294*0Sstevel@tonic-gate my $all_passed = 1; 295*0Sstevel@tonic-gate $all_passed 296*0Sstevel@tonic-gate &= is ($slowc, $slowr, "left col tag should be top row tag"); 297*0Sstevel@tonic-gate $all_passed 298*0Sstevel@tonic-gate &= is ($fastc, $fastr, "right col tag should be bottom row tag"); 299*0Sstevel@tonic-gate $all_passed &= 300*0Sstevel@tonic-gate like ($slowslow, qr/^-+/, "should be dash for comparing slow with slow"); 301*0Sstevel@tonic-gate $all_passed 302*0Sstevel@tonic-gate &= is ($slowslow, $fastfast, "slow v slow should be same as fast v fast"); 303*0Sstevel@tonic-gate my $slowrate = $slowratet; 304*0Sstevel@tonic-gate my $fastrate = $fastratet; 305*0Sstevel@tonic-gate my ($slow_is_rate, $fast_is_rate); 306*0Sstevel@tonic-gate unless ($slow_is_rate = $slowrate =~ s!/s!!) { 307*0Sstevel@tonic-gate # Slow is expressed as iters per second. 308*0Sstevel@tonic-gate $slowrate = 1/$slowrate if $slowrate; 309*0Sstevel@tonic-gate } 310*0Sstevel@tonic-gate unless ($fast_is_rate = $fastrate =~ s!/s!!) { 311*0Sstevel@tonic-gate # Fast is expressed as iters per second. 312*0Sstevel@tonic-gate $fastrate = 1/$fastrate if $fastrate; 313*0Sstevel@tonic-gate } 314*0Sstevel@tonic-gate if ($ratetext =~ /rate/i) { 315*0Sstevel@tonic-gate $all_passed 316*0Sstevel@tonic-gate &= ok ($slow_is_rate, "slow should be expressed as a rate"); 317*0Sstevel@tonic-gate $all_passed 318*0Sstevel@tonic-gate &= ok ($fast_is_rate, "fast should be expressed as a rate"); 319*0Sstevel@tonic-gate } else { 320*0Sstevel@tonic-gate $all_passed &= 321*0Sstevel@tonic-gate ok (!$slow_is_rate, "slow should be expressed as a iters per second"); 322*0Sstevel@tonic-gate $all_passed &= 323*0Sstevel@tonic-gate ok (!$fast_is_rate, "fast should be expressed as a iters per second"); 324*0Sstevel@tonic-gate } 325*0Sstevel@tonic-gate 326*0Sstevel@tonic-gate (my $slowfast = $slowfastt) =~ s!%!!; 327*0Sstevel@tonic-gate (my $fastslow = $fastslowt) =~ s!%!!; 328*0Sstevel@tonic-gate if ($slowrate < $fastrate) { 329*0Sstevel@tonic-gate pass ("slow rate is less than fast rate"); 330*0Sstevel@tonic-gate unless (ok ($slowfast <= 0 && $slowfast >= -100, 331*0Sstevel@tonic-gate "slowfast should be less than or equal to zero, and >= -100")) { 332*0Sstevel@tonic-gate print STDERR "# slowfast $slowfast\n"; 333*0Sstevel@tonic-gate $all_passed = 0; 334*0Sstevel@tonic-gate } 335*0Sstevel@tonic-gate unless (ok ($fastslow > 0, "fastslow should be > 0")) { 336*0Sstevel@tonic-gate print STDERR "# fastslow $fastslow\n"; 337*0Sstevel@tonic-gate $all_passed = 0; 338*0Sstevel@tonic-gate } 339*0Sstevel@tonic-gate } else { 340*0Sstevel@tonic-gate $all_passed 341*0Sstevel@tonic-gate &= is ($slowrate, $fastrate, 342*0Sstevel@tonic-gate "slow rate isn't less than fast rate, so should be the same"); 343*0Sstevel@tonic-gate # In OpenBSD the $slowfast is sometimes a really, really, really 344*0Sstevel@tonic-gate # small number less than zero, and this gets stringified as -0. 345*0Sstevel@tonic-gate $all_passed 346*0Sstevel@tonic-gate &= like ($slowfast, qr/^-?0$/, "slowfast should be zero"); 347*0Sstevel@tonic-gate $all_passed 348*0Sstevel@tonic-gate &= like ($fastslow, qr/^-?0$/, "fastslow should be zero"); 349*0Sstevel@tonic-gate } 350*0Sstevel@tonic-gate return $all_passed; 351*0Sstevel@tonic-gate} 352*0Sstevel@tonic-gate 353*0Sstevel@tonic-gatesub check_graph_vs_output { 354*0Sstevel@tonic-gate my ($chart, $got) = @_; 355*0Sstevel@tonic-gate my ( $ratetext, $slowc, $fastc, 356*0Sstevel@tonic-gate $slowr, $slowratet, $slowslow, $slowfastt, 357*0Sstevel@tonic-gate $fastr, $fastratet, $fastslowt, $fastfast) 358*0Sstevel@tonic-gate = $got =~ $graph_dissassembly; 359*0Sstevel@tonic-gate my $all_passed 360*0Sstevel@tonic-gate = check_graph_consistency ( $ratetext, $slowc, $fastc, 361*0Sstevel@tonic-gate $slowr, $slowratet, $slowslow, $slowfastt, 362*0Sstevel@tonic-gate $fastr, $fastratet, $fastslowt, $fastfast); 363*0Sstevel@tonic-gate $all_passed 364*0Sstevel@tonic-gate &= is_deeply ($chart, [['', $ratetext, $slowc, $fastc], 365*0Sstevel@tonic-gate [$slowr, $slowratet, $slowslow, $slowfastt], 366*0Sstevel@tonic-gate [$fastr, $fastratet, $fastslowt, $fastfast]], 367*0Sstevel@tonic-gate "check the chart layout matches the formatted output"); 368*0Sstevel@tonic-gate unless ($all_passed) { 369*0Sstevel@tonic-gate print STDERR "# Something went wrong there. I got this chart:\n"; 370*0Sstevel@tonic-gate print STDERR "# $_\n" foreach split /\n/, $got; 371*0Sstevel@tonic-gate } 372*0Sstevel@tonic-gate} 373*0Sstevel@tonic-gate 374*0Sstevel@tonic-gatesub check_graph { 375*0Sstevel@tonic-gate my ($title, $row1, $row2) = @_; 376*0Sstevel@tonic-gate is (scalar @$title, 4, "Four entries in title row"); 377*0Sstevel@tonic-gate is (scalar @$row1, 4, "Four entries in first row"); 378*0Sstevel@tonic-gate is (scalar @$row2, 4, "Four entries in second row"); 379*0Sstevel@tonic-gate is (shift @$title, '', "First entry of output graph should be ''"); 380*0Sstevel@tonic-gate check_graph_consistency (@$title, @$row1, @$row2); 381*0Sstevel@tonic-gate} 382*0Sstevel@tonic-gate 383*0Sstevel@tonic-gate{ 384*0Sstevel@tonic-gate select(OUT); 385*0Sstevel@tonic-gate my $start = times; 386*0Sstevel@tonic-gate my $chart = cmpthese( -0.1, { a => "++\$i", b => "\$i = sqrt(\$i++)" }, "auto" ) ; 387*0Sstevel@tonic-gate my $end = times; 388*0Sstevel@tonic-gate select(STDOUT); 389*0Sstevel@tonic-gate ok (($end - $start) > 0.05, "benchmarked code ran for over 0.05 seconds"); 390*0Sstevel@tonic-gate 391*0Sstevel@tonic-gate $got = $out->read(); 392*0Sstevel@tonic-gate # Remove any warnings about having too few iterations. 393*0Sstevel@tonic-gate $got =~ s/\(warning:[^\)]+\)//gs; 394*0Sstevel@tonic-gate 395*0Sstevel@tonic-gate like ($got, qr/running\W+a\W+b.*?for at least 0\.1 CPU second/s, 396*0Sstevel@tonic-gate 'check title'); 397*0Sstevel@tonic-gate # Remove the title 398*0Sstevel@tonic-gate $got =~ s/.*\.\.\.//s; 399*0Sstevel@tonic-gate like ($got, $Default_Pattern, 'should find default format somewhere'); 400*0Sstevel@tonic-gate like ($got, $graph_dissassembly, "Should find the output graph somewhere"); 401*0Sstevel@tonic-gate check_graph_vs_output ($chart, $got); 402*0Sstevel@tonic-gate} 403*0Sstevel@tonic-gate 404*0Sstevel@tonic-gate# Not giving auto should suppress timethese results. 405*0Sstevel@tonic-gate{ 406*0Sstevel@tonic-gate select(OUT); 407*0Sstevel@tonic-gate my $start = times; 408*0Sstevel@tonic-gate my $chart = cmpthese( -0.1, { a => "++\$i", b => "\$i = sqrt(\$i++)" } ) ; 409*0Sstevel@tonic-gate my $end = times; 410*0Sstevel@tonic-gate select(STDOUT); 411*0Sstevel@tonic-gate ok (($end - $start) > 0.05, "benchmarked code ran for over 0.05 seconds"); 412*0Sstevel@tonic-gate 413*0Sstevel@tonic-gate $got = $out->read(); 414*0Sstevel@tonic-gate # Remove any warnings about having too few iterations. 415*0Sstevel@tonic-gate $got =~ s/\(warning:[^\)]+\)//gs; 416*0Sstevel@tonic-gate 417*0Sstevel@tonic-gate unlike ($got, qr/running\W+a\W+b.*?for at least 0\.1 CPU second/s, 418*0Sstevel@tonic-gate 'should not have title'); 419*0Sstevel@tonic-gate # Remove the title 420*0Sstevel@tonic-gate $got =~ s/.*\.\.\.//s; 421*0Sstevel@tonic-gate unlike ($got, $Default_Pattern, 'should not find default format somewhere'); 422*0Sstevel@tonic-gate like ($got, $graph_dissassembly, "Should find the output graph somewhere"); 423*0Sstevel@tonic-gate check_graph_vs_output ($chart, $got); 424*0Sstevel@tonic-gate} 425*0Sstevel@tonic-gate 426*0Sstevel@tonic-gate{ 427*0Sstevel@tonic-gate $foo = $bar = 0; 428*0Sstevel@tonic-gate select(OUT); 429*0Sstevel@tonic-gate my $chart = cmpthese( 10, $code_to_test, 'nop' ) ; 430*0Sstevel@tonic-gate select(STDOUT); 431*0Sstevel@tonic-gate ok ($foo > 0, "Foo code was run"); 432*0Sstevel@tonic-gate ok ($bar > 0, "Bar code was run"); 433*0Sstevel@tonic-gate 434*0Sstevel@tonic-gate $got = $out->read(); 435*0Sstevel@tonic-gate # Remove any warnings about having too few iterations. 436*0Sstevel@tonic-gate $got =~ s/\(warning:[^\)]+\)//gs; 437*0Sstevel@tonic-gate like ($got, qr/timing 10 iterations of\s+Bar\W+Foo\W*?\.\.\./s, 438*0Sstevel@tonic-gate 'check title'); 439*0Sstevel@tonic-gate # Remove the title 440*0Sstevel@tonic-gate $got =~ s/.*\.\.\.//s; 441*0Sstevel@tonic-gate like ($got, $Nop_Pattern, 'specify format as nop'); 442*0Sstevel@tonic-gate like ($got, $graph_dissassembly, "Should find the output graph somewhere"); 443*0Sstevel@tonic-gate check_graph_vs_output ($chart, $got); 444*0Sstevel@tonic-gate} 445*0Sstevel@tonic-gate 446*0Sstevel@tonic-gate{ 447*0Sstevel@tonic-gate $foo = $bar = 0; 448*0Sstevel@tonic-gate select(OUT); 449*0Sstevel@tonic-gate my $chart = cmpthese( 10, $code_to_test, 'none' ) ; 450*0Sstevel@tonic-gate select(STDOUT); 451*0Sstevel@tonic-gate ok ($foo > 0, "Foo code was run"); 452*0Sstevel@tonic-gate ok ($bar > 0, "Bar code was run"); 453*0Sstevel@tonic-gate 454*0Sstevel@tonic-gate $got = $out->read(); 455*0Sstevel@tonic-gate # Remove any warnings about having too few iterations. 456*0Sstevel@tonic-gate $got =~ s/\(warning:[^\)]+\)//gs; 457*0Sstevel@tonic-gate $got =~ s/^[ \t\n]+//s; # Remove all the whitespace from the beginning 458*0Sstevel@tonic-gate is ($got, '', "format 'none' should suppress output"); 459*0Sstevel@tonic-gate is (ref $chart, 'ARRAY', "output should be an array ref"); 460*0Sstevel@tonic-gate # Some of these will go bang if the preceding test fails. There will be 461*0Sstevel@tonic-gate # a big clue as to why, from the previous test's diagnostic 462*0Sstevel@tonic-gate is (ref $chart->[0], 'ARRAY', "output should be an array of arrays"); 463*0Sstevel@tonic-gate check_graph (@$chart); 464*0Sstevel@tonic-gate} 465*0Sstevel@tonic-gate 466*0Sstevel@tonic-gate{ 467*0Sstevel@tonic-gate $foo = $bar = 0; 468*0Sstevel@tonic-gate select(OUT); 469*0Sstevel@tonic-gate my $chart = cmpthese( $results ) ; 470*0Sstevel@tonic-gate select(STDOUT); 471*0Sstevel@tonic-gate is ($foo, 0, "Foo code was not run"); 472*0Sstevel@tonic-gate is ($bar, 0, "Bar code was not run"); 473*0Sstevel@tonic-gate 474*0Sstevel@tonic-gate $got = $out->read(); 475*0Sstevel@tonic-gate ok ($got !~ /\.\.\./s, 'check that there is no title'); 476*0Sstevel@tonic-gate like ($got, $graph_dissassembly, "Should find the output graph somewhere"); 477*0Sstevel@tonic-gate check_graph_vs_output ($chart, $got); 478*0Sstevel@tonic-gate} 479*0Sstevel@tonic-gate 480*0Sstevel@tonic-gate{ 481*0Sstevel@tonic-gate $foo = $bar = 0; 482*0Sstevel@tonic-gate select(OUT); 483*0Sstevel@tonic-gate my $chart = cmpthese( $results, 'none' ) ; 484*0Sstevel@tonic-gate select(STDOUT); 485*0Sstevel@tonic-gate is ($foo, 0, "Foo code was not run"); 486*0Sstevel@tonic-gate is ($bar, 0, "Bar code was not run"); 487*0Sstevel@tonic-gate 488*0Sstevel@tonic-gate $got = $out->read(); 489*0Sstevel@tonic-gate is ($got, '', "'none' should suppress all output"); 490*0Sstevel@tonic-gate is (ref $chart, 'ARRAY', "output should be an array ref"); 491*0Sstevel@tonic-gate # Some of these will go bang if the preceding test fails. There will be 492*0Sstevel@tonic-gate # a big clue as to why, from the previous test's diagnostic 493*0Sstevel@tonic-gate is (ref $chart->[0], 'ARRAY', "output should be an array of arrays"); 494*0Sstevel@tonic-gate check_graph (@$chart); 495*0Sstevel@tonic-gate} 496*0Sstevel@tonic-gate 497*0Sstevel@tonic-gate###}my $out = tie *OUT, 'TieOut'; my ($got); ### 498*0Sstevel@tonic-gate 499*0Sstevel@tonic-gatemy $debug = tie *STDERR, 'TieOut'; 500*0Sstevel@tonic-gate 501*0Sstevel@tonic-gate$bar = 0; 502*0Sstevel@tonic-gateisa_ok(timeit(5, '++$bar'), 'Benchmark', "timeit eval"); 503*0Sstevel@tonic-gateis ($bar, 5, "benchmarked code was run 5 times"); 504*0Sstevel@tonic-gateis ($debug->read(), '', "There was no debug output"); 505*0Sstevel@tonic-gate 506*0Sstevel@tonic-gateBenchmark->debug(1); 507*0Sstevel@tonic-gate 508*0Sstevel@tonic-gate$bar = 0; 509*0Sstevel@tonic-gateselect(OUT); 510*0Sstevel@tonic-gate$got = timeit(5, '++$bar'); 511*0Sstevel@tonic-gateselect(STDOUT); 512*0Sstevel@tonic-gateisa_ok($got, 'Benchmark', "timeit eval"); 513*0Sstevel@tonic-gateis ($bar, 5, "benchmarked code was run 5 times"); 514*0Sstevel@tonic-gateis ($out->read(), '', "There was no STDOUT output with debug enabled"); 515*0Sstevel@tonic-gateisnt ($debug->read(), '', "There was STDERR debug output with debug enabled"); 516*0Sstevel@tonic-gate 517*0Sstevel@tonic-gateBenchmark->debug(0); 518*0Sstevel@tonic-gate 519*0Sstevel@tonic-gate$bar = 0; 520*0Sstevel@tonic-gateisa_ok(timeit(5, '++$bar'), 'Benchmark', "timeit eval"); 521*0Sstevel@tonic-gateis ($bar, 5, "benchmarked code was run 5 times"); 522*0Sstevel@tonic-gateis ($debug->read(), '', "There was no debug output debug disabled"); 523*0Sstevel@tonic-gate 524*0Sstevel@tonic-gateundef $debug; 525*0Sstevel@tonic-gateuntie *STDERR; 526*0Sstevel@tonic-gate 527*0Sstevel@tonic-gate# To check the cache we are poking where we don't belong, inside the namespace. 528*0Sstevel@tonic-gate# The way benchmark is written We can't actually check whehter the cache is 529*0Sstevel@tonic-gate# being used, merely what's become cached. 530*0Sstevel@tonic-gate 531*0Sstevel@tonic-gateclearallcache(); 532*0Sstevel@tonic-gatemy @before_keys = keys %Benchmark::Cache; 533*0Sstevel@tonic-gate$bar = 0; 534*0Sstevel@tonic-gateisa_ok(timeit(5, '++$bar'), 'Benchmark', "timeit eval"); 535*0Sstevel@tonic-gateis ($bar, 5, "benchmarked code was run 5 times"); 536*0Sstevel@tonic-gatemy @after5_keys = keys %Benchmark::Cache; 537*0Sstevel@tonic-gate$bar = 0; 538*0Sstevel@tonic-gateisa_ok(timeit(10, '++$bar'), 'Benchmark', "timeit eval"); 539*0Sstevel@tonic-gateis ($bar, 10, "benchmarked code was run 10 times"); 540*0Sstevel@tonic-gateok (!eq_array ([keys %Benchmark::Cache], \@after5_keys), "10 differs from 5"); 541*0Sstevel@tonic-gate 542*0Sstevel@tonic-gateclearcache(10); 543*0Sstevel@tonic-gate# Hash key order will be the same if there are the same keys. 544*0Sstevel@tonic-gateis_deeply ([keys %Benchmark::Cache], \@after5_keys, 545*0Sstevel@tonic-gate "cleared 10, only cached results for 5 should remain"); 546*0Sstevel@tonic-gate 547*0Sstevel@tonic-gateclearallcache(); 548*0Sstevel@tonic-gateis_deeply ([keys %Benchmark::Cache], \@before_keys, 549*0Sstevel@tonic-gate "back to square 1 when we clear the cache again?"); 550*0Sstevel@tonic-gate 551*0Sstevel@tonic-gate 552*0Sstevel@tonic-gate{ # Check usage error messages 553*0Sstevel@tonic-gate my %usage = %Benchmark::_Usage; 554*0Sstevel@tonic-gate delete $usage{runloop}; # not public, not worrying about it just now 555*0Sstevel@tonic-gate 556*0Sstevel@tonic-gate my @takes_no_args = qw(clearallcache disablecache enablecache); 557*0Sstevel@tonic-gate 558*0Sstevel@tonic-gate my %cmpthese = ('forgot {}' => 'cmpthese( 42, foo => sub { 1 } )', 559*0Sstevel@tonic-gate 'not result' => 'cmpthese(42)', 560*0Sstevel@tonic-gate 'array ref' => 'cmpthese( 42, [ foo => sub { 1 } ] )', 561*0Sstevel@tonic-gate ); 562*0Sstevel@tonic-gate while( my($name, $code) = each %cmpthese ) { 563*0Sstevel@tonic-gate eval $code; 564*0Sstevel@tonic-gate is( $@, $usage{cmpthese}, "cmpthese usage: $name" ); 565*0Sstevel@tonic-gate } 566*0Sstevel@tonic-gate 567*0Sstevel@tonic-gate my %timethese = ('forgot {}' => 'timethese( 42, foo => sub { 1 } )', 568*0Sstevel@tonic-gate 'no code' => 'timethese(42)', 569*0Sstevel@tonic-gate 'array ref' => 'timethese( 42, [ foo => sub { 1 } ] )', 570*0Sstevel@tonic-gate ); 571*0Sstevel@tonic-gate 572*0Sstevel@tonic-gate while( my($name, $code) = each %timethese ) { 573*0Sstevel@tonic-gate eval $code; 574*0Sstevel@tonic-gate is( $@, $usage{timethese}, "timethese usage: $name" ); 575*0Sstevel@tonic-gate } 576*0Sstevel@tonic-gate 577*0Sstevel@tonic-gate 578*0Sstevel@tonic-gate while( my($func, $usage) = each %usage ) { 579*0Sstevel@tonic-gate next if grep $func eq $_, @takes_no_args; 580*0Sstevel@tonic-gate eval "$func()"; 581*0Sstevel@tonic-gate is( $@, $usage, "$func usage: no args" ); 582*0Sstevel@tonic-gate } 583*0Sstevel@tonic-gate 584*0Sstevel@tonic-gate foreach my $func (@takes_no_args) { 585*0Sstevel@tonic-gate eval "$func(42)"; 586*0Sstevel@tonic-gate is( $@, $usage{$func}, "$func usage: with args" ); 587*0Sstevel@tonic-gate } 588*0Sstevel@tonic-gate} 589*0Sstevel@tonic-gate 590*0Sstevel@tonic-gate 591*0Sstevel@tonic-gatepackage TieOut; 592*0Sstevel@tonic-gate 593*0Sstevel@tonic-gatesub TIEHANDLE { 594*0Sstevel@tonic-gate my $class = shift; 595*0Sstevel@tonic-gate bless(\( my $ref = ''), $class); 596*0Sstevel@tonic-gate} 597*0Sstevel@tonic-gate 598*0Sstevel@tonic-gatesub PRINT { 599*0Sstevel@tonic-gate my $self = shift; 600*0Sstevel@tonic-gate $$self .= join('', @_); 601*0Sstevel@tonic-gate} 602*0Sstevel@tonic-gate 603*0Sstevel@tonic-gatesub PRINTF { 604*0Sstevel@tonic-gate my $self = shift; 605*0Sstevel@tonic-gate $$self .= sprintf shift, @_; 606*0Sstevel@tonic-gate} 607*0Sstevel@tonic-gate 608*0Sstevel@tonic-gatesub read { 609*0Sstevel@tonic-gate my $self = shift; 610*0Sstevel@tonic-gate return substr($$self, 0, length($$self), ''); 611*0Sstevel@tonic-gate} 612