xref: /onnv-gate/usr/src/cmd/perl/5.8.4/distrib/lib/Benchmark.t (revision 0:68f95e015346)
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