| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
#!/usr/bin/perl |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# Can be invoked as: |
|
4
|
|
|
|
|
|
|
# ./bench.PL # generates FU/Benchmarks.pod, running new benchmarks as necessary |
|
5
|
|
|
|
|
|
|
# ./bench.PL id x y # invalidate cache for the (regex-)matching benchmark IDs, x and y and re-run them |
|
6
|
|
|
|
|
|
|
# ./bench.PL exec id x y # Run just the given benchmark and exit |
|
7
|
|
|
|
|
|
|
# |
|
8
|
|
|
|
|
|
|
# This script obviously has more dependencies than the FU distribution itself. |
|
9
|
|
|
|
|
|
|
# It's supposed to be used by maintainers, not users. |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
# MakeMaker automatically runs this script as a default built step, but that's not very useful. |
|
13
|
1
|
50
|
33
|
1
|
|
7241
|
BEGIN { exit if @ARGV && @ARGV[0] eq 'bench'; } |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
use v5.36; |
|
16
|
|
|
|
|
|
|
use builtin 'true', 'false'; |
|
17
|
|
|
|
|
|
|
use Benchmark ':hireswallclock', 'timethis'; |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
my %modules = map +($_, eval "require $_; \$${_}::VERSION"), qw/ |
|
20
|
|
|
|
|
|
|
FU |
|
21
|
|
|
|
|
|
|
Cpanel::JSON::XS |
|
22
|
|
|
|
|
|
|
JSON::PP |
|
23
|
|
|
|
|
|
|
JSON::SIMD |
|
24
|
|
|
|
|
|
|
JSON::Tiny |
|
25
|
|
|
|
|
|
|
JSON::XS |
|
26
|
|
|
|
|
|
|
TUWF::XML |
|
27
|
|
|
|
|
|
|
HTML::Tiny |
|
28
|
|
|
|
|
|
|
XML::Writer |
|
29
|
|
|
|
|
|
|
DBD::Pg |
|
30
|
|
|
|
|
|
|
Pg::PQ |
|
31
|
|
|
|
|
|
|
/; |
|
32
|
|
|
|
|
|
|
use FU::Pg; |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
my @exec = $ARGV[0] && $ARGV[0] eq 'exec' ? @ARGV[1..3] : (); |
|
35
|
|
|
|
|
|
|
my @run = !@exec && @ARGV && (qr/$ARGV[0]/i, $ARGV[1] ? qr/$ARGV[1]/i : qr/.*/, $ARGV[2] ? qr/$ARGV[2]/i : qr/.*/); |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
my %data; # "id x y" => { id x y rate exists } |
|
38
|
|
|
|
|
|
|
my @bench; # [ id, text, [ x_1, .. ], [ [ y_1, mod_1, sub_1, .. ], .. ] ] |
|
39
|
|
|
|
|
|
|
my %oldmodules; |
|
40
|
|
|
|
|
|
|
if (!@exec) { |
|
41
|
|
|
|
|
|
|
if (open my $F, '<', 'FU/Benchmarks.pod') { |
|
42
|
|
|
|
|
|
|
my $indata; |
|
43
|
|
|
|
|
|
|
while (<$F>) { |
|
44
|
|
|
|
|
|
|
chomp; |
|
45
|
|
|
|
|
|
|
$oldmodules{$1} = $2 if /^=item L<([a-zA-Z0-9:]+)> ([0-9.]+)/; |
|
46
|
|
|
|
|
|
|
$indata = 1 if /^# Cached data used by bench\.PL/; |
|
47
|
|
|
|
|
|
|
next if !$indata || !$_ || /^#/; |
|
48
|
|
|
|
|
|
|
my %d; |
|
49
|
|
|
|
|
|
|
@d{qw/id x y rate/} = split /\t/; |
|
50
|
|
|
|
|
|
|
$data{"$d{id} $d{x} $d{y}"} = \%d; |
|
51
|
|
|
|
|
|
|
} |
|
52
|
|
|
|
|
|
|
} |
|
53
|
|
|
|
|
|
|
} |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
sub fmtbench($id, $text, $xs, $ys) { |
|
57
|
|
|
|
|
|
|
my $r = "$text\n\n"; |
|
58
|
|
|
|
|
|
|
if (@$xs > 1) { |
|
59
|
|
|
|
|
|
|
$r .= sprintf '%18s', ''; |
|
60
|
|
|
|
|
|
|
$r .= sprintf '%12s', $_ for @$xs; |
|
61
|
|
|
|
|
|
|
$r .= "\n"; |
|
62
|
|
|
|
|
|
|
} |
|
63
|
|
|
|
|
|
|
for my ($n, $yr) (builtin::indexed @$ys) { |
|
64
|
|
|
|
|
|
|
my $x = $xs->[$n]; |
|
65
|
|
|
|
|
|
|
my ($y, $m, @ys) = @$yr; |
|
66
|
|
|
|
|
|
|
$m ||= $y; |
|
67
|
|
|
|
|
|
|
$r .= sprintf '%18s', $y; |
|
68
|
|
|
|
|
|
|
for my $i (0..$#$xs) { |
|
69
|
|
|
|
|
|
|
my $d = $data{"$id $xs->[$i] $y"}; |
|
70
|
|
|
|
|
|
|
$r .= $d && $d->{rate} ? sprintf '%10d/s', $d->{rate} : sprintf '%12s', '-'; |
|
71
|
|
|
|
|
|
|
} |
|
72
|
|
|
|
|
|
|
$r .= "\n"; |
|
73
|
|
|
|
|
|
|
} |
|
74
|
|
|
|
|
|
|
"$r\n" |
|
75
|
|
|
|
|
|
|
} |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
$SIG{INT} = $SIG{HUP} = sub { exit }; |
|
78
|
|
|
|
|
|
|
END { |
|
79
|
|
|
|
|
|
|
exit if @exec; |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
open my $F, '>FU/Benchmarks.pod' or die $!; |
|
82
|
|
|
|
|
|
|
select $F; |
|
83
|
|
|
|
|
|
|
while () { |
|
84
|
|
|
|
|
|
|
s/^%/=/; |
|
85
|
|
|
|
|
|
|
s#^:modules#join '', map sprintf("=item L<%s> %s\n\n", $_, $modules{$_}), sort keys %modules#e; |
|
86
|
|
|
|
|
|
|
s#^:benches (.+)#join '', map fmtbench(@$_), grep $_->[0] =~ /$1/, @bench#e; |
|
87
|
|
|
|
|
|
|
print; |
|
88
|
|
|
|
|
|
|
} |
|
89
|
|
|
|
|
|
|
for (sort keys %data) { |
|
90
|
|
|
|
|
|
|
my $b = $data{$_}; |
|
91
|
|
|
|
|
|
|
print join("\t", map $_//'', @{$b}{qw/ id x y rate /})."\n"; |
|
92
|
|
|
|
|
|
|
} |
|
93
|
|
|
|
|
|
|
} |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
sub def($id, $text, $xs, @ys) { |
|
98
|
|
|
|
|
|
|
for my ($ya) (@ys) { |
|
99
|
|
|
|
|
|
|
my($y, $m, @sub) = @$ya; |
|
100
|
|
|
|
|
|
|
$m ||= $y; |
|
101
|
|
|
|
|
|
|
for my($i, $x) (builtin::indexed @$xs) { |
|
102
|
|
|
|
|
|
|
next if !$sub[$i]; |
|
103
|
|
|
|
|
|
|
my $d = "$id $x $y"; |
|
104
|
|
|
|
|
|
|
$data{$d} ||= { id => $id, x => $x, y => $y }; |
|
105
|
|
|
|
|
|
|
$d = $data{$d}; |
|
106
|
|
|
|
|
|
|
$d->{exists} = 1; |
|
107
|
|
|
|
|
|
|
} |
|
108
|
|
|
|
|
|
|
} |
|
109
|
|
|
|
|
|
|
push @bench, [ $id, $text, $xs, \@ys ]; |
|
110
|
|
|
|
|
|
|
} |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
use FU::Util 'json_format', 'json_parse'; |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
sub defjson($name, $canon, $text, $val) { |
|
118
|
|
|
|
|
|
|
# Use similar options for fair comparisons. |
|
119
|
|
|
|
|
|
|
my $cp = Cpanel::JSON::XS->new->allow_nonref->unblessed_bool->convert_blessed; |
|
120
|
|
|
|
|
|
|
my $pp = JSON::PP->new->allow_nonref->core_bools->convert_blessed; |
|
121
|
|
|
|
|
|
|
my $xs = JSON::XS->new->allow_nonref->boolean_values([false,true])->convert_blessed; |
|
122
|
|
|
|
|
|
|
my $si = JSON::SIMD->new->allow_nonref->core_bools->convert_blessed; |
|
123
|
|
|
|
|
|
|
my $c_cp = Cpanel::JSON::XS->new->allow_nonref->unblessed_bool->convert_blessed->canonical; |
|
124
|
|
|
|
|
|
|
my $c_pp = JSON::PP->new->allow_nonref->core_bools->convert_blessed->canonical; |
|
125
|
|
|
|
|
|
|
my $c_xs = JSON::XS->new->allow_nonref->boolean_values([false,true])->convert_blessed->canonical; |
|
126
|
|
|
|
|
|
|
my $c_si = JSON::SIMD->new->allow_nonref->core_bools->convert_blessed->canonical; |
|
127
|
|
|
|
|
|
|
my $enc = json_format $val; |
|
128
|
|
|
|
|
|
|
def "json/$name", $text, [ 'Encode', $canon ? 'Canonical' : (), 'Decode' ], |
|
129
|
|
|
|
|
|
|
[ 'JSON::PP', undef, sub { $pp->encode($val) }, $canon ? sub { $c_pp->encode($val) } : (), sub { $pp->decode($enc) } ], |
|
130
|
|
|
|
|
|
|
[ 'JSON::Tiny', undef, sub { JSON::Tiny::to_json($val) }, $canon ? undef : (), sub { JSON::Tiny::from_json($enc) } ], |
|
131
|
|
|
|
|
|
|
[ 'Cpanel::JSON::XS', undef, sub { $cp->encode($val) }, $canon ? sub { $c_cp->encode($val) } : (), sub { $cp->decode($enc) } ], |
|
132
|
|
|
|
|
|
|
[ 'JSON::SIMD', undef, sub { $si->encode($val) }, $canon ? sub { $c_si->encode($val) } : (), sub { $si->decode($enc) } ], |
|
133
|
|
|
|
|
|
|
[ 'JSON::XS', undef, sub { $xs->encode($val) }, $canon ? sub { $c_xs->encode($val) } : (), sub { $xs->decode($enc) } ], |
|
134
|
|
|
|
|
|
|
[ 'FU::Util', 'FU', sub { json_format $val }, $canon ? sub { json_format $val, canonical => 1 } : (), sub { json_parse $enc } ]; |
|
135
|
|
|
|
|
|
|
} |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
# From JSON::XS POD. |
|
138
|
|
|
|
|
|
|
defjson api => 1, 'API object from L documentation.', |
|
139
|
|
|
|
|
|
|
[ map +{method => 'handleMessage', params => ['user1','we were just talking'], 'id' => undef, 'array' => [1,11,234,-5,1e5,1e7,1,0]}, 1..10 ]; |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
defjson objs => 1, 'Object (small)', [ map +{ map +("string$_", 1), 'a'..'f' }, 0..100 ]; |
|
142
|
|
|
|
|
|
|
defjson objl => 1, 'Object (large)', { map +("string$_-something", 1), 'aa'..'zz' }; |
|
143
|
|
|
|
|
|
|
defjson obju => 1, 'Object (large, mixed unicode)', { map +("str\x{1234}g$_-some\x{85232}hing", 1), 'aa'..'zz' }; |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
defjson ints => 0, 'Small integers', [ -5000..5000 ]; |
|
146
|
|
|
|
|
|
|
defjson intl => 0, 'Large integers', [ map { my $n=$_; map +($n+1<<$_), 10..60 } 1..10 ]; |
|
147
|
|
|
|
|
|
|
defjson strs => 0, 'ASCII strings', [ map +('hello, world', 'one more string', 'another string'), 1..100 ]; |
|
148
|
|
|
|
|
|
|
defjson stru => 0, 'Unicode strings', do { use utf8; |
|
149
|
|
|
|
|
|
|
[ map +('グリザイアの果実 -LE FRUIT DE LA GRISAIA-', '💩', 'Я люблю нічого не робити'), 1..50 ]; |
|
150
|
|
|
|
|
|
|
}; |
|
151
|
|
|
|
|
|
|
defjson stres => 0, 'String escaping (few)', [ map 'This string needs to "be escaped" a little bit', 1..100 ]; |
|
152
|
|
|
|
|
|
|
defjson strel => 0, 'String escaping (many)', [ map "This \" \\ needs \n\x41\x42\x43\x44 more", 1..100 ]; |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
package BENCH::TUWFXML { |
|
158
|
|
|
|
|
|
|
use TUWF::XML ':html5_', 'xml_string'; |
|
159
|
|
|
|
|
|
|
sub f($id) { |
|
160
|
|
|
|
|
|
|
li_ class => $id % 2 ? 'one' : undef, '+', $id % 5 > 2 ? 'two' : undef, sub { |
|
161
|
|
|
|
|
|
|
small_ '--'x($id % 50).' ' if $id % 3; |
|
162
|
|
|
|
|
|
|
a_ href => "/$id", |
|
163
|
|
|
|
|
|
|
class => $id % 7 > 2 ? 'another-class' : undef, |
|
164
|
|
|
|
|
|
|
'+' => $id % 9 < 7 ? 'and-another-one' : undef, |
|
165
|
|
|
|
|
|
|
style => "width: ${id}px", |
|
166
|
|
|
|
|
|
|
$id; |
|
167
|
|
|
|
|
|
|
}; |
|
168
|
|
|
|
|
|
|
} |
|
169
|
|
|
|
|
|
|
sub t { xml_string sub { div_ sub { f $_ for (1..100) } } } |
|
170
|
|
|
|
|
|
|
} |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
package BENCH::XMLWriter { |
|
173
|
|
|
|
|
|
|
my $wr; |
|
174
|
|
|
|
|
|
|
sub f($id) { |
|
175
|
|
|
|
|
|
|
$wr->startTag(li => class => join(' ', $id % 2 ? 'one' : (), $id % 5 > 2 ? 'two' : ())); |
|
176
|
|
|
|
|
|
|
$wr->dataElement(small => '--'x($id % 50).' ') if $id % 3; |
|
177
|
|
|
|
|
|
|
$wr->dataElement(a => $id, href => "/$id", class => join(' ', |
|
178
|
|
|
|
|
|
|
$id % 7 > 2 ? 'another-class' : (), |
|
179
|
|
|
|
|
|
|
$id % 9 < 7 ? 'and-another-one' : () |
|
180
|
|
|
|
|
|
|
), style => "width: ${id}px"); |
|
181
|
|
|
|
|
|
|
$wr->endTag(); |
|
182
|
|
|
|
|
|
|
} |
|
183
|
|
|
|
|
|
|
sub t { |
|
184
|
|
|
|
|
|
|
$wr = XML::Writer->new(OUTPUT => \my $str, UNSAFE => 1); |
|
185
|
|
|
|
|
|
|
$wr->startTag('div'); |
|
186
|
|
|
|
|
|
|
f $_ for (1..100); |
|
187
|
|
|
|
|
|
|
$wr->endTag(); |
|
188
|
|
|
|
|
|
|
} |
|
189
|
|
|
|
|
|
|
} |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
package BENCH::HTMLTiny { |
|
192
|
|
|
|
|
|
|
my $h; |
|
193
|
|
|
|
|
|
|
sub f($id) { |
|
194
|
|
|
|
|
|
|
$h->li({ class => join(' ', $id % 2 ? 'one' : (), '+', $id % 5 > 2 ? 'two' : ()) }, [ |
|
195
|
|
|
|
|
|
|
$id % 3 ? $h->small('--'x($id % 50).' ') : '', |
|
196
|
|
|
|
|
|
|
$h->a({ |
|
197
|
|
|
|
|
|
|
href => "/$id", |
|
198
|
|
|
|
|
|
|
class => join (' ', |
|
199
|
|
|
|
|
|
|
$id % 7 > 2 ? 'another-class' : (), |
|
200
|
|
|
|
|
|
|
$id % 9 < 7 ? 'and-another-one' : (), |
|
201
|
|
|
|
|
|
|
), |
|
202
|
|
|
|
|
|
|
style => "width: ${id}px" |
|
203
|
|
|
|
|
|
|
}, $id), |
|
204
|
|
|
|
|
|
|
]); |
|
205
|
|
|
|
|
|
|
} |
|
206
|
|
|
|
|
|
|
sub t { |
|
207
|
|
|
|
|
|
|
$h = HTML::Tiny->new; |
|
208
|
|
|
|
|
|
|
$h->div(map f($_), 1..100); |
|
209
|
|
|
|
|
|
|
} |
|
210
|
|
|
|
|
|
|
} |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
package BENCH::FUXMLWriter { |
|
213
|
|
|
|
|
|
|
use FU::XMLWriter ':html5_', 'fragment'; |
|
214
|
|
|
|
|
|
|
sub f($id) { |
|
215
|
|
|
|
|
|
|
li_ class => $id % 2 ? 'one' : undef, '+', $id % 5 > 2 ? 'two' : undef, sub { |
|
216
|
|
|
|
|
|
|
small_ '--'x($id % 50).' ' if $id % 3; |
|
217
|
|
|
|
|
|
|
a_ href => "/$id", |
|
218
|
|
|
|
|
|
|
class => $id % 7 > 2 ? 'another-class' : undef, |
|
219
|
|
|
|
|
|
|
'+' => $id % 9 < 7 ? 'and-another-one' : undef, |
|
220
|
|
|
|
|
|
|
style => "width: ${id}px", |
|
221
|
|
|
|
|
|
|
$id; |
|
222
|
|
|
|
|
|
|
}; |
|
223
|
|
|
|
|
|
|
} |
|
224
|
|
|
|
|
|
|
sub t { fragment { div_ sub { f $_ for (1..100) } } } |
|
225
|
|
|
|
|
|
|
} |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
def 'xml/a', 'HTML fragment', [ 'Rate' ], |
|
228
|
|
|
|
|
|
|
[ 'TUWF::XML', undef, \&BENCH::TUWFXML::t ], |
|
229
|
|
|
|
|
|
|
[ 'XML::Writer', undef, \&BENCH::XMLWriter::t ], |
|
230
|
|
|
|
|
|
|
[ 'HTML::Tiny', undef, \&BENCH::HTMLTiny::t ], |
|
231
|
|
|
|
|
|
|
[ 'FU::XMLWriter', 'FU', \&BENCH::FUXMLWriter::t ]; |
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
{ |
|
237
|
|
|
|
|
|
|
die "FU_TEST_DB not set.\n" if @exec && !$ENV{FU_TEST_DB}; |
|
238
|
|
|
|
|
|
|
my $pq = @exec && Pg::PQ::Conn->new($ENV{FU_TEST_DB}); |
|
239
|
|
|
|
|
|
|
my $fu = @exec && FU::Pg->connect($ENV{FU_TEST_DB}); |
|
240
|
|
|
|
|
|
|
# XXX: Doesn't support all connection params this way |
|
241
|
|
|
|
|
|
|
my $dbi = @exec && DBI->connect("dbi:Pg:dbname=".$pq->db, $pq->user, $pq->pass, {RaiseError => 1, PrintError => 0}); |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
my $small = 'SELECT x, x+1, x+2, x+3, x+4, x+5, x+6, x+7, x+8, x+9 FROM generate_series(-10000::smallint, 9999, 10) x(x)'; |
|
244
|
|
|
|
|
|
|
my $big = 'SELECT x<<5, x<<10, x<<15, x<<20, x<<25, x<<30, x<<35, x<<40, x<<45, x<<50 FROM generate_series(1::bigint, 20000, 1) x(x)'; |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
my sub dbi { my $sum = 0; my $st = $dbi->prepare_cached($_[0]); for my $row ($dbi->selectall_arrayref($st)->@*) { $sum ^= $_ for @$row; } } |
|
247
|
|
|
|
|
|
|
my sub pq { my $sum = 0; $pq->prepare('' => $_[0]); for my $row ($pq->execQueryPrepared('')->rows) { $sum ^= $_ for @$row; } } |
|
248
|
|
|
|
|
|
|
my sub fub { my $sum = 0; for my $row ($fu->sql($_[0])->alla->@*) { $sum ^= $_ for @$row; } } |
|
249
|
|
|
|
|
|
|
my sub fut { my $sum = 0; for my $row ($fu->sql($_[0])->text->alla->@*) { $sum ^= $_ for @$row; } } |
|
250
|
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
def 'pg/ints', 'Fetch and bitwise-or 20k integers', [ 'Smallint', 'Bigint' ], |
|
252
|
|
|
|
|
|
|
[ 'DBD::Pg', undef, sub { dbi($small) }, sub { dbi($big) } ], |
|
253
|
|
|
|
|
|
|
[ 'Pg::PQ', undef, sub { pq($small) }, sub { pq($big) } ], |
|
254
|
|
|
|
|
|
|
[ 'FU::Pg (bin)', 'FU', sub { fub($small) }, sub { fub($big) } ], |
|
255
|
|
|
|
|
|
|
[ 'FU::Pg (text)', 'FU', sub { fut($small) }, sub { fut($big) } ]; |
|
256
|
|
|
|
|
|
|
} |
|
257
|
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
delete @data{ grep !$data{$_}{exists}, keys %data }; |
|
263
|
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
sub runbench($sub) { |
|
266
|
|
|
|
|
|
|
my $o = timethis -1, $sub, 0, 'none'; |
|
267
|
|
|
|
|
|
|
printf "%.2f\n", $o->iters/$o->real; |
|
268
|
|
|
|
|
|
|
exit; |
|
269
|
|
|
|
|
|
|
} |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
sub execbench($d) { |
|
272
|
|
|
|
|
|
|
my $sum = 0; |
|
273
|
|
|
|
|
|
|
my $num = 1; |
|
274
|
|
|
|
|
|
|
local $| = 1; |
|
275
|
|
|
|
|
|
|
printf "%-20s%-12s%-20s", $d->{id}, $d->{x}, $d->{y}; |
|
276
|
|
|
|
|
|
|
for (1..$num) { |
|
277
|
|
|
|
|
|
|
open my $P, '-|', $^X, (map "-I$_", @INC), $0, 'exec', $d->{id}, $d->{x}, $d->{y}; |
|
278
|
|
|
|
|
|
|
chomp(my $rate = <$P>); |
|
279
|
|
|
|
|
|
|
printf "%10d", $rate; |
|
280
|
|
|
|
|
|
|
$sum += $rate; |
|
281
|
|
|
|
|
|
|
} |
|
282
|
|
|
|
|
|
|
printf " ->%10d\n", $sum/$num; |
|
283
|
|
|
|
|
|
|
$d->{rate} = sprintf '%.0f', $sum/$num; |
|
284
|
|
|
|
|
|
|
} |
|
285
|
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
for my $b (@bench) { |
|
287
|
|
|
|
|
|
|
my ($id, $text, $xs, $ys) = @$b; |
|
288
|
|
|
|
|
|
|
for my ($ya) (@$ys) { |
|
289
|
|
|
|
|
|
|
my($y, $m, @sub) = @$ya; |
|
290
|
|
|
|
|
|
|
$m ||= $y; |
|
291
|
|
|
|
|
|
|
for my($i, $x) (builtin::indexed @$xs) { |
|
292
|
|
|
|
|
|
|
next if !$sub[$i]; |
|
293
|
|
|
|
|
|
|
if (@exec) { |
|
294
|
|
|
|
|
|
|
runbench $sub[$i] if $exec[0] eq $id && $exec[1] eq $x && $exec[2] eq $y; |
|
295
|
|
|
|
|
|
|
} else { |
|
296
|
|
|
|
|
|
|
my $d = $data{"$id $x $y"}; |
|
297
|
|
|
|
|
|
|
execbench $d if !$oldmodules{$m} || $modules{$m} ne $oldmodules{$m} |
|
298
|
|
|
|
|
|
|
|| (@run && $id =~ /$run[0]/ && $x =~ /$run[1]/ && $y =~ /$run[2]/); |
|
299
|
|
|
|
|
|
|
} |
|
300
|
|
|
|
|
|
|
} |
|
301
|
|
|
|
|
|
|
} |
|
302
|
|
|
|
|
|
|
} |
|
303
|
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
die if @exec; |
|
305
|
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
# s/^=/%/ to prevent tools from interpreting the below as POD |
|
308
|
|
|
|
|
|
|
__DATA__ |