File Coverage

blib/lib/PerlBench.pm
Criterion Covered Total %
statement 42 136 30.8
branch 20 62 32.2
condition 1 14 7.1
subroutine 7 11 63.6
pod 0 5 0.0
total 70 228 30.7


line stmt bran cond sub pod time code
1             package PerlBench;
2              
3 1     1   3874 use strict;
  1         1  
  1         25  
4 1     1   4 use base 'Exporter';
  1         3  
  1         104  
5             our @EXPORT_OK = qw(timeit make_timeit_sub_code sec_f);
6              
7             our $VERSION = "0.93";
8              
9 1     1   351 use PerlBench::Stats qw(calc_stats);
  1         2  
  1         45  
10 1     1   389 use Time::HiRes qw(gettimeofday);
  1         1019  
  1         4  
11 1     1   195 use Carp qw(croak);
  1         2  
  1         1243  
12              
13              
14             sub timeit {
15 0     0 0 0 my($code, %opt) = @_;
16 0         0 my $init = $opt{init};
17              
18             # XXX auto determine how long we need to time stuff
19 0   0     0 my $enough = $opt{enough} || 0.5;
20              
21             # auto determine $loop_count and $repeat_count
22             print STDERR "# Determine loop count - enough is " . sec_f($enough) . "\n"
23 0 0       0 if $opt{verbose};
24 0         0 my($loop_count, $repeat_count) = do {
25 0         0 my $count = 1;
26 0   0     0 my $repeat = $opt{repeat} || 1;
27 0         0 while (1) {
28 0 0       0 print STDERR "# $count ==> " if $opt{verbose};
29 0         0 my $t = timeit_once($code, $init, $count, $repeat);
30 0 0       0 print STDERR sec_f($t, undef), "\n" if $opt{verbose};
31 0 0       0 last if $t > $enough;
32 0 0       0 if ($t < 0.00001) {
    0          
33 0         0 $count *= 1000;
34 0         0 next;
35             }
36             elsif ($t < 0.01) {
37 0         0 $count *= 2;
38 0         0 next;
39             }
40 0         0 $count = int($count * ($enough / $t) * 1.05) + 1;
41             }
42 0         0 ($count, $repeat);
43             };
44              
45 0         0 my @experiment;
46 0         0 push(@experiment, {
47             loop_count => $loop_count,
48             repeat_count => $repeat_count,
49             });
50 0 0       0 $loop_count++ if $loop_count % 2;
51 0         0 push(@experiment, {
52             loop_count => $loop_count / 2,
53             repeat_count => $repeat_count * 2,
54             });
55              
56 0         0 my $pl = "tt$$.pl";
57 0 0       0 open(my $fh, ">", $pl) || die "Can't create $pl: $!";
58 0         0 print $fh "#!perl\n";
59 0         0 print $fh "use strict;\n";
60 0         0 print $fh "require Time::HiRes;\n";
61 0 0       0 print $fh "{\n $init;\n" if $init;
62 0         0 print $fh "my \@TIMEIT = (\n";
63 0         0 for my $e (@experiment) {
64 0         0 print $fh &make_timeit_sub_code($code, undef, $e->{loop_count}, $e->{repeat_count}), ",\n";
65             }
66 0         0 print $fh ");\n";
67              
68 0         0 print $fh <<'EOT';
69              
70             my $e = shift || die;
71             my $trials = shift || die;
72             my $loop_count = shift || die;
73             my @t;
74             my $sum = 0;
75             for (1.. $trials) {
76             print "t$e=", $TIMEIT[$e-1]->(), "\n";
77             }
78             print "---\n";
79             EOT
80 0 0       0 print $fh "}\n" if $init;
81 0 0       0 close($fh) || die "Can't write $pl: $!";
82              
83 0 0       0 print STDERR "# Running tests...\n" if $opt{verbose};
84 0         0 my $rounds = 4;
85 0         0 for my $round (1..$rounds) {
86 0 0 0     0 printf STDERR "# %.0f%%\n", (($round-1)/$rounds)*100 if $opt{verbose} && $round > 1;
87 0         0 my $e_num = 0;
88 0         0 for my $e (@experiment) {
89 0         0 $e_num++;
90 0 0       0 open($fh, "$^X $pl $e_num 7 $loop_count |") || die "Can't run $pl: $!";
91 0         0 while (<$fh>) {
92             #print "XXX $_";
93 0 0       0 if (/^t(\d+)=(.*)/) {
94 0 0       0 die unless $1 eq $e_num;
95 0         0 my $t = $2+0;
96 0         0 push(@{$e->{t}}, $t);
  0         0  
97             }
98             }
99 0         0 close($fh);
100             }
101             }
102 0         0 unlink($pl);
103 0 0       0 print STDERR "# done\n" if $opt{verbose};
104              
105 0         0 for my $e (@experiment) {
106 0   0     0 my $t = $e->{t} ||return;
107 0         0 calc_stats($e->{t}, $e);
108              
109 0         0 my $count = $e->{loop_count} * $e->{repeat_count};
110 0         0 $e->{count} = $count;
111             }
112              
113 0         0 my $loop_overhead = do {
114 0         0 my $e1 = $experiment[0];
115 0         0 my $e2 = $experiment[-1];
116 0         0 my $t1 = $e1->{med} / $e1->{loop_count};
117 0         0 my $t2 = $e2->{med} / $e2->{loop_count};
118 0         0 my $f = $e2->{repeat_count} / $e1->{repeat_count};
119 0         0 $f * $t1 - $t2;
120             };
121              
122 0         0 for my $e (@experiment) {
123 0         0 $e->{loop_overhead} = $loop_overhead * $e->{loop_count};
124 0         0 $e->{loop_overhead_relative} = $e->{loop_overhead} / $e->{med};
125             }
126              
127 0         0 my %res;
128 0         0 $res{x} = \@experiment;
129              
130             # calculate combined stats
131 0         0 my @t;
132 0         0 for my $e (@experiment) {
133 0         0 my $c = $e->{count};
134 0         0 my $o = $e->{loop_overhead};
135 0         0 push(@t, map { ($_-$o)/$c } @{$e->{t}});
  0         0  
  0         0  
136             }
137 0         0 calc_stats(\@t, \%res);
138              
139 0         0 for my $f (qw(count loop_overhead_relative)) {
140             # XXX avg
141 0         0 $res{$f} = $experiment[0]{$f};
142             }
143              
144 0         0 return \%res;
145             }
146              
147             sub timeit_once {
148 0     0 0 0 return make_timeit_sub(@_)->();
149             }
150              
151             sub make_timeit_sub {
152 0     0 0 0 my $code = make_timeit_sub_code(@_);
153 0         0 my $sub = eval $code;
154 0 0       0 die $@ if $@;
155 0         0 return $sub;
156             }
157              
158             sub make_timeit_sub_code {
159 0     0 0 0 my($code, $init, $loop_count, $repeat_count) = @_;
160 0         0 $loop_count = int($loop_count);
161 0 0       0 die unless $loop_count > 0;
162 0 0       0 die if $loop_count + 1 == $loop_count; # too large
163 0   0     0 $repeat_count ||= 1;
164 0 0       0 $init = "" unless defined $init;
165 0         0 return <
166             sub {
167             my \$COUNT = $loop_count;
168             \$COUNT++;
169             package main;
170             EOT1
171              
172             my($BEFORE_S, $BEFORE_US) = Time::HiRes::gettimeofday();
173             while (--$COUNT) {
174             EOT2
175              
176             }
177             my($AFTER_S, $AFTER_US) = Time::HiRes::gettimeofday();
178             return ($AFTER_S - $BEFORE_S) + ($AFTER_US - $BEFORE_US)/1e6;
179             }
180             EOT3
181             }
182              
183 0         0 BEGIN {
184 1     1   8 my %UNITS = (
185             "h" => 1/3600,
186             "min" => 1/60,
187             "s" => 1,
188             "ms" => 1e3,
189             "µs" => 1e6,
190             "ns" => 1e9,
191             );
192              
193             my @UNITS =
194 10         35 sort { $b->[1] <=> $a->[1] }
195 1         4 map { [$_ => $UNITS{$_}] }
  6         18  
196             keys %UNITS;
197              
198             sub sec_f {
199 20     20 0 536 my($t, $d, $u) = @_;
200 20         46 my $f;
201 20 100       57 if (defined $u) {
202 3   33     16 $f = $UNITS{$u} || croak("Unknown unit '$u'");
203             }
204             else {
205 17         71 for (my $i = 1; $i < @UNITS; $i++) {
206 49 100       215 if ($t < 1/$UNITS[$i][1]) {
207 16         35 ($u, $f) = @{$UNITS[$i-1]};
  16         54  
208 16         40 last;
209             }
210             }
211 17 100       55 unless ($u) {
212 1         3 ($u, $f) = @{$UNITS[-1]};
  1         5  
213             }
214             }
215              
216 20 100       63 my $dev = defined($d) ? 1 : "";
217 20 100       63 $d = $t unless $dev;
218              
219 20 100       57 if ($f != 1) {
220 13         49 $_ *= $f for $t, $d;
221             }
222              
223 20         41 my $p = 0;
224 20 100       86 if ($d < 0.05) {
    100          
    100          
225 2         5 $p = 3;
226             }
227             elsif ($d < 0.5) {
228 2         6 $p = 2;
229             }
230             elsif ($d < 5) {
231 7         14 $p = 1;
232             }
233              
234 20 100       78 $dev = sprintf(" ±%.*f", $p, $d) if $dev;
235 20         201 return sprintf("%.*f %s%s", $p, $t, $u, $dev);
236             }
237             }
238              
239             1;