This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Porting/bench.pl: trivial var changes
[perl5.git] / t / porting / bench.t
CommitLineData
6568b26d
DM
1#!/usr/bin/perl
2
3# Check the functionality of the Porting/bench.pl executable;
4# in particular, its argument handling and its ability to produce
5# the expected output for particular arguments.
6#
7# See also t/porting/bench_selftest.pl
8
9use warnings;
10use strict;
11
12BEGIN {
13 chdir '..' if -f 'test.pl' && -f 'thread_it.pl';
14 require './t/test.pl';
15}
16
17# Only test on git checkouts - this is more of a perl core developer
18# tool than an end-user tool.
19# Only test on a platform likely to support forking, pipes, cachegrind
20# etc. Add other platforms if you think they're safe.
21
22skip_all "not devel" unless -d ".git";
23skip_all "not linux" unless $^O eq 'linux';
24skip_all "no valgrind" unless -x '/bin/valgrind' || -x '/usr/bin/valgrind';
25
26
27my $bench_pl = "Porting/bench.pl";
28
29ok -e $bench_pl, "$bench_pl exists and is executable";
30
31my $bench_cmd = "$^X -Ilib $bench_pl";
32
33my $out;
34
35# Read in the expected output format templates and create qr//s from them.
36
37my %formats;
38my %format_qrs;
39
40{
41 my $cur;
42 while (<DATA>) {
43 next if /^#/;
44 if (/^FORMAT:/) {
45 die "invalid format line: $_" unless /^FORMAT:\s+(\w+)\s*$/;
46 $cur = $1;
47 die "duplicate format: '$cur'\n" if exists $formats{$cur};
48 next;
49 }
50 $formats{$cur} .= $_;
51 }
52
53 for my $name (sort keys %formats) {
54 my $f = $formats{$name};
55
56 # expand "%%SUB_FORMAT%%
57 $f =~ s{^ \s* %% (\w+) %% [ \t]* \n}
58 {
59 my $f1 = $formats{$1};
60 die "No such sub-format '%%$1%%' in format '$name'\n"
61 unless defined $f1;
62 $f1;
63 }gmxe;
64
65 $f = quotemeta $f;
66
67 # convert NNNN.NN placeholders into a regex
68 $f =~ s{(N+)\\.(N+)}
69 {
70 "("
71 . "\\s*-?\\d+\."
72 . "\\d" x length($2)
99b1e78b 73 ."|\\s+-)"
6568b26d
DM
74 }ge;
75 $format_qrs{$name} = qr/\A$f\z/;
76 }
77}
78
79
1137c9fa
DM
80# ---------------------------------------------------
81# check croaks
82
83for my $test (
84 [
85 "--boz",
86 "Unknown option: boz\nUse the -h option for usage information.\n",
87 "croak: basic unknown option"
88 ],
89 [
90 "--fields=Ir,Boz",
91 "Error: --fields: unknown field 'Boz'\n",
92 "croak: unknown --field"
93 ],
94 [
95 "--action=boz",
96 "Error: unrecognised action 'boz'\nmust be one of: grind, selftest\n",
97 "croak: unknown --action"
98 ],
99 [
100 "--sort=boz",
101 "Error: --sort argument should be of the form field:perl: 'boz'\n",
102 "croak: invalid --sort"
103 ],
104 [
105 "--sort=boz:perl",
106 "Error: --sort: unknown field 'boz'\n",
107 "croak: unknown --sort field"
108 ],
109 [
110 "-action=selftest perl",
111 "Error: no perl executables may be specified with selftest\n",
112 "croak: --action-selftest with executable"
113 ],
114 [
115 "--tests=/boz perl",
116 "Error: --tests regex must be of the form /.../\n",
117 "croak: invalid --tests regex"
118 ],
119 [
120 "--tests=call::sub::empty,foo::bar::baz::boz perl",
121 "Error: no such test found: 'foo::bar::baz::boz'\n",
122 "croak: unknown test in --tests"
123 ],
124 [
125 "--tests=/foo::bar::baz::boz/ perl",
126 "Error: no tests to run\n",
127 "croak: no --tests to run "
128 ],
129 [
130 "--benchfile=no-such-file-boz perl",
131 qr/\AError: can't read 'no-such-file-boz':/,
132 "croak: non-existent --benchfile "
133 ],
134 [
135 "--benchfile=t/porting/bench/synerr perl",
136 qr{\AError: can't parse 't/porting/bench/synerr':\nsyntax error},
137 "croak: --benchfile with syntax error"
138 ],
139 [
140 "--benchfile=t/porting/bench/ret0 perl",
141 "Error: can't load 't/porting/bench/ret0': code didn't return a true value\n",
142 "croak: --benchfile which returns 0"
143 ],
144 [
145 "--norm=2 ./miniperl ./perl",
146 "Error: --norm value 2 outside range 0..1\n",
147 "croak: select-a-perl out of range"
148 ],
149 [
150 "--sort=Ir:myperl ./miniperl ./perl",
151 "Error: --sort: unrecognised perl 'myperl'\n",
152 "croak: select-a-perl unrecognised"
153 ],
154 [
155 "--compact=./perl ./perl=A ./perl=B",
156 "Error: --compact: ambiguous perl './perl'\n",
157 "croak: select-a-perl ambiguous"
158 ],
159 [
99b1e78b
DM
160 "./perl --foo",
161 "Error: unrecognised executable switch '--foo'\n",
162 "croak: ./perl --foo"
163 ],
164 [
165 "-- --args=foo",
166 "Error: --args without a preceding executable name\n",
167 "croak: --args without perl"
168 ],
169 [
170 "-- --env=foo=bar",
171 "Error: --env without a preceding executable name\n",
172 "croak: --env without perl"
173 ],
174 [
175 "./perl --args",
176 "Error: --args is missing value\n",
177 "croak: --args without value"
178 ],
179 [
180 "./perl --env",
181 "Error: --env is missing value\n",
182 "croak: --env without value"
183 ],
184 [
185 "./perl --env='FOO'",
186 "Error: --env is missing =value\n",
187 "croak: --env without =value"
188 ],
189 [
190 "./perl ./perl",
81cb9d79
DM
191 "Error: duplicate label './perl': each executable must have a unique label\n",
192 "croak: duplicate label ./perl ./perl"
193 ],
194 [
99b1e78b 195 "./perl=A ./miniperl=A",
81cb9d79
DM
196 "Error: duplicate label 'A': each executable must have a unique label\n",
197 "croak: duplicate label =A =A"
198 ],
199 [
200 "--read=t/porting/bench/callsub.json --read=t/porting/bench/callsub.json",
201 "Error: duplicate label './perl': seen in file 't/porting/bench/callsub.json'\n",
202 "croak: duplicate label --read=... --read=..."
203 ],
204 [
205 "--read=t/porting/bench/callsub.json ./perl",
206 "Error: duplicate label './perl': seen both in --read file and on command line\n",
207 "croak: duplicate label --read=... ./perl"
1137c9fa
DM
208 ],
209 [
99b1e78b
DM
210 "./nosuch-perl",
211 qr{^\QError: unable to execute './nosuch-perl': },
212 "croak: no such perl"
213 ],
214 [
1137c9fa
DM
215 "--grindargs=Boz --tests=call::sub::empty ./perl=A ./perl=B",
216 qr{Error: while executing call::sub::empty/A empty/short loop:\nunexpected code or cachegrind output:\n},
217 "croak: cachegrind output format "
218 ],
219 [
220 "--bisect=Ir",,
221 "Error: --bisect option must be of form 'field,integer,integer'\n",
222 "croak: --bisect=Ir"
223 ],
224 [
225 "--bisect=Ir,1",,
226 "Error: --bisect option must be of form 'field,integer,integer'\n",
227 "croak: --bisect=Ir,1"
228 ],
229 [
230 "--bisect=Ir,1,2,3",
231 "Error: --bisect option must be of form 'field,integer,integer'\n",
232 "croak: --bisect=Ir,1,2,3"
233 ],
234 [
235 "--bisect=Ir,1,x",
236 "Error: --bisect option must be of form 'field,integer,integer'\n",
237 "croak: --bisect=Ir,1,x"
238 ],
239 [
240 "--bisect=Ir,x,2",
241 "Error: --bisect option must be of form 'field,integer,integer'\n",
242 "croak: --bisect=Ir,x,2"
243 ],
244 [
245 "--bisect=boz,1,2",
246 "Error: unrecognised field 'boz' in --bisect option\n",
247 "croak: --bisect=boz,1,2"
248 ],
249 [
250 "--bisect=Ir,2,1",
251 "Error: --bisect min (2) must be <= max (1)\n",
252 "croak: --bisect=boz,2,1"
253 ],
254 [
255 "--read=no-such-file-boz",
256 qr/\AError: can't open 'no-such-file-boz' for reading:/,
257 "croak: non-existent --read file "
258 ],
259 [
260 "--read=t/porting/bench/badversion.json",
261 "Error: unsupported version 9999.9 in file 't/porting/bench/badversion.json' (too new)\n",
262 "croak: --read version"
263 ],
4533e88f
DM
264 [
265 "--read=t/porting/bench/callsub.json --benchfile=t/perf/benchmarks ./perl ",
266 "Error: --benchfile cannot be used when --read is present\n",
267 "croak: benchfile with read"
268 ],
1137c9fa
DM
269 [
270 "",
271 "Error: nothing to do: no perls to run, no data to read.\n",
272 "croak: no input"
273 ],
274 [
275 "./perl",
276 "Error: need at least 2 perls for comparison.\n",
277 "croak: need 2 perls"
278 ],
279 [
280 "--bisect=Ir,1,2 ./perl=A ./perl=B",
281 "Error: exactly one perl executable must be specified for bisect\n",
282 "croak: --bisect, need 1 perls"
283 ],
284 [
285 "--bisect=Ir,1,2 --tests=/call/ ./perl=A",
286 "Error: only a single test may be specified with --bisect\n",
287 "croak: --bisect one test only"
288 ],
289 # note that callsub.json was created using
290 # ./perl -Ilib Porting/bench.pl --tests='/call::sub::(amp_)?empty/' \
291 # --write=t/porting/bench/callsub.json ./perl
1137c9fa
DM
292 [
293 "--read=t/porting/bench/callsub.json --write=no/such/file/boz",
294 qr{\AError: can't open 'no/such/file/boz' for writing: },
295 "croak: --write open error"
296 ],
68de41bc
DM
297 # note that callsub2.json was created using
298 # ./perl -Ilib Porting/bench.pl \
299 # --tests='call::sub::empty,call::sub::args3' \
81cb9d79 300 # --write=t/porting/bench/callsub2.json ./perl=perl2
68de41bc
DM
301 [
302 "--read=t/porting/bench/callsub.json "
303 . " --read=t/porting/bench/callsub2.json",
304 "Can't merge multiple read files: they contain differing test sets.\n"
305 . "Re-run with --verbose to see the differences.\n",
306 "croak: --read callsub, callsub2"
307 ],
308 [
309 "--read=t/porting/bench/callsub.json "
310 . " --read=t/porting/bench/callsub2.json"
311 . " --verbose",
312 "Can't merge multiple read files: they contain differing test sets.\n"
313 . "Previous tests:\n"
314 . " call::sub::amp_empty\n"
315 . " call::sub::empty\n"
316 . "tests from 't/porting/bench/callsub2.json':\n"
317 . " call::sub::args3\n"
318 . " call::sub::empty\n",
319 "croak: --read callsub, callsub2 --verbose"
320 ],
1137c9fa 321
68de41bc 322 # these ones aren't tested (and nor are any "Panic:" ones):
1137c9fa
DM
323
324 # Error: can't parse '$field' field from cachegrind output
325 # Error: while starting cachegrind subprocess for NNNN:
68de41bc
DM
326 # File '$file' contains no results
327 # File '$file' contains differing test and results names
328 # File '$file' contains differing test and sort order names
329 # Can't merge multiple read files: differing loop counts:
1137c9fa
DM
330)
331{
332 my ($args, $expected, $desc) = @$test;
333 $out = qx($bench_cmd $args 2>&1);
334 if (ref($expected)) {
335 like $out, $expected, $desc;
336 }
337 else {
338 is $out, $expected, $desc;
339 }
340}
341
342# ---------------------------------------------------
343# run benchmarks
344
345
6568b26d
DM
346my $resultfile1 = tempfile(); # benchmark results for 1 perl
347my $resultfile2 = tempfile(); # benchmark results for 2 perls
348
349# Run a real cachegrind session and write results to file.
350# the -j 2 is to minimally exercise its parallel facility.
351
352note("running cachegrind for 1st perl; may be slow...");
353$out = qx($bench_cmd -j 2 --write=$resultfile1 --tests=call::sub::empty $^X=p0 2>&1);
354is length($out), 0, "--write should produce no output (1 perl)";
355ok -s $resultfile1, "--write should create a non-empty results file (1 perl)";
356
357# and again with 2 perls. This is also tests the 'mix read and new new
358# perls' functionality.
359
360note("running cachegrind for 2nd perl; may be slow...");
361$out = qx($bench_cmd -j 2 --read=$resultfile1 --write=$resultfile2 $^X=p1 2>&1);
d9b91f79
DM
362is length($out), 0, "--write should produce no output (2 perls)"
363 or diag("got: $out");
6568b26d
DM
364ok -s $resultfile2, "--write should create a non-empty results file (2 perls)";
365
366# 1 perl:
367
368# read back the results in raw form
369
370$out = qx($bench_cmd --read=$resultfile1 --raw 2>&1);
371like $out, $format_qrs{raw1}, "basic cachegrind raw format; 1 perl";
372
373# and read back the results in raw compact form
374
375$out = qx($bench_cmd --read=$resultfile1 --raw --compact=0 2>&1);
376like $out, $format_qrs{raw_compact}, "basic cachegrind raw compact format; 1 perl";
377
378# 2 perls:
379
380# read back the results in relative-percent form
381
382$out = qx($bench_cmd --read=$resultfile2 2>&1);
383like $out, $format_qrs{percent2}, "basic cachegrind percent format; 2 perls";
384
385# and read back the results in raw form
386
387$out = qx($bench_cmd --read=$resultfile2 --raw 2>&1);
388like $out, $format_qrs{raw2}, "basic cachegrind raw format; 2 perls";
389
390# and read back the results in compact form
391
392$out = qx($bench_cmd --read=$resultfile2 --compact=1 2>&1);
393like $out, $format_qrs{compact}, "basic cachegrind compact format; 2 perls";
394
395
d9b91f79
DM
396# bisect
397
d9b91f79 398# the Ir range here is intended such that the bisect will always fail
30792d9e 399$out = qx($bench_cmd --read=t/porting/bench/callsub.json --tests=call::sub::empty --bisect=Ir,100000,100001 2>&1);
d9b91f79
DM
400
401is $?, 1 << 8, "--bisect should not match";
402is length($out), 0, "--bisect should produce no output"
403 or diag("got: $out");
6568b26d 404
68de41bc
DM
405# multiple reads with differing test sets but common --tests subset
406
407$out = qx($bench_cmd --read=t/porting/bench/callsub.json --read=t/porting/bench/callsub2.json --tests=call::sub::empty 2>&1);
81cb9d79 408$out =~ s{\Q./perl perl2}{ p0 p1};
68de41bc 409$out =~ s{^\./perl}{p0}m;
68de41bc
DM
410like $out, $format_qrs{percent2}, "2 reads; overlapping test sets";
411
412# A read defines what benchmarks to run
413
414note("running cachegrind on 1 perl; may be slow...");
415$out = qx($bench_cmd --read=t/porting/bench/callsub.json --tests=call::sub::empty $^X=p1 2>&1);
416$out =~ s{^\./perl}{p0}m;
417$out =~ s{\Q./perl}{ p0};
418like $out, $format_qrs{percent2}, "1 read; 1 generate";
419
99b1e78b
DM
420# Process environment and optional args.
421# This is a minimal test that it runs - it doesn't test whether
422# the environment and args are getting applied correctly, apart from the
423# fact that the perls in question are being successfully executed.
424
425note("running cachegrind on 2 perls; may be slow...");
426$out = qx($bench_cmd --tests=call::sub::empty --perlargs=-Ilib $^X=p0 --args='-Ifoo/bar -Mstrict' --env='FOO=foo' $^X=p1 --args='-Ifoo/bar' --env='BAR=bar' --env='BAZ=baz' 2>&1);
427like $out, $format_qrs{percent2}, "2 perls with args and env";
428
68de41bc 429
1137c9fa 430
6568b26d
DM
431done_testing();
432
433
434# Templates for expected output formats.
435#
436# Lines starting with '#' are skipped.
437# Lines of the form 'FORMAT: foo' start and name a new template
438# All other lines are part of the template
439# Entries of the form NNNN.NN are converted into a regex of the form
440# ( \s* -? \d+\.\d\d | - )
441# i.e. it expects number with a fixed number of digits after the point,
442# or a '-'.
443# Lines of the form %%FOO%% are substituted with format 'FOO'
444
445
446__END__
447# ===================================================================
448FORMAT: STD_HEADER
449Key:
450 Ir Instruction read
451 Dr Data read
452 Dw Data write
453 COND conditional branches
454 IND indirect branches
455 _m branch predict miss
456 _m1 level 1 cache miss
457 _mm last cache (e.g. L3) miss
458 - indeterminate percentage (e.g. 1/0)
459# ===================================================================
460FORMAT: percent2
461%%STD_HEADER%%
462
463The numbers represent relative counts per loop iteration, compared to
464p0 at 100.0%.
465Higher is better: for example, using half as many instructions gives 200%,
466while using twice as many gives 50%.
467
468call::sub::empty
469function call with no args or body
470
471 p0 p1
472 ------ ------
473 Ir 100.00 NNN.NN
474 Dr 100.00 NNN.NN
475 Dw 100.00 NNN.NN
476 COND 100.00 NNN.NN
477 IND 100.00 NNN.NN
478
479COND_m 100.00 NNN.NN
480 IND_m 100.00 NNN.NN
481
482 Ir_m1 100.00 NNN.NN
483 Dr_m1 100.00 NNN.NN
484 Dw_m1 100.00 NNN.NN
485
486 Ir_mm 100.00 NNN.NN
487 Dr_mm 100.00 NNN.NN
488 Dw_mm 100.00 NNN.NN
489# ===================================================================
490FORMAT: raw1
491%%STD_HEADER%%
492
493The numbers represent raw counts per loop iteration.
494
495call::sub::empty
496function call with no args or body
497
498 p0
499 --------
500 Ir NNNNNN.N
501 Dr NNNNNN.N
502 Dw NNNNNN.N
503 COND NNNNNN.N
504 IND NNNNNN.N
505
506COND_m NNNNNN.N
507 IND_m NNNNNN.N
508
509 Ir_m1 NNNNNN.N
510 Dr_m1 NNNNNN.N
511 Dw_m1 NNNNNN.N
512
513 Ir_mm NNNNNN.N
514 Dr_mm NNNNNN.N
515 Dw_mm NNNNNN.N
516# ===================================================================
517FORMAT: raw2
518%%STD_HEADER%%
519
520The numbers represent raw counts per loop iteration.
521
522call::sub::empty
523function call with no args or body
524
525 p0 p1
526 -------- --------
527 Ir NNNNNN.N NNNNNN.N
528 Dr NNNNNN.N NNNNNN.N
529 Dw NNNNNN.N NNNNNN.N
530 COND NNNNNN.N NNNNNN.N
531 IND NNNNNN.N NNNNNN.N
532
533COND_m NNNNNN.N NNNNNN.N
534 IND_m NNNNNN.N NNNNNN.N
535
536 Ir_m1 NNNNNN.N NNNNNN.N
537 Dr_m1 NNNNNN.N NNNNNN.N
538 Dw_m1 NNNNNN.N NNNNNN.N
539
540 Ir_mm NNNNNN.N NNNNNN.N
541 Dr_mm NNNNNN.N NNNNNN.N
542 Dw_mm NNNNNN.N NNNNNN.N
543# ===================================================================
544FORMAT: compact
545%%STD_HEADER%%
546
547The numbers represent relative counts per loop iteration, compared to
548p0 at 100.0%.
549Higher is better: for example, using half as many instructions gives 200%,
550while using twice as many gives 50%.
551
552Results for p1
553
554 Ir Dr Dw COND IND COND_m IND_m Ir_m1 Dr_m1 Dw_m1 Ir_mm Dr_mm Dw_mm
555 ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------
556 NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN call::sub::empty
557# ===================================================================
558FORMAT: raw_compact
559%%STD_HEADER%%
560
561The numbers represent raw counts per loop iteration.
562
563Results for p0
564
565 Ir Dr Dw COND IND COND_m IND_m Ir_m1 Dr_m1 Dw_m1 Ir_mm Dr_mm Dw_mm
566 ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------
567 NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N call::sub::empty
568# ===================================================================