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.
7 # See also t/porting/bench_selftest.pl
10 chdir '..' if -f 'test.pl';
12 require './t/test.pl';
20 # Only test on git checkouts - this is more of a perl core developer
21 # tool than an end-user tool.
22 # Only test on a platform likely to support forking, pipes, cachegrind
23 # etc. Add other platforms if you think they're safe.
25 skip_all "not devel" unless -d "./.git";
26 skip_all "not linux" unless $^O eq 'linux';
27 skip_all "no valgrind" unless -x '/bin/valgrind' || -x '/usr/bin/valgrind';
28 # Address sanitizer clashes horribly with cachegrind
29 skip_all "not with ASAN" if $Config{ccflags} =~ /sanitize=address/;
30 skip_all "cachegrind broken" if system "( ulimit -c 0; valgrind -q --tool=cachegrind --cachegrind-out-file=/dev/null $^X -e0 ) 2>/dev/null";
33 my $bench_pl = "Porting/bench.pl";
35 ok -e $bench_pl, "$bench_pl exists and is executable";
37 my $bench_cmd = "$^X -Ilib $bench_pl";
41 # Read in the expected output format templates and create qr//s from them.
51 die "invalid format line: $_" unless /^FORMAT:\s+(\w+)\s*$/;
53 die "duplicate format: '$cur'\n" if exists $formats{$cur};
59 for my $name (sort keys %formats) {
60 my $f = $formats{$name};
62 # expand "%%SUB_FORMAT%%
63 $f =~ s{^ \s* %% (\w+) %% [ \t]* \n}
65 my $f1 = $formats{$1};
66 die "No such sub-format '%%$1%%' in format '$name'\n"
73 # convert NNNN.NN placeholders into a regex
83 # convert run of space chars into ' +' or ' *'
85 $f =~ s/(\A|\n)(\\ )+/$1 */g;
88 # convert '---' placeholders into a regex
89 $f =~ s/(\\-){2,}/-+/g;
91 $format_qrs{$name} = qr/\A$f\z/;
96 # ---------------------------------------------------
102 "Unknown option: boz\nUse the -h option for usage information.\n",
103 "croak: basic unknown option"
107 "Error: --fields: unknown field 'Boz'\n",
108 "croak: unknown --field"
112 "Error: unrecognised action 'boz'\nmust be one of: grind, selftest\n",
113 "croak: unknown --action"
117 "Error: --sort argument should be of the form field:perl: 'boz'\n",
118 "croak: invalid --sort"
122 "Error: --sort: unknown field 'boz'\n",
123 "croak: unknown --sort field"
126 "-action=selftest perl",
127 "Error: no perl executables may be specified with selftest\n",
128 "croak: --action-selftest with executable"
132 "Error: --tests regex must be of the form /.../\n",
133 "croak: invalid --tests regex"
136 "--tests=call::sub::empty,foo::bar::baz::boz perl",
137 "Error: no such test found: 'foo::bar::baz::boz'\n"
138 . "Re-run with --verbose for a list of valid tests.\n",
139 "croak: unknown test in --tests"
142 "--verbose --tests=call::sub::empty,foo::bar::baz::boz --read=t/porting/bench/callsub.json",
143 "Error: no such test found: 'foo::bar::baz::boz'\n"
144 . "Valid test names are:\n"
145 . " call::sub::amp_empty\n"
146 . " call::sub::empty\n",
147 "croak: unknown test in --tests --verbose"
150 "--tests=/foo::bar::baz::boz/ perl",
151 "Error: no tests to run\n",
152 "croak: no --tests to run "
155 "--benchfile=no-such-file-boz perl",
156 qr/\AError: can't read 'no-such-file-boz':/,
157 "croak: non-existent --benchfile "
160 "--benchfile=t/porting/bench/synerr perl",
161 qr{\AError: can't parse 't/porting/bench/synerr':\nsyntax error},
162 "croak: --benchfile with syntax error"
165 "--benchfile=t/porting/bench/ret0 perl",
166 "Error: can't load 't/porting/bench/ret0': code didn't return a true value\n",
167 "croak: --benchfile which returns 0"
170 "--benchfile=t/porting/bench/oddentry perl",
171 qr{\AError: 't/porting/bench/oddentry' does not contain evenly paired test names and hashes\n},
172 "croak: --benchfile with odd number of entries"
175 "--benchfile=t/porting/bench/badname perl",
176 qr{\AError: 't/porting/bench/badname': invalid test name: '1='\n},
177 "croak: --benchfile with invalid test name"
180 "--benchfile=t/porting/bench/badhash perl",
181 qr{\AError: 't/porting/bench/badhash': invalid key 'blah' for test 'foo::bar'\n},
182 "croak: --benchfile with invalid test hash key"
185 "--norm=2 ./miniperl ./perl",
186 "Error: --norm value 2 outside range 0..1\n",
187 "croak: select-a-perl out of range"
190 "--norm=-0 ./miniperl ./perl",
191 "Error: --norm value -0 outside range -1..-2\n",
192 "croak: select-a-perl out of range"
195 "--norm=-3 ./miniperl ./perl",
196 "Error: --norm value -3 outside range -1..-2\n",
197 "croak: select-a-perl out of range"
200 "--sort=Ir:myperl ./miniperl ./perl",
201 "Error: --sort: unrecognised perl 'myperl'\n"
202 . "Valid perl names are:\n"
205 "croak: select-a-perl unrecognised"
208 "--compact=./perl ./perl=A ./perl=B",
209 "Error: --compact: ambiguous perl './perl'\n",
210 "croak: select-a-perl ambiguous"
214 "Error: unrecognised executable switch '--foo'\n",
215 "croak: ./perl --foo"
219 "Error: --args without a preceding executable name\n",
220 "croak: --args without perl"
224 "Error: --env without a preceding executable name\n",
225 "croak: --env without perl"
229 "Error: --args is missing value\n",
230 "croak: --args without value"
234 "Error: --env is missing value\n",
235 "croak: --env without value"
238 "./perl --env='FOO'",
239 "Error: --env is missing =value\n",
240 "croak: --env without =value"
244 "Error: duplicate label './perl': each executable must have a unique label\n",
245 "croak: duplicate label ./perl ./perl"
248 "./perl=A ./miniperl=A",
249 "Error: duplicate label 'A': each executable must have a unique label\n",
250 "croak: duplicate label =A =A"
253 "--read=t/porting/bench/callsub.json --read=t/porting/bench/callsub.json",
254 "Error: duplicate label './perl': seen in file 't/porting/bench/callsub.json'\n",
255 "croak: duplicate label --read=... --read=..."
258 "--read=t/porting/bench/callsub.json ./perl",
259 "Error: duplicate label './perl': seen both in --read file and on command line\n",
260 "croak: duplicate label --read=... ./perl"
264 qr{^\QError: unable to execute './nosuch-perl': },
265 "croak: no such perl"
268 "--grindargs=Boz --debug --tests=call::sub::empty ./perl=A ./perl=B",
269 qr{Error: .*?(unexpected code or cachegrind output|gave return status)}s,
270 "croak: cachegrind output format "
274 "Error: --bisect option must be of form 'field,integer,integer'\n",
279 "Error: --bisect option must be of form 'field,integer,integer'\n",
280 "croak: --bisect=Ir,1"
284 "Error: --bisect option must be of form 'field,integer,integer'\n",
285 "croak: --bisect=Ir,1,2,3"
289 "Error: --bisect option must be of form 'field,integer,integer'\n",
290 "croak: --bisect=Ir,1,x"
294 "Error: --bisect option must be of form 'field,integer,integer'\n",
295 "croak: --bisect=Ir,x,2"
299 "Error: unrecognised field 'boz' in --bisect option\n",
300 "croak: --bisect=boz,1,2"
304 "Error: --bisect min (2) must be <= max (1)\n",
305 "croak: --bisect=boz,2,1"
308 "--read=no-such-file-boz",
309 qr/\AError: can't open 'no-such-file-boz' for reading:/,
310 "croak: non-existent --read file "
313 "--read=t/porting/bench/badversion.json",
314 "Error: unsupported version 9999.9 in file 't/porting/bench/badversion.json' (too new)\n",
315 "croak: --read version"
318 "--read=t/porting/bench/callsub.json --benchfile=t/perf/benchmarks ./perl ",
319 "Error: --benchfile cannot be used when --read is present\n",
320 "croak: benchfile with read"
324 "Error: nothing to do: no perls to run, no data to read.\n",
329 "Error: need at least 2 perls for comparison.\n",
330 "croak: need 2 perls"
333 "--bisect=Ir,1,2 ./perl=A ./perl=B",
334 "Error: exactly one perl executable must be specified for bisect\n",
335 "croak: --bisect, need 1 perls"
338 "--bisect=Ir,1,2 --tests=/call/ ./perl=A",
339 "Error: only a single test may be specified with --bisect\n",
340 "croak: --bisect one test only"
342 # note that callsub.json was created using
343 # ./perl -Ilib Porting/bench.pl --tests='/call::sub::(amp_)?empty/' \
344 # --write=t/porting/bench/callsub.json ./perl
346 "--read=t/porting/bench/callsub.json --write=no/such/file/boz",
347 qr{\AError: can't open 'no/such/file/boz' for writing: },
348 "croak: --write open error"
350 # note that callsub2.json was created using
351 # ./perl -Ilib Porting/bench.pl \
352 # --tests='call::sub::empty,call::sub::args3' \
353 # --write=t/porting/bench/callsub2.json ./perl=perl2
355 "--read=t/porting/bench/callsub.json "
356 . " --read=t/porting/bench/callsub2.json",
357 "Can't merge multiple read files: they contain differing test sets.\n"
358 . "Re-run with --verbose to see the differences.\n",
359 "croak: --read callsub, callsub2"
362 "--read=t/porting/bench/callsub.json "
363 . " --read=t/porting/bench/callsub2.json"
365 "Can't merge multiple read files: they contain differing test sets.\n"
366 . "Previous tests:\n"
367 . " call::sub::amp_empty\n"
368 . " call::sub::empty\n"
369 . "tests from 't/porting/bench/callsub2.json':\n"
370 . " call::sub::args3\n"
371 . " call::sub::empty\n",
372 "croak: --read callsub, callsub2 --verbose"
375 # these ones aren't tested (and nor are any "Panic:" ones):
377 # Error: can't parse '$field' field from cachegrind output
378 # Error: while starting cachegrind subprocess for NNNN:
379 # File '$file' contains no results
380 # File '$file' contains differing test and results names
381 # File '$file' contains differing test and sort order names
382 # Can't merge multiple read files: differing loop counts:
385 my ($args, $expected, $desc) = @$test;
386 $out = qx($bench_cmd $args 2>&1);
387 if (ref($expected)) {
388 like $out, $expected, $desc;
391 is $out, $expected, $desc;
395 # ---------------------------------------------------
399 my $resultfile1 = tempfile(); # benchmark results for 1 perl
400 my $resultfile2 = tempfile(); # benchmark results for 2 perls
402 # Run a real cachegrind session and write results to file.
403 # the -j 2 is to minimally exercise its parallel facility.
405 note("running cachegrind for 1st perl; may be slow...");
406 $out = qx($bench_cmd -j 2 --write=$resultfile1 --tests=call::sub::empty $^X=p0 2>&1);
407 is $out, "", "--write should produce no output (1 perl)";
408 ok -s $resultfile1, "--write should create a non-empty results file (1 perl)";
410 # and again with 2 perls. This is also tests the 'mix read and new new
411 # perls' functionality.
413 note("running cachegrind for 2nd perl; may be slow...");
414 $out = qx($bench_cmd -j 2 --read=$resultfile1 --write=$resultfile2 $^X=p1 2>&1);
415 is $out, "", "--write should produce no output (2 perls)"
416 or diag("got: $out");
417 ok -s $resultfile2, "--write should create a non-empty results file (2 perls)";
421 # read back the results in raw form
423 $out = qx($bench_cmd --read=$resultfile1 --raw 2>&1);
424 like $out, $format_qrs{raw1}, "basic cachegrind raw format; 1 perl";
426 # and read back the results in raw compact form
428 $out = qx($bench_cmd --read=$resultfile1 --raw --compact=0 2>&1);
429 like $out, $format_qrs{raw_compact}, "basic cachegrind raw compact format; 1 perl";
431 # and read back the results in raw average form
433 $out = qx($bench_cmd --read=$resultfile1 --raw --average 2>&1);
434 like $out, $format_qrs{raw_average1}, "basic cachegrind raw average format; 1 perl";
436 # and read back the results with raw selected fields
438 $out = qx($bench_cmd --read=$resultfile1 --raw --fields=Ir,Dr 2>&1);
439 like $out, $format_qrs{fields1}, "basic cachegrind --fields; 1 perl";
443 # read back the results in relative-percent form
445 $out = qx($bench_cmd --read=$resultfile2 2>&1);
446 like $out, $format_qrs{percent2}, "basic cachegrind percent format; 2 perls";
448 # read back the results in relative-percent form with norm
450 $out = qx($bench_cmd --read=$resultfile2 --norm=0 2>&1);
451 like $out, $format_qrs{percent2}, "basic cachegrind percent format, norm; 2 perls";
453 # ditto with negative norm
455 $out = qx($bench_cmd --read=$resultfile2 --norm=-2 2>&1);
456 like $out, $format_qrs{percent2}, "basic cachegrind percent format, norm -2; 2 perls";
458 # read back the results in relative-percent form with sort
460 $out = qx($bench_cmd --read=$resultfile2 --sort=Ir:0 2>&1);
461 like $out, $format_qrs{percent2}, "basic cachegrind percent format, sort; 2 perls";
463 # read back the results in relative-percent form with sort and norm
465 $out = qx($bench_cmd --read=$resultfile2 --sort=Ir:0 --norm=0 2>&1);
466 like $out, $format_qrs{percent2}, "basic cachegrind percent format, sort, norm; 2 perls";
468 # and read back the results in raw form
470 $out = qx($bench_cmd --read=$resultfile2 --raw 2>&1);
471 like $out, $format_qrs{raw2}, "basic cachegrind raw format; 2 perls";
473 # and read back the results in raw form with norm
475 $out = qx($bench_cmd --read=$resultfile2 --raw --norm=0 2>&1);
476 like $out, $format_qrs{raw2}, "basic cachegrind raw format, norm; 2 perls";
478 # and read back the results in raw form with sort
480 $out = qx($bench_cmd --read=$resultfile2 --raw --sort=Ir:0 2>&1);
481 like $out, $format_qrs{raw2}, "basic cachegrind raw format, sort, norm; 2 perls";
483 # and read back the results in raw form with sort and norm
485 $out = qx($bench_cmd --read=$resultfile2 --raw --sort=Ir:0 --norm=0 2>&1);
486 like $out, $format_qrs{raw2}, "basic cachegrind raw format, sort, norm; 2 perls";
488 # and read back the results in compact form
490 $out = qx($bench_cmd --read=$resultfile2 --compact=1 2>&1);
491 like $out, $format_qrs{compact}, "basic cachegrind compact format; 2 perls";
493 # and read back the results in average form
495 $out = qx($bench_cmd --read=$resultfile2 --average 2>&1);
496 like $out, $format_qrs{average}, "basic cachegrind average format; 2 perls";
498 # and read back the results with selected fields
500 $out = qx($bench_cmd --read=$resultfile2 --fields=Ir,Dr 2>&1);
501 like $out, $format_qrs{fields2}, "basic cachegrind --fields; 2 perls";
503 # and read back the results in compact form with selected fields
505 $out = qx($bench_cmd --read=$resultfile2 --compact=1 --fields=Ir,Dr 2>&1);
506 like $out, $format_qrs{compact_fields}, "basic cachegrind compact, fields; 2 perls";
508 # and read back the results with 1 selected fields (this is more compact)
510 $out = qx($bench_cmd --read=$resultfile2 --fields=Ir 2>&1);
511 like $out, $format_qrs{'1field'}, "basic cachegrind 1 field; 2 perls";
516 # the Ir range here is intended such that the bisect will always fail
517 $out = qx($bench_cmd --read=t/porting/bench/callsub.json --tests=call::sub::empty --bisect=Ir,100000,100001 2>&1);
519 is $?, 1 << 8, "--bisect: exit result: should not match";
520 like $out, qr/^Bisect: Ir had the value -?\d+\n/,
521 "--bisect: got expected output";
523 # multiple reads with differing test sets but common --tests subset
525 $out = qx($bench_cmd --read=t/porting/bench/callsub.json --read=t/porting/bench/callsub2.json --tests=call::sub::empty 2>&1);
526 $out =~ s{\Q./perl perl2}{ p0 p1};
527 $out =~ s{^\./perl}{p0}m;
528 like $out, $format_qrs{percent2}, "2 reads; overlapping test sets";
530 # A read defines what benchmarks to run
532 note("running cachegrind on 1 perl; may be slow...");
533 $out = qx($bench_cmd --read=t/porting/bench/callsub.json --tests=call::sub::empty $^X=p1 2>&1);
534 $out =~ s{^\./perl}{p0}m;
535 $out =~ s{\Q./perl}{ p0};
536 like $out, $format_qrs{percent2}, "1 read; 1 generate";
538 # Process environment and optional args.
539 # This is a minimal test that it runs - it doesn't test whether
540 # the environment and args are getting applied correctly, apart from the
541 # fact that the perls in question are being successfully executed.
543 # Also check the --autolabel feature
545 note("running cachegrind on 2 perls; may be slow...");
548 --read=t/porting/bench/callsub.json
549 --read=t/porting/bench/callsub2.json
550 --tests=call::sub::empty
553 $^X --args='-Ifoo/bar -Mstrict' --env='FOO=foo'
554 $^X --args='-Ifoo/bar' --env='BAR=bar' --env='BAZ=baz'
559 $out =~ s{^\./perl}{p0}m;
560 $out =~ s{\Q ./perl perl2 p-0 p-1}
562 like $out, $format_qrs{percent4}, "4 perls with autolabel and args and env";
568 # Templates for expected output formats.
570 # Lines starting with '#' are skipped.
572 # Lines of the form 'FORMAT: foo' start and name a new template
574 # All other lines are part of the template
576 # Entries of the form NNNN.NN are converted into a regex of the form
577 # ( \s* -? \d+\.\d\d | - )
578 # i.e. it expects number with a fixed number of digits after the point,
581 # Any runs of space chars (but not tab) are converted into ' +',
582 # or ' *' if at the start of a line
584 # Entries of the form --- are converted into [-]+
586 # Lines of the form %%FOO%% are substituted with format 'FOO'
590 # ===================================================================
596 COND conditional branches
597 IND indirect branches
598 _m branch predict miss
599 _m1 level 1 cache miss
600 _mm last cache (e.g. L3) miss
601 - indeterminate percentage (e.g. 1/0)
602 # ===================================================================
606 The numbers represent relative counts per loop iteration, compared to
608 Higher is better: for example, using half as many instructions gives 200%,
609 while using twice as many gives 50%.
612 function call with no args or body
632 # ===================================================================
636 The numbers represent relative counts per loop iteration, compared to
638 Higher is better: for example, using half as many instructions gives 200%,
639 while using twice as many gives 50%.
642 function call with no args or body
645 ------ ------ ------ ------
646 Ir 100.00 NNN.NN NNN.NN NNN.NN
647 Dr 100.00 NNN.NN NNN.NN NNN.NN
648 Dw 100.00 NNN.NN NNN.NN NNN.NN
649 COND 100.00 NNN.NN NNN.NN NNN.NN
650 IND 100.00 NNN.NN NNN.NN NNN.NN
652 COND_m 100.00 NNN.NN NNN.NN NNN.NN
653 IND_m 100.00 NNN.NN NNN.NN NNN.NN
655 Ir_m1 100.00 NNN.NN NNN.NN NNN.NN
656 Dr_m1 100.00 NNN.NN NNN.NN NNN.NN
657 Dw_m1 100.00 NNN.NN NNN.NN NNN.NN
659 Ir_mm 100.00 NNN.NN NNN.NN NNN.NN
660 Dr_mm 100.00 NNN.NN NNN.NN NNN.NN
661 Dw_mm 100.00 NNN.NN NNN.NN NNN.NN
662 # ===================================================================
666 The numbers represent relative counts per loop iteration, compared to
668 Higher is better: for example, using half as many instructions gives 200%,
669 while using twice as many gives 50%.
672 function call with no args or body
678 # ===================================================================
682 The numbers represent raw counts per loop iteration.
685 function call with no args or body
705 # ===================================================================
709 The numbers represent raw counts per loop iteration.
731 # ===================================================================
735 The numbers represent raw counts per loop iteration.
738 function call with no args or body
744 # ===================================================================
748 The numbers represent raw counts per loop iteration.
751 function call with no args or body
758 COND NNNNNN.N NNNNNN.N
759 IND NNNNNN.N NNNNNN.N
761 COND_m NNNNNN.N NNNNNN.N
762 IND_m NNNNNN.N NNNNNN.N
764 Ir_m1 NNNNNN.N NNNNNN.N
765 Dr_m1 NNNNNN.N NNNNNN.N
766 Dw_m1 NNNNNN.N NNNNNN.N
768 Ir_mm NNNNNN.N NNNNNN.N
769 Dr_mm NNNNNN.N NNNNNN.N
770 Dw_mm NNNNNN.N NNNNNN.N
771 # ===================================================================
775 The numbers represent relative counts per loop iteration, compared to
777 Higher is better: for example, using half as many instructions gives 200%,
778 while using twice as many gives 50%.
782 Ir Dr Dw COND IND COND_m IND_m Ir_m1 Dr_m1 Dw_m1 Ir_mm Dr_mm Dw_mm
783 ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------
784 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 function call with no args or body
785 # ===================================================================
786 FORMAT: compact_fields
789 The numbers represent relative counts per loop iteration, compared to
791 Higher is better: for example, using half as many instructions gives 200%,
792 while using twice as many gives 50%.
798 NNN.NN NNN.NN call::sub::empty function call with no args or body
799 # ===================================================================
803 The numbers represent relative counts per loop iteration, compared to
805 Higher is better: for example, using half as many instructions gives 200%,
806 while using twice as many gives 50%.
812 call::sub::empty NNN.NN NNN.NN
813 # ===================================================================
817 The numbers represent relative counts per loop iteration, compared to
819 Higher is better: for example, using half as many instructions gives 200%,
820 while using twice as many gives 50%.
842 # ===================================================================
846 The numbers represent raw counts per loop iteration.
850 Ir Dr Dw COND IND COND_m IND_m Ir_m1 Dr_m1 Dw_m1 Ir_mm Dr_mm Dw_mm
851 ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------
852 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 function call with no args or body
853 # ===================================================================