This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Porting/bench.pl: only use used sort elems
[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)
73 ."|-)"
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 [
160 " ./perl=A ./miniperl=A",
161 "A cannot be used on 2 different perls under test\n",
162 "croak: duplicate label"
163 ],
164 [
165 "--grindargs=Boz --tests=call::sub::empty ./perl=A ./perl=B",
166 qr{Error: while executing call::sub::empty/A empty/short loop:\nunexpected code or cachegrind output:\n},
167 "croak: cachegrind output format "
168 ],
169 [
170 "--bisect=Ir",,
171 "Error: --bisect option must be of form 'field,integer,integer'\n",
172 "croak: --bisect=Ir"
173 ],
174 [
175 "--bisect=Ir,1",,
176 "Error: --bisect option must be of form 'field,integer,integer'\n",
177 "croak: --bisect=Ir,1"
178 ],
179 [
180 "--bisect=Ir,1,2,3",
181 "Error: --bisect option must be of form 'field,integer,integer'\n",
182 "croak: --bisect=Ir,1,2,3"
183 ],
184 [
185 "--bisect=Ir,1,x",
186 "Error: --bisect option must be of form 'field,integer,integer'\n",
187 "croak: --bisect=Ir,1,x"
188 ],
189 [
190 "--bisect=Ir,x,2",
191 "Error: --bisect option must be of form 'field,integer,integer'\n",
192 "croak: --bisect=Ir,x,2"
193 ],
194 [
195 "--bisect=boz,1,2",
196 "Error: unrecognised field 'boz' in --bisect option\n",
197 "croak: --bisect=boz,1,2"
198 ],
199 [
200 "--bisect=Ir,2,1",
201 "Error: --bisect min (2) must be <= max (1)\n",
202 "croak: --bisect=boz,2,1"
203 ],
204 [
205 "--read=no-such-file-boz",
206 qr/\AError: can't open 'no-such-file-boz' for reading:/,
207 "croak: non-existent --read file "
208 ],
209 [
210 "--read=t/porting/bench/badversion.json",
211 "Error: unsupported version 9999.9 in file 't/porting/bench/badversion.json' (too new)\n",
212 "croak: --read version"
213 ],
214 [
215 "",
216 "Error: nothing to do: no perls to run, no data to read.\n",
217 "croak: no input"
218 ],
219 [
220 "./perl",
221 "Error: need at least 2 perls for comparison.\n",
222 "croak: need 2 perls"
223 ],
224 [
225 "--bisect=Ir,1,2 ./perl=A ./perl=B",
226 "Error: exactly one perl executable must be specified for bisect\n",
227 "croak: --bisect, need 1 perls"
228 ],
229 [
230 "--bisect=Ir,1,2 --tests=/call/ ./perl=A",
231 "Error: only a single test may be specified with --bisect\n",
232 "croak: --bisect one test only"
233 ],
234 # note that callsub.json was created using
235 # ./perl -Ilib Porting/bench.pl --tests='/call::sub::(amp_)?empty/' \
236 # --write=t/porting/bench/callsub.json ./perl
237
238 [
239 "--read=t/porting/bench/callsub.json --write=no/such/file/boz",
240 qr{\AError: can't open 'no/such/file/boz' for writing: },
241 "croak: --write open error"
242 ],
243
244 # these ones isn't tested:
245
246 # Error: can't parse '$field' field from cachegrind output
247 # Error: while starting cachegrind subprocess for NNNN:
248)
249{
250 my ($args, $expected, $desc) = @$test;
251 $out = qx($bench_cmd $args 2>&1);
252 if (ref($expected)) {
253 like $out, $expected, $desc;
254 }
255 else {
256 is $out, $expected, $desc;
257 }
258}
259
260# ---------------------------------------------------
261# run benchmarks
262
263
6568b26d
DM
264my $resultfile1 = tempfile(); # benchmark results for 1 perl
265my $resultfile2 = tempfile(); # benchmark results for 2 perls
266
267# Run a real cachegrind session and write results to file.
268# the -j 2 is to minimally exercise its parallel facility.
269
270note("running cachegrind for 1st perl; may be slow...");
271$out = qx($bench_cmd -j 2 --write=$resultfile1 --tests=call::sub::empty $^X=p0 2>&1);
272is length($out), 0, "--write should produce no output (1 perl)";
273ok -s $resultfile1, "--write should create a non-empty results file (1 perl)";
274
275# and again with 2 perls. This is also tests the 'mix read and new new
276# perls' functionality.
277
278note("running cachegrind for 2nd perl; may be slow...");
279$out = qx($bench_cmd -j 2 --read=$resultfile1 --write=$resultfile2 $^X=p1 2>&1);
d9b91f79
DM
280is length($out), 0, "--write should produce no output (2 perls)"
281 or diag("got: $out");
6568b26d
DM
282ok -s $resultfile2, "--write should create a non-empty results file (2 perls)";
283
284# 1 perl:
285
286# read back the results in raw form
287
288$out = qx($bench_cmd --read=$resultfile1 --raw 2>&1);
289like $out, $format_qrs{raw1}, "basic cachegrind raw format; 1 perl";
290
291# and read back the results in raw compact form
292
293$out = qx($bench_cmd --read=$resultfile1 --raw --compact=0 2>&1);
294like $out, $format_qrs{raw_compact}, "basic cachegrind raw compact format; 1 perl";
295
296# 2 perls:
297
298# read back the results in relative-percent form
299
300$out = qx($bench_cmd --read=$resultfile2 2>&1);
301like $out, $format_qrs{percent2}, "basic cachegrind percent format; 2 perls";
302
303# and read back the results in raw form
304
305$out = qx($bench_cmd --read=$resultfile2 --raw 2>&1);
306like $out, $format_qrs{raw2}, "basic cachegrind raw format; 2 perls";
307
308# and read back the results in compact form
309
310$out = qx($bench_cmd --read=$resultfile2 --compact=1 2>&1);
311like $out, $format_qrs{compact}, "basic cachegrind compact format; 2 perls";
312
313
d9b91f79
DM
314# bisect
315
316note("running cachegrind bisect on 1 perl; may be slow...");
317
318# the Ir range here is intended such that the bisect will always fail
319$out = qx($bench_cmd --tests=call::sub::empty --bisect=Ir,100000,100001 $^X=p0 2>&1);
320
321is $?, 1 << 8, "--bisect should not match";
322is length($out), 0, "--bisect should produce no output"
323 or diag("got: $out");
6568b26d 324
1137c9fa 325
6568b26d
DM
326done_testing();
327
328
329# Templates for expected output formats.
330#
331# Lines starting with '#' are skipped.
332# Lines of the form 'FORMAT: foo' start and name a new template
333# All other lines are part of the template
334# Entries of the form NNNN.NN are converted into a regex of the form
335# ( \s* -? \d+\.\d\d | - )
336# i.e. it expects number with a fixed number of digits after the point,
337# or a '-'.
338# Lines of the form %%FOO%% are substituted with format 'FOO'
339
340
341__END__
342# ===================================================================
343FORMAT: STD_HEADER
344Key:
345 Ir Instruction read
346 Dr Data read
347 Dw Data write
348 COND conditional branches
349 IND indirect branches
350 _m branch predict miss
351 _m1 level 1 cache miss
352 _mm last cache (e.g. L3) miss
353 - indeterminate percentage (e.g. 1/0)
354# ===================================================================
355FORMAT: percent2
356%%STD_HEADER%%
357
358The numbers represent relative counts per loop iteration, compared to
359p0 at 100.0%.
360Higher is better: for example, using half as many instructions gives 200%,
361while using twice as many gives 50%.
362
363call::sub::empty
364function call with no args or body
365
366 p0 p1
367 ------ ------
368 Ir 100.00 NNN.NN
369 Dr 100.00 NNN.NN
370 Dw 100.00 NNN.NN
371 COND 100.00 NNN.NN
372 IND 100.00 NNN.NN
373
374COND_m 100.00 NNN.NN
375 IND_m 100.00 NNN.NN
376
377 Ir_m1 100.00 NNN.NN
378 Dr_m1 100.00 NNN.NN
379 Dw_m1 100.00 NNN.NN
380
381 Ir_mm 100.00 NNN.NN
382 Dr_mm 100.00 NNN.NN
383 Dw_mm 100.00 NNN.NN
384# ===================================================================
385FORMAT: raw1
386%%STD_HEADER%%
387
388The numbers represent raw counts per loop iteration.
389
390call::sub::empty
391function call with no args or body
392
393 p0
394 --------
395 Ir NNNNNN.N
396 Dr NNNNNN.N
397 Dw NNNNNN.N
398 COND NNNNNN.N
399 IND NNNNNN.N
400
401COND_m NNNNNN.N
402 IND_m NNNNNN.N
403
404 Ir_m1 NNNNNN.N
405 Dr_m1 NNNNNN.N
406 Dw_m1 NNNNNN.N
407
408 Ir_mm NNNNNN.N
409 Dr_mm NNNNNN.N
410 Dw_mm NNNNNN.N
411# ===================================================================
412FORMAT: raw2
413%%STD_HEADER%%
414
415The numbers represent raw counts per loop iteration.
416
417call::sub::empty
418function call with no args or body
419
420 p0 p1
421 -------- --------
422 Ir NNNNNN.N NNNNNN.N
423 Dr NNNNNN.N NNNNNN.N
424 Dw NNNNNN.N NNNNNN.N
425 COND NNNNNN.N NNNNNN.N
426 IND NNNNNN.N NNNNNN.N
427
428COND_m NNNNNN.N NNNNNN.N
429 IND_m NNNNNN.N NNNNNN.N
430
431 Ir_m1 NNNNNN.N NNNNNN.N
432 Dr_m1 NNNNNN.N NNNNNN.N
433 Dw_m1 NNNNNN.N NNNNNN.N
434
435 Ir_mm NNNNNN.N NNNNNN.N
436 Dr_mm NNNNNN.N NNNNNN.N
437 Dw_mm NNNNNN.N NNNNNN.N
438# ===================================================================
439FORMAT: compact
440%%STD_HEADER%%
441
442The numbers represent relative counts per loop iteration, compared to
443p0 at 100.0%.
444Higher is better: for example, using half as many instructions gives 200%,
445while using twice as many gives 50%.
446
447Results for p1
448
449 Ir Dr Dw COND IND COND_m IND_m Ir_m1 Dr_m1 Dw_m1 Ir_mm Dr_mm Dw_mm
450 ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------
451 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
452# ===================================================================
453FORMAT: raw_compact
454%%STD_HEADER%%
455
456The numbers represent raw counts per loop iteration.
457
458Results for p0
459
460 Ir Dr Dw COND IND COND_m IND_m Ir_m1 Dr_m1 Dw_m1 Ir_mm Dr_mm Dw_mm
461 ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------
462 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
463# ===================================================================