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
85 # convert run of space chars into ' +' or ' *'
87 $f =~ s/(\A|\n)(\\ )+/$1 */g;
90 # convert '---' placeholders into a regex
91 $f =~ s/(\\-){2,}/-+/g;
93 $format_qrs{$name} = qr/\A$f\z/;
98 # ---------------------------------------------------
104 "Unknown option: boz\nUse the -h option for usage information.\n",
105 "croak: basic unknown option"
109 "Error: --fields: unknown field 'Boz'\n",
110 "croak: unknown --field"
114 "Error: unrecognised action 'boz'\nmust be one of: grind, selftest\n",
115 "croak: unknown --action"
119 "Error: --sort argument should be of the form field:perl: 'boz'\n",
120 "croak: invalid --sort"
124 "Error: --sort: unknown field 'boz'\n",
125 "croak: unknown --sort field"
128 "-action=selftest perl",
129 "Error: no perl executables may be specified with selftest\n",
130 "croak: --action-selftest with executable"
134 "Error: --tests regex must be of the form /.../\n",
135 "croak: invalid --tests regex"
138 "--tests=call::sub::empty,foo::bar::baz::boz perl",
139 "Error: no such test found: 'foo::bar::baz::boz'\n"
140 . "Re-run with --verbose for a list of valid tests.\n",
141 "croak: unknown test in --tests"
144 "--verbose --tests=call::sub::empty,foo::bar::baz::boz --read=t/porting/bench/callsub.json",
145 "Error: no such test found: 'foo::bar::baz::boz'\n"
146 . "Valid test names are:\n"
147 . " call::sub::amp_empty\n"
148 . " call::sub::empty\n",
149 "croak: unknown test in --tests --verbose"
152 "--tests=/foo::bar::baz::boz/ perl",
153 "Error: no tests to run\n",
154 "croak: no --tests to run "
157 "--benchfile=no-such-file-boz perl",
158 qr/\AError: can't read 'no-such-file-boz':/,
159 "croak: non-existent --benchfile "
162 "--benchfile=t/porting/bench/synerr perl",
163 qr{\AError: can't parse 't/porting/bench/synerr':\nsyntax error},
164 "croak: --benchfile with syntax error"
167 "--benchfile=t/porting/bench/ret0 perl",
168 "Error: can't load 't/porting/bench/ret0': code didn't return a true value\n",
169 "croak: --benchfile which returns 0"
172 "--benchfile=t/porting/bench/oddentry perl",
173 qr{\AError: 't/porting/bench/oddentry' does not contain evenly paired test names and hashes\n},
174 "croak: --benchfile with odd number of entries"
177 "--benchfile=t/porting/bench/badname perl",
178 qr{\AError: 't/porting/bench/badname': invalid test name: '1='\n},
179 "croak: --benchfile with invalid test name"
182 "--benchfile=t/porting/bench/badhash perl",
183 qr{\AError: 't/porting/bench/badhash': invalid key 'blah' for test 'foo::bar'\n},
184 "croak: --benchfile with invalid test hash key"
187 "--norm=2 ./miniperl ./perl",
188 "Error: --norm value 2 outside range 0..1\n",
189 "croak: select-a-perl out of range"
192 "--norm=-0 ./miniperl ./perl",
193 "Error: --norm value -0 outside range -1..-2\n",
194 "croak: select-a-perl out of range"
197 "--norm=-3 ./miniperl ./perl",
198 "Error: --norm value -3 outside range -1..-2\n",
199 "croak: select-a-perl out of range"
202 "--sort=Ir:myperl ./miniperl ./perl",
203 "Error: --sort: unrecognised perl 'myperl'\n"
204 . "Valid perl names are:\n"
207 "croak: select-a-perl unrecognised"
210 "--compact=./perl ./perl=A ./perl=B",
211 "Error: --compact: ambiguous perl './perl'\n",
212 "croak: select-a-perl ambiguous"
216 "Error: unrecognised executable switch '--foo'\n",
217 "croak: ./perl --foo"
221 "Error: --args without a preceding executable name\n",
222 "croak: --args without perl"
226 "Error: --env without a preceding executable name\n",
227 "croak: --env without perl"
231 "Error: --args is missing value\n",
232 "croak: --args without value"
236 "Error: --env is missing value\n",
237 "croak: --env without value"
240 "./perl --env='FOO'",
241 "Error: --env is missing =value\n",
242 "croak: --env without =value"
246 "Error: duplicate label './perl': each executable must have a unique label\n",
247 "croak: duplicate label ./perl ./perl"
250 "./perl=A ./miniperl=A",
251 "Error: duplicate label 'A': each executable must have a unique label\n",
252 "croak: duplicate label =A =A"
255 "--read=t/porting/bench/callsub.json --read=t/porting/bench/callsub.json",
256 "Error: duplicate label './perl': seen in file 't/porting/bench/callsub.json'\n",
257 "croak: duplicate label --read=... --read=..."
260 "--read=t/porting/bench/callsub.json ./perl",
261 "Error: duplicate label './perl': seen both in --read file and on command line\n",
262 "croak: duplicate label --read=... ./perl"
266 qr{^\QError: unable to execute './nosuch-perl': },
267 "croak: no such perl"
270 "--grindargs=Boz --debug --tests=call::sub::empty ./perl=A ./perl=B",
271 qr{Error: .*?(unexpected code or cachegrind output|gave return status)}s,
272 "croak: cachegrind output format "
276 "Error: --bisect option must be of form 'field,integer,integer'\n",
281 "Error: --bisect option must be of form 'field,integer,integer'\n",
282 "croak: --bisect=Ir,1"
286 "Error: --bisect option must be of form 'field,integer,integer'\n",
287 "croak: --bisect=Ir,1,2,3"
291 "Error: --bisect option must be of form 'field,integer,integer'\n",
292 "croak: --bisect=Ir,1,x"
296 "Error: --bisect option must be of form 'field,integer,integer'\n",
297 "croak: --bisect=Ir,x,2"
301 "Error: unrecognised field 'boz' in --bisect option\n",
302 "croak: --bisect=boz,1,2"
306 "Error: --bisect min (2) must be <= max (1)\n",
307 "croak: --bisect=boz,2,1"
310 "--read=no-such-file-boz",
311 qr/\AError: can't open 'no-such-file-boz' for reading:/,
312 "croak: non-existent --read file "
315 "--read=t/porting/bench/badversion.json",
316 "Error: unsupported version 9999.9 in file 't/porting/bench/badversion.json' (too new)\n",
317 "croak: --read version"
320 "--read=t/porting/bench/callsub.json --benchfile=t/perf/benchmarks ./perl ",
321 "Error: --benchfile cannot be used when --read is present\n",
322 "croak: benchfile with read"
326 "Error: nothing to do: no perls to run, no data to read.\n",
331 "Error: need at least 2 perls for comparison.\n",
332 "croak: need 2 perls"
335 "--bisect=Ir,1,2 ./perl=A ./perl=B",
336 "Error: exactly one perl executable must be specified for bisect\n",
337 "croak: --bisect, need 1 perls"
340 "--bisect=Ir,1,2 --tests=/call/ ./perl=A",
341 "Error: only a single test may be specified with --bisect\n",
342 "croak: --bisect one test only"
344 # note that callsub.json was created using
345 # ./perl -Ilib Porting/bench.pl --tests='/call::sub::(amp_)?empty/' \
346 # --write=t/porting/bench/callsub.json ./perl
348 "--read=t/porting/bench/callsub.json --write=no/such/file/boz",
349 qr{\AError: can't open 'no/such/file/boz' for writing: },
350 "croak: --write open error"
352 # note that callsub2.json was created using
353 # ./perl -Ilib Porting/bench.pl \
354 # --tests='call::sub::empty,call::sub::args3' \
355 # --write=t/porting/bench/callsub2.json ./perl=perl2
357 "--read=t/porting/bench/callsub.json "
358 . " --read=t/porting/bench/callsub2.json",
359 "Can't merge multiple read files: they contain differing test sets.\n"
360 . "Re-run with --verbose to see the differences.\n",
361 "croak: --read callsub, callsub2"
364 "--read=t/porting/bench/callsub.json "
365 . " --read=t/porting/bench/callsub2.json"
367 "Can't merge multiple read files: they contain differing test sets.\n"
368 . "Previous tests:\n"
369 . " call::sub::amp_empty\n"
370 . " call::sub::empty\n"
371 . "tests from 't/porting/bench/callsub2.json':\n"
372 . " call::sub::args3\n"
373 . " call::sub::empty\n",
374 "croak: --read callsub, callsub2 --verbose"
377 # these ones aren't tested (and nor are any "Panic:" ones):
379 # Error: can't parse '$field' field from cachegrind output
380 # Error: while starting cachegrind subprocess for NNNN:
381 # File '$file' contains no results
382 # File '$file' contains differing test and results names
383 # File '$file' contains differing test and sort order names
384 # Can't merge multiple read files: differing loop counts:
387 my ($args, $expected, $desc) = @$test;
388 $out = qx($bench_cmd $args 2>&1);
389 if (ref($expected)) {
390 like $out, $expected, $desc;
393 is $out, $expected, $desc;
397 # ---------------------------------------------------
401 my $resultfile1 = tempfile(); # benchmark results for 1 perl
402 my $resultfile2 = tempfile(); # benchmark results for 2 perls
404 # Run a real cachegrind session and write results to file.
405 # the -j 2 is to minimally exercise its parallel facility.
407 note("running cachegrind for 1st perl; may be slow...");
408 $out = qx($bench_cmd -j 2 --write=$resultfile1 --tests=call::sub::empty $^X=p0 2>&1);
409 is $out, "", "--write should produce no output (1 perl)";
410 ok -s $resultfile1, "--write should create a non-empty results file (1 perl)";
412 # and again with 2 perls. This is also tests the 'mix read and new new
413 # perls' functionality.
415 note("running cachegrind for 2nd perl; may be slow...");
416 $out = qx($bench_cmd -j 2 --read=$resultfile1 --write=$resultfile2 $^X=p1 2>&1);
417 is $out, "", "--write should produce no output (2 perls)"
418 or diag("got: $out");
419 ok -s $resultfile2, "--write should create a non-empty results file (2 perls)";
423 # read back the results in raw form
425 $out = qx($bench_cmd --read=$resultfile1 --raw 2>&1);
426 like $out, $format_qrs{raw1}, "basic cachegrind raw format; 1 perl";
428 # and read back the results in raw compact form
430 $out = qx($bench_cmd --read=$resultfile1 --raw --compact=0 2>&1);
431 like $out, $format_qrs{raw_compact}, "basic cachegrind raw compact format; 1 perl";
433 # and read back the results in raw average form
435 $out = qx($bench_cmd --read=$resultfile1 --raw --average 2>&1);
436 like $out, $format_qrs{raw_average1}, "basic cachegrind raw average format; 1 perl";
438 # and read back the results with raw selected fields
440 $out = qx($bench_cmd --read=$resultfile1 --raw --fields=Ir,Dr 2>&1);
441 like $out, $format_qrs{fields1}, "basic cachegrind --fields; 1 perl";
445 # read back the results in relative-percent form
447 $out = qx($bench_cmd --read=$resultfile2 2>&1);
448 like $out, $format_qrs{percent2}, "basic cachegrind percent format; 2 perls";
450 # read back the results in relative-percent form with norm
452 $out = qx($bench_cmd --read=$resultfile2 --norm=0 2>&1);
453 like $out, $format_qrs{percent2}, "basic cachegrind percent format, norm; 2 perls";
455 # ditto with negative norm
457 $out = qx($bench_cmd --read=$resultfile2 --norm=-2 2>&1);
458 like $out, $format_qrs{percent2}, "basic cachegrind percent format, norm -2; 2 perls";
460 # read back the results in relative-percent form with sort
462 $out = qx($bench_cmd --read=$resultfile2 --sort=Ir:0 2>&1);
463 like $out, $format_qrs{percent2}, "basic cachegrind percent format, sort; 2 perls";
465 # read back the results in relative-percent form with sort and norm
467 $out = qx($bench_cmd --read=$resultfile2 --sort=Ir:0 --norm=0 2>&1);
468 like $out, $format_qrs{percent2}, "basic cachegrind percent format, sort, norm; 2 perls";
470 # and read back the results in raw form
472 $out = qx($bench_cmd --read=$resultfile2 --raw 2>&1);
473 like $out, $format_qrs{raw2}, "basic cachegrind raw format; 2 perls";
475 # and read back the results in raw form with norm
477 $out = qx($bench_cmd --read=$resultfile2 --raw --norm=0 2>&1);
478 like $out, $format_qrs{raw2}, "basic cachegrind raw format, norm; 2 perls";
480 # and read back the results in raw form with sort
482 $out = qx($bench_cmd --read=$resultfile2 --raw --sort=Ir:0 2>&1);
483 like $out, $format_qrs{raw2}, "basic cachegrind raw format, sort, norm; 2 perls";
485 # and read back the results in raw form with sort and norm
487 $out = qx($bench_cmd --read=$resultfile2 --raw --sort=Ir:0 --norm=0 2>&1);
488 like $out, $format_qrs{raw2}, "basic cachegrind raw format, sort, norm; 2 perls";
490 # and read back the results in compact form
492 $out = qx($bench_cmd --read=$resultfile2 --compact=1 2>&1);
493 like $out, $format_qrs{compact}, "basic cachegrind compact format; 2 perls";
495 # and read back the results in average form
497 $out = qx($bench_cmd --read=$resultfile2 --average 2>&1);
498 like $out, $format_qrs{average}, "basic cachegrind average format; 2 perls";
500 # and read back the results with selected fields
502 $out = qx($bench_cmd --read=$resultfile2 --fields=Ir,Dr 2>&1);
503 like $out, $format_qrs{fields2}, "basic cachegrind --fields; 2 perls";
505 # and read back the results in compact form with selected fields
507 $out = qx($bench_cmd --read=$resultfile2 --compact=1 --fields=Ir,Dr 2>&1);
508 like $out, $format_qrs{compact_fields}, "basic cachegrind compact, fields; 2 perls";
510 # and read back the results with 1 selected fields (this is more compact)
512 $out = qx($bench_cmd --read=$resultfile2 --fields=Ir 2>&1);
513 like $out, $format_qrs{'1field'}, "basic cachegrind 1 field; 2 perls";
518 # the Ir range here is intended such that the bisect will always fail
519 $out = qx($bench_cmd --read=t/porting/bench/callsub.json --tests=call::sub::empty --bisect=Ir,100000,100001 2>&1);
521 is $?, 1 << 8, "--bisect: exit result: should not match";
522 like $out, qr/^Bisect: Ir had the value -?\d+\n/,
523 "--bisect: got expected output";
525 # multiple reads with differing test sets but common --tests subset
527 $out = qx($bench_cmd --read=t/porting/bench/callsub.json --read=t/porting/bench/callsub2.json --tests=call::sub::empty 2>&1);
528 $out =~ s{\Q./perl perl2}{ p0 p1};
529 $out =~ s{^\./perl}{p0}m;
530 like $out, $format_qrs{percent2}, "2 reads; overlapping test sets";
532 # A read defines what benchmarks to run
534 note("running cachegrind on 1 perl; may be slow...");
535 $out = qx($bench_cmd --read=t/porting/bench/callsub.json --tests=call::sub::empty $^X=p1 2>&1);
536 $out =~ s{^\./perl}{p0}m;
537 $out =~ s{\Q./perl}{ p0};
538 like $out, $format_qrs{percent2}, "1 read; 1 generate";
540 # Process environment and optional args.
541 # This is a minimal test that it runs - it doesn't test whether
542 # the environment and args are getting applied correctly, apart from the
543 # fact that the perls in question are being successfully executed.
545 # Also check the --autolabel feature
547 note("running cachegrind on 2 perls; may be slow...");
550 --read=t/porting/bench/callsub.json
551 --read=t/porting/bench/callsub2.json
552 --tests=call::sub::empty
555 $^X --args='-Ifoo/bar -Mstrict' --env='FOO=foo'
556 $^X --args='-Ifoo/bar' --env='BAR=bar' --env='BAZ=baz'
561 $out =~ s{^\./perl}{p0}m;
562 $out =~ s{\Q ./perl perl2 p-0 p-1}
564 like $out, $format_qrs{percent4}, "4 perls with autolabel and args and env";
570 # Templates for expected output formats.
572 # Lines starting with '#' are skipped.
574 # Lines of the form 'FORMAT: foo' start and name a new template
576 # All other lines are part of the template
578 # Entries of the form NNNN.NN are converted into a regex of the form
579 # ( \s* -? \d+\.\d\d | - )
580 # i.e. it expects number with a fixed number of digits after the point,
583 # Any runs of space chars (but not tab) are converted into ' +',
584 # or ' *' if at the start of a line
586 # Entries of the form --- are converted into [-]+
588 # Lines of the form %%FOO%% are substituted with format 'FOO'
592 # ===================================================================
598 COND conditional branches
599 IND indirect branches
600 _m branch predict miss
601 _m1 level 1 cache miss
602 _mm last cache (e.g. L3) miss
603 - indeterminate percentage (e.g. 1/0)
604 # ===================================================================
608 The numbers represent relative counts per loop iteration, compared to
610 Higher is better: for example, using half as many instructions gives 200%,
611 while using twice as many gives 50%.
614 function call with no args or body
634 # ===================================================================
638 The numbers represent relative counts per loop iteration, compared to
640 Higher is better: for example, using half as many instructions gives 200%,
641 while using twice as many gives 50%.
644 function call with no args or body
647 ------ ------ ------ ------
648 Ir 100.00 NNN.NN NNN.NN NNN.NN
649 Dr 100.00 NNN.NN NNN.NN NNN.NN
650 Dw 100.00 NNN.NN NNN.NN NNN.NN
651 COND 100.00 NNN.NN NNN.NN NNN.NN
652 IND 100.00 NNN.NN NNN.NN NNN.NN
654 COND_m 100.00 NNN.NN NNN.NN NNN.NN
655 IND_m 100.00 NNN.NN NNN.NN NNN.NN
657 Ir_m1 100.00 NNN.NN NNN.NN NNN.NN
658 Dr_m1 100.00 NNN.NN NNN.NN NNN.NN
659 Dw_m1 100.00 NNN.NN NNN.NN NNN.NN
661 Ir_mm 100.00 NNN.NN NNN.NN NNN.NN
662 Dr_mm 100.00 NNN.NN NNN.NN NNN.NN
663 Dw_mm 100.00 NNN.NN NNN.NN NNN.NN
664 # ===================================================================
668 The numbers represent relative counts per loop iteration, compared to
670 Higher is better: for example, using half as many instructions gives 200%,
671 while using twice as many gives 50%.
674 function call with no args or body
680 # ===================================================================
684 The numbers represent raw counts per loop iteration.
687 function call with no args or body
707 # ===================================================================
711 The numbers represent raw counts per loop iteration.
733 # ===================================================================
737 The numbers represent raw counts per loop iteration.
740 function call with no args or body
746 # ===================================================================
750 The numbers represent raw counts per loop iteration.
753 function call with no args or body
760 COND NNNNNN.N NNNNNN.N
761 IND NNNNNN.N NNNNNN.N
763 COND_m NNNNNN.N NNNNNN.N
764 IND_m NNNNNN.N NNNNNN.N
766 Ir_m1 NNNNNN.N NNNNNN.N
767 Dr_m1 NNNNNN.N NNNNNN.N
768 Dw_m1 NNNNNN.N NNNNNN.N
770 Ir_mm NNNNNN.N NNNNNN.N
771 Dr_mm NNNNNN.N NNNNNN.N
772 Dw_mm NNNNNN.N NNNNNN.N
773 # ===================================================================
777 The numbers represent relative counts per loop iteration, compared to
779 Higher is better: for example, using half as many instructions gives 200%,
780 while using twice as many gives 50%.
784 Ir Dr Dw COND IND COND_m IND_m Ir_m1 Dr_m1 Dw_m1 Ir_mm Dr_mm Dw_mm
785 ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------
786 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
787 # ===================================================================
788 FORMAT: compact_fields
791 The numbers represent relative counts per loop iteration, compared to
793 Higher is better: for example, using half as many instructions gives 200%,
794 while using twice as many gives 50%.
800 NNN.NN NNN.NN call::sub::empty function call with no args or body
801 # ===================================================================
805 The numbers represent relative counts per loop iteration, compared to
807 Higher is better: for example, using half as many instructions gives 200%,
808 while using twice as many gives 50%.
814 call::sub::empty NNN.NN NNN.NN
815 # ===================================================================
819 The numbers represent relative counts per loop iteration, compared to
821 Higher is better: for example, using half as many instructions gives 200%,
822 while using twice as many gives 50%.
844 # ===================================================================
848 The numbers represent raw counts per loop iteration.
852 Ir Dr Dw COND IND COND_m IND_m Ir_m1 Dr_m1 Dw_m1 Ir_mm Dr_mm Dw_mm
853 ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------
854 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
855 # ===================================================================