This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Porting/bench.pl: improve 'no such test' err msg
[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",
e89a8e10
DM
121 "Error: no such test found: 'foo::bar::baz::boz'\n"
122 . "Re-run with --verbose for a list of valid tests.\n",
1137c9fa
DM
123 "croak: unknown test in --tests"
124 ],
125 [
e89a8e10
DM
126 "--verbose --tests=call::sub::empty,foo::bar::baz::boz --read=t/porting/bench/callsub.json",
127 "Error: no such test found: 'foo::bar::baz::boz'\n"
128 . "Valid test names are:\n"
129 . " call::sub::amp_empty\n"
130 . " call::sub::empty\n",
131 "croak: unknown test in --tests --verbose"
132 ],
133 [
1137c9fa
DM
134 "--tests=/foo::bar::baz::boz/ perl",
135 "Error: no tests to run\n",
136 "croak: no --tests to run "
137 ],
138 [
139 "--benchfile=no-such-file-boz perl",
140 qr/\AError: can't read 'no-such-file-boz':/,
141 "croak: non-existent --benchfile "
142 ],
143 [
144 "--benchfile=t/porting/bench/synerr perl",
145 qr{\AError: can't parse 't/porting/bench/synerr':\nsyntax error},
146 "croak: --benchfile with syntax error"
147 ],
148 [
149 "--benchfile=t/porting/bench/ret0 perl",
150 "Error: can't load 't/porting/bench/ret0': code didn't return a true value\n",
151 "croak: --benchfile which returns 0"
152 ],
153 [
154 "--norm=2 ./miniperl ./perl",
155 "Error: --norm value 2 outside range 0..1\n",
156 "croak: select-a-perl out of range"
157 ],
158 [
159 "--sort=Ir:myperl ./miniperl ./perl",
160 "Error: --sort: unrecognised perl 'myperl'\n",
161 "croak: select-a-perl unrecognised"
162 ],
163 [
164 "--compact=./perl ./perl=A ./perl=B",
165 "Error: --compact: ambiguous perl './perl'\n",
166 "croak: select-a-perl ambiguous"
167 ],
168 [
99b1e78b
DM
169 "./perl --foo",
170 "Error: unrecognised executable switch '--foo'\n",
171 "croak: ./perl --foo"
172 ],
173 [
174 "-- --args=foo",
175 "Error: --args without a preceding executable name\n",
176 "croak: --args without perl"
177 ],
178 [
179 "-- --env=foo=bar",
180 "Error: --env without a preceding executable name\n",
181 "croak: --env without perl"
182 ],
183 [
184 "./perl --args",
185 "Error: --args is missing value\n",
186 "croak: --args without value"
187 ],
188 [
189 "./perl --env",
190 "Error: --env is missing value\n",
191 "croak: --env without value"
192 ],
193 [
194 "./perl --env='FOO'",
195 "Error: --env is missing =value\n",
196 "croak: --env without =value"
197 ],
198 [
199 "./perl ./perl",
81cb9d79
DM
200 "Error: duplicate label './perl': each executable must have a unique label\n",
201 "croak: duplicate label ./perl ./perl"
202 ],
203 [
99b1e78b 204 "./perl=A ./miniperl=A",
81cb9d79
DM
205 "Error: duplicate label 'A': each executable must have a unique label\n",
206 "croak: duplicate label =A =A"
207 ],
208 [
209 "--read=t/porting/bench/callsub.json --read=t/porting/bench/callsub.json",
210 "Error: duplicate label './perl': seen in file 't/porting/bench/callsub.json'\n",
211 "croak: duplicate label --read=... --read=..."
212 ],
213 [
214 "--read=t/porting/bench/callsub.json ./perl",
215 "Error: duplicate label './perl': seen both in --read file and on command line\n",
216 "croak: duplicate label --read=... ./perl"
1137c9fa
DM
217 ],
218 [
99b1e78b
DM
219 "./nosuch-perl",
220 qr{^\QError: unable to execute './nosuch-perl': },
221 "croak: no such perl"
222 ],
223 [
1137c9fa
DM
224 "--grindargs=Boz --tests=call::sub::empty ./perl=A ./perl=B",
225 qr{Error: while executing call::sub::empty/A empty/short loop:\nunexpected code or cachegrind output:\n},
226 "croak: cachegrind output format "
227 ],
228 [
229 "--bisect=Ir",,
230 "Error: --bisect option must be of form 'field,integer,integer'\n",
231 "croak: --bisect=Ir"
232 ],
233 [
234 "--bisect=Ir,1",,
235 "Error: --bisect option must be of form 'field,integer,integer'\n",
236 "croak: --bisect=Ir,1"
237 ],
238 [
239 "--bisect=Ir,1,2,3",
240 "Error: --bisect option must be of form 'field,integer,integer'\n",
241 "croak: --bisect=Ir,1,2,3"
242 ],
243 [
244 "--bisect=Ir,1,x",
245 "Error: --bisect option must be of form 'field,integer,integer'\n",
246 "croak: --bisect=Ir,1,x"
247 ],
248 [
249 "--bisect=Ir,x,2",
250 "Error: --bisect option must be of form 'field,integer,integer'\n",
251 "croak: --bisect=Ir,x,2"
252 ],
253 [
254 "--bisect=boz,1,2",
255 "Error: unrecognised field 'boz' in --bisect option\n",
256 "croak: --bisect=boz,1,2"
257 ],
258 [
259 "--bisect=Ir,2,1",
260 "Error: --bisect min (2) must be <= max (1)\n",
261 "croak: --bisect=boz,2,1"
262 ],
263 [
264 "--read=no-such-file-boz",
265 qr/\AError: can't open 'no-such-file-boz' for reading:/,
266 "croak: non-existent --read file "
267 ],
268 [
269 "--read=t/porting/bench/badversion.json",
270 "Error: unsupported version 9999.9 in file 't/porting/bench/badversion.json' (too new)\n",
271 "croak: --read version"
272 ],
4533e88f
DM
273 [
274 "--read=t/porting/bench/callsub.json --benchfile=t/perf/benchmarks ./perl ",
275 "Error: --benchfile cannot be used when --read is present\n",
276 "croak: benchfile with read"
277 ],
1137c9fa
DM
278 [
279 "",
280 "Error: nothing to do: no perls to run, no data to read.\n",
281 "croak: no input"
282 ],
283 [
284 "./perl",
285 "Error: need at least 2 perls for comparison.\n",
286 "croak: need 2 perls"
287 ],
288 [
289 "--bisect=Ir,1,2 ./perl=A ./perl=B",
290 "Error: exactly one perl executable must be specified for bisect\n",
291 "croak: --bisect, need 1 perls"
292 ],
293 [
294 "--bisect=Ir,1,2 --tests=/call/ ./perl=A",
295 "Error: only a single test may be specified with --bisect\n",
296 "croak: --bisect one test only"
297 ],
298 # note that callsub.json was created using
299 # ./perl -Ilib Porting/bench.pl --tests='/call::sub::(amp_)?empty/' \
300 # --write=t/porting/bench/callsub.json ./perl
1137c9fa
DM
301 [
302 "--read=t/porting/bench/callsub.json --write=no/such/file/boz",
303 qr{\AError: can't open 'no/such/file/boz' for writing: },
304 "croak: --write open error"
305 ],
68de41bc
DM
306 # note that callsub2.json was created using
307 # ./perl -Ilib Porting/bench.pl \
308 # --tests='call::sub::empty,call::sub::args3' \
81cb9d79 309 # --write=t/porting/bench/callsub2.json ./perl=perl2
68de41bc
DM
310 [
311 "--read=t/porting/bench/callsub.json "
312 . " --read=t/porting/bench/callsub2.json",
313 "Can't merge multiple read files: they contain differing test sets.\n"
314 . "Re-run with --verbose to see the differences.\n",
315 "croak: --read callsub, callsub2"
316 ],
317 [
318 "--read=t/porting/bench/callsub.json "
319 . " --read=t/porting/bench/callsub2.json"
320 . " --verbose",
321 "Can't merge multiple read files: they contain differing test sets.\n"
322 . "Previous tests:\n"
323 . " call::sub::amp_empty\n"
324 . " call::sub::empty\n"
325 . "tests from 't/porting/bench/callsub2.json':\n"
326 . " call::sub::args3\n"
327 . " call::sub::empty\n",
328 "croak: --read callsub, callsub2 --verbose"
329 ],
1137c9fa 330
68de41bc 331 # these ones aren't tested (and nor are any "Panic:" ones):
1137c9fa
DM
332
333 # Error: can't parse '$field' field from cachegrind output
334 # Error: while starting cachegrind subprocess for NNNN:
68de41bc
DM
335 # File '$file' contains no results
336 # File '$file' contains differing test and results names
337 # File '$file' contains differing test and sort order names
338 # Can't merge multiple read files: differing loop counts:
1137c9fa
DM
339)
340{
341 my ($args, $expected, $desc) = @$test;
342 $out = qx($bench_cmd $args 2>&1);
343 if (ref($expected)) {
344 like $out, $expected, $desc;
345 }
346 else {
347 is $out, $expected, $desc;
348 }
349}
350
351# ---------------------------------------------------
352# run benchmarks
353
354
6568b26d
DM
355my $resultfile1 = tempfile(); # benchmark results for 1 perl
356my $resultfile2 = tempfile(); # benchmark results for 2 perls
357
358# Run a real cachegrind session and write results to file.
359# the -j 2 is to minimally exercise its parallel facility.
360
361note("running cachegrind for 1st perl; may be slow...");
362$out = qx($bench_cmd -j 2 --write=$resultfile1 --tests=call::sub::empty $^X=p0 2>&1);
363is length($out), 0, "--write should produce no output (1 perl)";
364ok -s $resultfile1, "--write should create a non-empty results file (1 perl)";
365
366# and again with 2 perls. This is also tests the 'mix read and new new
367# perls' functionality.
368
369note("running cachegrind for 2nd perl; may be slow...");
370$out = qx($bench_cmd -j 2 --read=$resultfile1 --write=$resultfile2 $^X=p1 2>&1);
d9b91f79
DM
371is length($out), 0, "--write should produce no output (2 perls)"
372 or diag("got: $out");
6568b26d
DM
373ok -s $resultfile2, "--write should create a non-empty results file (2 perls)";
374
375# 1 perl:
376
377# read back the results in raw form
378
379$out = qx($bench_cmd --read=$resultfile1 --raw 2>&1);
380like $out, $format_qrs{raw1}, "basic cachegrind raw format; 1 perl";
381
382# and read back the results in raw compact form
383
384$out = qx($bench_cmd --read=$resultfile1 --raw --compact=0 2>&1);
385like $out, $format_qrs{raw_compact}, "basic cachegrind raw compact format; 1 perl";
386
387# 2 perls:
388
389# read back the results in relative-percent form
390
391$out = qx($bench_cmd --read=$resultfile2 2>&1);
392like $out, $format_qrs{percent2}, "basic cachegrind percent format; 2 perls";
393
394# and read back the results in raw form
395
396$out = qx($bench_cmd --read=$resultfile2 --raw 2>&1);
397like $out, $format_qrs{raw2}, "basic cachegrind raw format; 2 perls";
398
399# and read back the results in compact form
400
401$out = qx($bench_cmd --read=$resultfile2 --compact=1 2>&1);
402like $out, $format_qrs{compact}, "basic cachegrind compact format; 2 perls";
403
404
d9b91f79
DM
405# bisect
406
d9b91f79 407# the Ir range here is intended such that the bisect will always fail
30792d9e 408$out = qx($bench_cmd --read=t/porting/bench/callsub.json --tests=call::sub::empty --bisect=Ir,100000,100001 2>&1);
d9b91f79
DM
409
410is $?, 1 << 8, "--bisect should not match";
411is length($out), 0, "--bisect should produce no output"
412 or diag("got: $out");
6568b26d 413
68de41bc
DM
414# multiple reads with differing test sets but common --tests subset
415
416$out = qx($bench_cmd --read=t/porting/bench/callsub.json --read=t/porting/bench/callsub2.json --tests=call::sub::empty 2>&1);
81cb9d79 417$out =~ s{\Q./perl perl2}{ p0 p1};
68de41bc 418$out =~ s{^\./perl}{p0}m;
68de41bc
DM
419like $out, $format_qrs{percent2}, "2 reads; overlapping test sets";
420
421# A read defines what benchmarks to run
422
423note("running cachegrind on 1 perl; may be slow...");
424$out = qx($bench_cmd --read=t/porting/bench/callsub.json --tests=call::sub::empty $^X=p1 2>&1);
425$out =~ s{^\./perl}{p0}m;
426$out =~ s{\Q./perl}{ p0};
427like $out, $format_qrs{percent2}, "1 read; 1 generate";
428
99b1e78b
DM
429# Process environment and optional args.
430# This is a minimal test that it runs - it doesn't test whether
431# the environment and args are getting applied correctly, apart from the
432# fact that the perls in question are being successfully executed.
433
434note("running cachegrind on 2 perls; may be slow...");
435$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);
436like $out, $format_qrs{percent2}, "2 perls with args and env";
437
68de41bc 438
1137c9fa 439
6568b26d
DM
440done_testing();
441
442
443# Templates for expected output formats.
444#
445# Lines starting with '#' are skipped.
446# Lines of the form 'FORMAT: foo' start and name a new template
447# All other lines are part of the template
448# Entries of the form NNNN.NN are converted into a regex of the form
449# ( \s* -? \d+\.\d\d | - )
450# i.e. it expects number with a fixed number of digits after the point,
451# or a '-'.
452# Lines of the form %%FOO%% are substituted with format 'FOO'
453
454
455__END__
456# ===================================================================
457FORMAT: STD_HEADER
458Key:
459 Ir Instruction read
460 Dr Data read
461 Dw Data write
462 COND conditional branches
463 IND indirect branches
464 _m branch predict miss
465 _m1 level 1 cache miss
466 _mm last cache (e.g. L3) miss
467 - indeterminate percentage (e.g. 1/0)
468# ===================================================================
469FORMAT: percent2
470%%STD_HEADER%%
471
472The numbers represent relative counts per loop iteration, compared to
473p0 at 100.0%.
474Higher is better: for example, using half as many instructions gives 200%,
475while using twice as many gives 50%.
476
477call::sub::empty
478function call with no args or body
479
480 p0 p1
481 ------ ------
482 Ir 100.00 NNN.NN
483 Dr 100.00 NNN.NN
484 Dw 100.00 NNN.NN
485 COND 100.00 NNN.NN
486 IND 100.00 NNN.NN
487
488COND_m 100.00 NNN.NN
489 IND_m 100.00 NNN.NN
490
491 Ir_m1 100.00 NNN.NN
492 Dr_m1 100.00 NNN.NN
493 Dw_m1 100.00 NNN.NN
494
495 Ir_mm 100.00 NNN.NN
496 Dr_mm 100.00 NNN.NN
497 Dw_mm 100.00 NNN.NN
498# ===================================================================
499FORMAT: raw1
500%%STD_HEADER%%
501
502The numbers represent raw counts per loop iteration.
503
504call::sub::empty
505function call with no args or body
506
507 p0
508 --------
509 Ir NNNNNN.N
510 Dr NNNNNN.N
511 Dw NNNNNN.N
512 COND NNNNNN.N
513 IND NNNNNN.N
514
515COND_m NNNNNN.N
516 IND_m NNNNNN.N
517
518 Ir_m1 NNNNNN.N
519 Dr_m1 NNNNNN.N
520 Dw_m1 NNNNNN.N
521
522 Ir_mm NNNNNN.N
523 Dr_mm NNNNNN.N
524 Dw_mm NNNNNN.N
525# ===================================================================
526FORMAT: raw2
527%%STD_HEADER%%
528
529The numbers represent raw counts per loop iteration.
530
531call::sub::empty
532function call with no args or body
533
534 p0 p1
535 -------- --------
536 Ir NNNNNN.N NNNNNN.N
537 Dr NNNNNN.N NNNNNN.N
538 Dw NNNNNN.N NNNNNN.N
539 COND NNNNNN.N NNNNNN.N
540 IND NNNNNN.N NNNNNN.N
541
542COND_m NNNNNN.N NNNNNN.N
543 IND_m NNNNNN.N NNNNNN.N
544
545 Ir_m1 NNNNNN.N NNNNNN.N
546 Dr_m1 NNNNNN.N NNNNNN.N
547 Dw_m1 NNNNNN.N NNNNNN.N
548
549 Ir_mm NNNNNN.N NNNNNN.N
550 Dr_mm NNNNNN.N NNNNNN.N
551 Dw_mm NNNNNN.N NNNNNN.N
552# ===================================================================
553FORMAT: compact
554%%STD_HEADER%%
555
556The numbers represent relative counts per loop iteration, compared to
557p0 at 100.0%.
558Higher is better: for example, using half as many instructions gives 200%,
559while using twice as many gives 50%.
560
561Results for p1
562
563 Ir Dr Dw COND IND COND_m IND_m Ir_m1 Dr_m1 Dw_m1 Ir_mm Dr_mm Dw_mm
564 ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------
565 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
566# ===================================================================
567FORMAT: raw_compact
568%%STD_HEADER%%
569
570The numbers represent raw counts per loop iteration.
571
572Results for p0
573
574 Ir Dr Dw COND IND COND_m IND_m Ir_m1 Dr_m1 Dw_m1 Ir_mm Dr_mm Dw_mm
575 ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------
576 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
577# ===================================================================