File Coverage

blib/lib/Benchmark/Perl/Formance.pm
Criterion Covered Total %
statement 68 434 15.6
branch 0 116 0.0
condition 0 41 0.0
subroutine 23 60 38.3
pod 0 19 0.0
total 91 670 13.5


line stmt bran cond sub pod time code
1             package Benchmark::Perl::Formance;
2             # git description: v0.53-13-gd912390
3              
4             our $AUTHORITY = 'cpan:SCHWIGON';
5             # ABSTRACT: Perl 5 performance benchmarking framework
6             $Benchmark::Perl::Formance::VERSION = '0.54';
7 2     2   1726 use 5.008;
  2         7  
8              
9 2     2   12 use warnings;
  2         4  
  2         61  
10 2     2   10 use strict;
  2         4  
  2         41  
11              
12 2     2   8 use Config;
  2         4  
  2         73  
13 2     2   967 use Config::Perl::V;
  2         5424  
  2         120  
14 2     2   18 use Exporter;
  2         4  
  2         72  
15 2     2   1476 use Getopt::Long ":config", "no_ignore_case", "bundling";
  2         21703  
  2         10  
16 2     2   1519 use Data::Structure::Util "unbless";
  2         14427  
  2         159  
17 2     2   1023 use Time::HiRes qw(gettimeofday);
  2         2719  
  2         13  
18 2     2   1298 use Devel::Platform::Info;
  2         971  
  2         74  
19 2     2   12 use List::Util "max";
  2         5  
  2         134  
20 2     2   1871 use Data::DPath 'dpath', 'dpathi';
  2         220050  
  2         17  
21 2     2   674 use File::Find;
  2         22  
  2         144  
22 2     2   14 use Storable "fd_retrieve", "store_fd";
  2         12  
  2         98  
23 2     2   1099 use Sys::Hostname;
  2         2246  
  2         114  
24 2     2   850 use Sys::Info;
  2         15873  
  2         7  
25 2     2   1120 use FindBin qw($Bin);
  2         2137  
  2         223  
26              
27 2     2   884 use Module::Pluggable;
  2         20602  
  2         13  
28 2     2   199 use Module::Runtime qw/ require_module /;
  2         6  
  2         12  
29              
30             # comma separated list of default plugins - basically the non-troublemakers
31             my $DEFAULT_PLUGINS = join ",", qw(DPath
32             Fib
33             FibOO
34             Mem
35             MatrixReal
36             Prime
37             Rx
38             RxMicro
39             Shootout::fasta
40             Shootout::regexdna
41             Shootout::binarytrees
42             Shootout::revcomp
43             Shootout::nbody
44             Shootout::spectralnorm
45             );
46              
47             # FibMXDeclare
48             my $ALL_PLUGINS = join ",", qw(DPath
49             Fib
50             FibMoose
51             FibMouse
52             FibOO
53             FibOOSig
54             MatrixReal
55             Mem
56             P6STD
57             PerlCritic
58             Prime
59             RegexpCommonTS
60             Rx
61             RxMicro
62             RxCmp
63             Shootout::binarytrees
64             Shootout::fannkuch
65             Shootout::fasta
66             Shootout::knucleotide
67             Shootout::mandelbrot
68             Shootout::nbody
69             Shootout::pidigits
70             Shootout::regexdna
71             Shootout::revcomp
72             Shootout::spectralnorm
73             SpamAssassin
74             Threads
75             ThreadsShared
76             );
77              
78             our $scaling_script = "$Bin/benchmark-perlformance-set-stable-system";
79             our $metric_prefix = "perlformance.perl5";
80              
81             our $DEFAULT_INDENT = 0;
82              
83             my @run_plugins;
84              
85             # incrementaly interesting Perl Config keys
86             my %CONFIG_KEYS = (
87             0 => [],
88             1 => [
89             qw(perlpath
90             version
91             archname
92             archname64
93             osvers
94             usethreads
95             useithreads
96             )],
97             2 => [
98             qw(gccversion
99             gnulibc_version
100             usemymalloc
101             config_args
102             optimize
103             )],
104             3 => [qw(ccflags
105             ccname
106             cccdlflags
107             ccdlflags
108             cppflags
109             nm_so_opt
110             )],
111             4 => [qw(PERL_REVISION
112             PERL_VERSION
113             PERL_SUBVERSION
114             PERL_PATCHLEVEL
115              
116             api_revision
117             api_version
118             api_subversion
119             api_versionstring
120              
121             git_branch
122             git_commit_id
123             git_describe
124             git_uncommitted_changes
125              
126             gnulibc_version
127             dtrace
128             doublesize
129             alignbytes
130             bin_ELF
131             git_commit_date
132             version_patchlevel_string
133             d_mymalloc
134              
135             i16size
136             i16type
137             i32size
138             i32type
139             i64size
140             i64type
141             i8size
142             i8type
143              
144             longdblsize
145             longlongsize
146             longsize
147              
148             perllibs
149             ptrsize
150             quadkind
151             quadtype
152             randbits
153             )],
154             5 => [
155             sort keys %Config
156             ],
157             );
158              
159             sub new {
160 0     0 0   my ($class, %args) = @_;
161 0           bless { %args }, $class;
162             }
163              
164             sub load_all_plugins
165             {
166             map {
167 0 0         my $version = $_->[1] ? $_->[0]->VERSION : '~';
168 0           (my $name = $_->[0]) =~ s/.*::Plugin:://;
169              
170 0           $name => $version;
171             }
172 0     0 0   map { [ $_ => eval { require_module($_) } ] }
  0            
  0            
173             __PACKAGE__->plugins;
174             }
175              
176             sub print_version
177             {
178 0     0 0   my ($self) = @_;
179              
180 0 0         if ($self->{options}{verbose})
181             {
182 0           print "Benchmark::Perl::Formance version $Benchmark::Perl::Formance::VERSION\n";
183 0           print "Plugins:\n";
184 0           my %plugins = load_all_plugins;
185 0           print " (v$plugins{$_}) $_\n" foreach sort keys %plugins;
186             }
187             else
188             {
189 0           print $Benchmark::Perl::Formance::VERSION, "\n";
190             }
191             }
192              
193             sub usage
194             {
195 0     0 0   print 'benchmark-perlformance - Frontend for Benchmark::Perl::Formance
196              
197             Usage:
198              
199             $ benchmark-perlformance
200             $ benchmark-perlformance --fastmode
201             $ benchmark-perlformance --useforks
202             $ benchmark-perlformance --plugins=SpamAssassin,RegexpCommonTS,RxCmp -v
203             $ benchmark-perlformance -ccccc --indent=2
204             $ benchmark-perlformance -q
205              
206             If run directly it uses the perl in your PATH:
207              
208             $ /path/to/benchmark-perlformance
209              
210             To use another perl start it via
211              
212             $ /other/path/to/bin/perl /path/to/benchmark-perlformance
213              
214             For more details see
215              
216             man benchmark-perlformance
217             perldoc Benchmark::Perl::Formance
218              
219             ';
220             }
221              
222             sub do_disk_sync {
223 0     0 0   system("sync ; sync");
224             }
225              
226             sub prepare_stable_system
227             {
228 0     0 0   my ($self) = @_;
229              
230 0           my $orig_values;
231 0 0 0       if ($self->{options}{stabilize_cpu} and $^O eq "linux") {
232 0           $self->{orig_system_values} = qx(sudo $scaling_script lo);
233 0           do_disk_sync();
234             }
235             }
236              
237             sub restore_stable_system
238             {
239 0     0 0   my ($self, $orig_values) = @_;
240 0 0 0       if ($self->{options}{stabilize_cpu} and $^O eq "linux") {
241 0 0         if (open my $RESTORE, "|-", "sudo $scaling_script restore") {
242 0           print $RESTORE $self->{orig_system_values};
243 0           close $RESTORE;
244             }
245             }
246             }
247              
248             sub prepare_fast_system
249             {
250 0     0 0   my ($self) = @_;
251              
252 0           my $orig_values;
253 0 0 0       if ($self->{options}{stabilize_cpu} and $^O eq "linux") {
254 0           $self->{orig_system_values} = qx(sudo $scaling_script hi);
255             }
256             }
257              
258             sub _error_printing
259             {
260 0     0     my ($self, $pluginname, $error) = @_;
261              
262 0           my @errors = split qr/\n/, $error;
263 0 0         my $maxerr = ($#errors < 10) ? $#errors : 10;
264 0 0         print STDERR "# Skip plugin '$pluginname'" if $self->{options}{verbose};
265 0 0         print STDERR ":".$errors[0] if $self->{options}{verbose} > 1;
266 0 0         print STDERR join("\n# ", "", @errors[1..$maxerr]) if $self->{options}{verbose} > 2;
267 0 0         print STDERR "\n" if $self->{options}{verbose};
268             }
269              
270             sub run_plugin
271             {
272 0     0 0   my ($self, $pluginname) = @_;
273              
274 0           $pluginname =~ s,\.,::,g;
275 2     2   2149 no strict 'refs'; ## no critic
  2         4  
  2         125  
276 0 0         print STDERR "# Run $pluginname...\n" if $self->{options}{verbose} >= 2;
277 0           my $res;
278 0           eval {
279 2     2   14 use IO::Handle;
  2         5  
  2         4029  
280 0           pipe(PARENT_RDR, CHILD_WTR);
281 0           CHILD_WTR->autoflush(1);
282 0           my $pid = open(my $PLUGIN, "-|"); # implicit fork
283 0 0         if ($pid == 0) {
284             # run in child process
285 0           close PARENT_RDR;
286 0           eval "use Benchmark::Perl::Formance::Plugin::$pluginname"; ## no critic
287 0 0         if ($@) {
288 0           $self->_error_printing($pluginname, $@);
289 0           exit 0;
290             }
291 0           $0 = "benchmark-perl-formance-$pluginname";
292 0           eval {
293 0           $res = &{"Benchmark::Perl::Formance::Plugin::${pluginname}::main"}($self->{options});
  0            
294             };
295 0 0         if ($@) {
296 0           $self->_error_printing($pluginname, $@);
297 0           $res = { failed => $@ };
298             }
299 0           $res->{PLUGIN_VERSION} = ${"Benchmark::Perl::Formance::Plugin::${pluginname}::VERSION"};
  0            
300 0           store_fd($res, \*CHILD_WTR);
301 0           close CHILD_WTR;
302 0           exit 0;
303             }
304 0           close CHILD_WTR;
305 0           $res = fd_retrieve(\*PARENT_RDR);
306 0           close PARENT_RDR;
307             };
308 0 0         if ($@) {
309             $res = {
310             failed => "Plugin $pluginname failed",
311 0 0         ($self->{options}{verbose} > 3 ? ( error => $@ ) : ()),
312             }
313             }
314 0           return $res;
315             }
316              
317             # ,-----------------------------------------------------------
318             # |
319             # | That's specific to the Tapper wrapper around
320             # | Benchmark::Perl::Formance and should be replaced
321             # | with something generic
322             sub _perl_gitversion {
323 0     0     my $perlpath = "$^X";
324 0           $perlpath =~ s,/[^/]*$,,;
325 0           my $perl_gitversion = "$perlpath/perl -MConfig -e 'print \$Config{bootstrap_perl_git_changeset}";
326              
327 0 0         if (-x $perl_gitversion) {
328 0           my $gitversion = qx!$perl_gitversion! ;
329 0           chomp $gitversion;
330 0           return $gitversion;
331             }
332             }
333              
334             sub _perl_gitdescribe {
335 0     0     my $perlpath = "$^X";
336 0           $perlpath =~ s,/[^/]*$,,;
337 0           my $perl_gitdescribe = "$perlpath/perl -MConfig -e 'print \$Config{bootstrap_perl_git_describe}";
338              
339 0 0         if (-x $perl_gitdescribe) {
340 0           my $gitdescribe = qx!$perl_gitdescribe! ;
341 0           chomp $gitdescribe;
342 0           return $gitdescribe;
343             }
344             }
345              
346             sub _perl_symbolic_name {
347 0     0     my $perlpath = "$^X";
348 0           $perlpath =~ s,/[^/]*$,,;
349 0           my $perl_symbolic_name = "$perlpath/perl -MConfig -e 'print \$Config{bootstrap_perl_symbolic_name}";
350              
351 0 0         if (-x $perl_symbolic_name) {
352 0           my $executable = qx!$perl_symbolic_name! ;
353 0           chomp $executable;
354 0           return $executable;
355             }
356             }
357             # |
358             # '-----------------------------------------------------------
359              
360             sub _get_hostname {
361 0     0     my $host = "unknown-hostname";
362 0           eval { $host = hostname };
  0            
363 0 0         $host = "perl64.org" if $host eq "h1891504"; # special case for PerlFormance.Net Æsthetics
364 0           return $host;
365             }
366              
367             sub _plugin_results {
368 0     0     my ($self, $plugin, $RESULTS) = @_;
369              
370 0           my @resultkeys = split(/\./, $plugin);
371 0           my ($res) = dpath("/results/".join("/", map { qq("$_") } @resultkeys)."/Benchmark/*[0]")->match($RESULTS);
  0            
372              
373 0           return $res;
374             }
375              
376             sub _codespeed_meta {
377 0     0     my ($self, $RESULTS) = @_;
378              
379 0   0       my $codespeed_exe_suffix = $self->{options}{cs_executable_suffix} || $ENV{CODESPEED_EXE_SUFFIX} || "";
380             my $codespeed_exe = $self->{options}{cs_executable} || _perl_symbolic_name || sprintf("perl-%s.%s%s",
381             $Config{PERL_REVISION},
382             $Config{PERL_VERSION},
383 0   0       $codespeed_exe_suffix,
384             );
385 0   0       my $codespeed_project = $self->{options}{cs_project} || $ENV{CODESPEED_PROJECT} || "perl5";
386 0   0       my $codespeed_branch = $self->{options}{cs_branch} || $ENV{CODESPEED_BRANCH} || "default";
387 0   0       my $codespeed_commitid = $self->{options}{cs_commitid} || $ENV{CODESPEED_COMMITID} || $Config{git_commit_id} || _perl_gitversion || "no-commit";
388 0   0       my $codespeed_environment = $self->{options}{cs_environment} || $ENV{CODESPEED_ENVIRONMENT} || _get_hostname || "no-env";
389 0           my %codespeed_meta = (
390             executable => $codespeed_exe,
391             project => $codespeed_project,
392             branch => $codespeed_branch,
393             commitid => $codespeed_commitid,
394             environment => $codespeed_environment,
395             );
396              
397 0           return %codespeed_meta;
398             }
399              
400             sub _get_bootstrap_perl_meta {
401 0     0     my ($self) = @_;
402              
403 0           return map { ("$_" => $Config{$_}) } grep { /^bootstrap_perl/ } keys %Config;
  0            
  0            
404             }
405              
406             # Convert value:
407             # - Perlish undef --> 0
408             # - String "define" --> 1
409             # - everything else keep the same
410             sub _booleanize_define {
411 0     0     my ($value) = @_;
412              
413 0 0         if (not defined $value) {
    0          
414 0           return 0;
415             } elsif ($value eq "define") {
416 0           return 1;
417             } else {
418 0           return $value;
419             }
420             }
421              
422             sub _taint_available {
423 0     0     require Scalar::Util;
424 0           require Cwd;
425 0           Scalar::Util::tainted(Cwd::getcwd());
426             }
427              
428             sub _get_perl_config_notaintsupport {
429 0     0     my ($self) = @_;
430              
431 0           my $config_args = $Config{config_args};
432 0           my $notaintsupport = 0; # standard
433 0 0 0       if ($config_args =~ /-DNO_TAINT_SUPPORT\b/ and not _taint_available()) {
434 0           $notaintsupport = 1;
435             }
436 0           return $notaintsupport;
437             }
438              
439             sub _get_perl_config {
440 0     0     my ($self) = @_;
441              
442 0           my @cfgkeys;
443 0           my $showconfig = 4;
444 0           push @cfgkeys, @{$CONFIG_KEYS{$_}} foreach 1..$showconfig;
  0            
445 0           my %perlconfig = map { ("perlconfig_$_" => $Config{$_}) } @cfgkeys;
  0            
446 0           $perlconfig{perlconfig_derived_notaintsupport} = $self->_get_perl_config_notaintsupport();
447 0           return %perlconfig;
448             }
449              
450             sub _get_perl_config_v {
451 0     0     my ($self) = @_;
452              
453             # only when ultimate verbose config requested
454 0 0         return unless $self->{options}{showconfig} >= 5;
455              
456 0           my $config_v_myconfig = Config::Perl::V::myconfig ();
457 0           my @config_v_keys = sort keys %$config_v_myconfig;
458              
459             # --- flat configs ---
460 0           my $prefix = "perlconfigv";
461 0           my %perlconfigv = ();
462             my %focus = (
463             derived => [ qw( Off_t uname) ],
464             build => [ qw( osname stamp ) ],
465 0           environment => [ keys %{$config_v_myconfig->{environment}} ], # all
  0            
466             );
467 0           foreach my $subcfg (keys %focus) {
468 0           foreach my $k (@{$focus{$subcfg}}) {
  0            
469 0           $perlconfigv{join("_", $prefix, $subcfg, $k)} = $config_v_myconfig->{$subcfg}{$k};
470             }
471             }
472              
473             # --- nested configs ---
474              
475             # build options
476 0           my @buildoptionkeys = keys %{$config_v_myconfig->{build}{options}};
  0            
477 0           foreach my $k (keys %focus) {
478 0           $perlconfigv{join("_", $prefix, "build", "options", $k)} = $config_v_myconfig->{build}{options}{$k};
479             }
480              
481 0           return %perlconfigv;
482             }
483              
484             sub _get_perlformance_config {
485 0     0     my ($self) = @_;
486              
487             # only easy printable data (i.e., no "D" hash)
488 0           my @config_keys = (qw(stabilize_cpu
489             fastmode
490             useforks
491             plugins
492             ));
493              
494 0 0         return map { $self->{options}{$_} ? ("perlformance_$_" => $self->{options}{$_}) : () } @config_keys;
  0            
495             }
496              
497             sub _get_perlformance_env
498             {
499 0     0     my ($self) = @_;
500              
501             # environment variables matching /^PERLFORMANCE_/
502 0           my @config_keys = grep { $ENV{$_} ne '' } grep /^PERLFORMANCE_/, keys %ENV;
  0            
503              
504 0           return map { lc("env_$_") => $ENV{$_} } @config_keys;
  0            
505             }
506              
507             sub _get_platforminfo {
508 0     0     my ($self) = @_;
509              
510 0           my $get_info = Devel::Platform::Info->new->get_info;
511 0           delete $get_info->{source}; # this currently breaks the simplified YAMLish
512 0           return %$get_info;
513             }
514              
515             sub _get_sysinfo {
516 0     0     my ($self) = @_;
517              
518 0           my %sysinfo = ();
519 0           my $prefix = "sysinfo";
520 0           my $cpu = (Sys::Info->new->device("CPU")->identify)[0];
521 0           $sysinfo{"${prefix}_hostname"} = _get_hostname;
522 0           $sysinfo{join("_", $prefix, "cpu", $_)} = $cpu->{$_} foreach qw(name
523             family
524             model
525             stepping
526             architecture
527             number_of_cores
528             number_of_logical_processors
529             architecture
530             manufacturer
531             );
532 0           $sysinfo{join("_", $prefix, "cpu", "l2_cache", "max_cache_size")} = $cpu->{L2_cache}{max_cache_size};
533 0           return %sysinfo;
534             }
535              
536             sub augment_results_with_meta {
537 0     0 0   my ($self, $NAME_KEY, $VALUE_KEY, $META, $RESULTS) = @_;
538              
539 0           my @run_plugins = $self->find_interesting_result_paths($RESULTS);
540 0           my @new_entries = ();
541 0           foreach my $plugin (sort @run_plugins) {
542 2     2   19 no strict 'refs'; ## no critic
  2         5  
  2         3467  
543 0           my $res = $self->_plugin_results($plugin, $RESULTS);
544 0 0         my $benchmark = join ".", $metric_prefix, ($self->{options}{fastmode} ? "$plugin(F)" : $plugin);
545 0   0       push @new_entries, {
546             %$META,
547             # metric name and value at last position to override
548             $NAME_KEY => $benchmark,
549             $VALUE_KEY => ($res || 0),
550             };
551             }
552 0           return \@new_entries;
553             }
554              
555             sub generate_codespeed_data
556             {
557 0     0 0   my ($self, $RESULTS) = @_;
558              
559 0           my %META = _codespeed_meta();
560 0           return $self->augment_results_with_meta("benchmark", "result_value", \%META, $RESULTS);
561             }
562              
563             sub generate_BenchmarkAnythingData_data
564             {
565 0     0 0   my ($self, $RESULTS, $codespeed) = @_;
566              
567             # share a common dataset with Codespeed, yet prefix it
568 0 0         my %codespeed_meta = $codespeed ? _codespeed_meta : ();
569 0           my %prefixed_codespeed_meta = map { ("codespeed_$_" => $codespeed_meta{$_}) } keys %codespeed_meta;
  0            
570              
571 0           my %platforminfo = $self->_get_platforminfo;
572 0           my %prefixed_platforminfo = map { ("platforminfo_$_" => $platforminfo{$_}) } keys %platforminfo;
  0            
573              
574 0           my %META = (
575             %prefixed_platforminfo,
576             %prefixed_codespeed_meta,
577             $self->_get_bootstrap_perl_meta,
578             $self->_get_perl_config,
579             $self->_get_perl_config_v,
580             $self->_get_sysinfo,
581             $self->_get_perlformance_config,
582             $self->_get_perlformance_env,
583             );
584 0           return $self->augment_results_with_meta("NAME", "VALUE", \%META, $RESULTS);
585             }
586              
587             sub run {
588 0     0 0   my ($self) = @_;
589              
590 0           my $help = 0;
591 0           my $showconfig = 0;
592 0           my $outstyle = "summary";
593 0           my $outfile = "";
594 0           my $platforminfo = 0;
595 0           my $codespeed = 0;
596 0           my $tap = 0;
597 0           my $tap_plan = 0;
598 0           my $tap_headers = 0;
599 0           my $benchmarkanything = 0;
600 0           my $benchmarkanything_report = 0;
601 0           my $cs_executable_suffix = "";
602 0           my $cs_executable = "";
603 0           my $cs_project = "";
604 0           my $cs_branch = "";
605 0           my $cs_commitid = "";
606 0           my $cs_environment = "";
607 0           my $verbose = 0;
608 0           my $version = 0;
609 0           my $fastmode = 0;
610 0           my $useforks = 0;
611 0           my $quiet = 0;
612 0           my $stabilize_cpu = 0;
613 0           my $plugins = $DEFAULT_PLUGINS;
614 0           my $indent = $DEFAULT_INDENT;
615 0           my $tapdescription = "";
616 0           my $D = {};
617              
618             # get options
619 0           my $ok = GetOptions (
620             "help|h" => \$help,
621             "quiet|q" => \$quiet,
622             "indent=i" => \$indent,
623             "plugins=s" => \$plugins,
624             "verbose|v+" => \$verbose,
625             "outstyle=s" => \$outstyle,
626             "outfile=s" => \$outfile,
627             "fastmode" => \$fastmode,
628             "version" => \$version,
629             "useforks" => \$useforks,
630             "stabilize-cpu" => \$stabilize_cpu,
631             "showconfig|c+" => \$showconfig,
632             "platforminfo|p" => \$platforminfo,
633             "codespeed" => \$codespeed,
634             "tap" => \$tap,
635             "tap-plan" => \$tap_plan,
636             "tap-headers" => \$tap_headers,
637             "benchmarkanything" => \$benchmarkanything,
638             "benchmarkanything-report" => \$benchmarkanything_report,
639             "cs-executable-suffix=s" => \$cs_executable_suffix,
640             "cs-executable=s" => \$cs_executable,
641             "cs-project=s" => \$cs_project,
642             "cs-branch=s" => \$cs_branch,
643             "cs-commitid=s" => \$cs_commitid,
644             "cs-environment=s" => \$cs_environment,
645             "tapdescription=s" => \$tapdescription,
646             "D=s%" => \$D,
647             );
648              
649             # special meta options - order matters!
650 0 0 0       if ($tap or $tap_plan) {
651 0           $tapdescription = 'perlformance results';
652 0           $outstyle = 'yamlish';
653 0           $indent = 2;
654 0           $platforminfo = 1;
655 0           $showconfig = 4;
656             }
657 0 0         $benchmarkanything = 1 if $benchmarkanything_report;
658 0 0         $platforminfo = 1 if $benchmarkanything; # -p
659 0 0         $showconfig = 4 if $benchmarkanything; # -cccc
660 0 0 0       $outstyle = 'json' if $benchmarkanything and $outstyle !~ /^(json|yaml|yamlish)$/;
661 0 0         $outstyle = 'json' if $benchmarkanything_report;
662              
663             # fill options
664             $self->{options} = {
665 0           help => $help,
666             quiet => $quiet,
667             verbose => $verbose,
668             outfile => $outfile,
669             outstyle => $outstyle,
670             fastmode => $fastmode,
671             useforks => $useforks,
672             stabilize_cpu => $stabilize_cpu,
673             showconfig => $showconfig,
674             platforminfo => $platforminfo,
675             codespeed => $codespeed,
676             tap => $tap,
677             tap_plan => $tap_plan,
678             tap_headers => $tap_headers,
679             benchmarkanything => $benchmarkanything,
680             benchmarkanything_report => $benchmarkanything_report,
681             cs_executable_suffix => $cs_executable_suffix,
682             cs_executable => $cs_executable,
683             cs_project => $cs_project,
684             cs_branch => $cs_branch,
685             cs_commitid => $cs_commitid,
686             cs_environment => $cs_environment,
687             plugins => $plugins,
688             tapdescription => $tapdescription,
689             indent => $indent,
690             D => $D,
691             };
692              
693 0 0         do { $self->print_version; exit 0 } if $version;
  0            
  0            
694 0 0         do { usage; exit 0 } if $help;
  0            
  0            
695 0 0         do { usage; exit -1 } if not $ok;
  0            
  0            
696              
697             # use forks if requested
698 0 0         if ($useforks) {
699 0           eval "use forks"; ## no critic
700 0 0         $useforks = 0 if $@;
701 0 0         print STDERR "# use forks " . ($@ ? "failed" : "") . "\n" if $verbose;
    0          
702             }
703              
704             # static list because dynamic require influences runtimes
705 0 0         $plugins = $ALL_PLUGINS if $plugins eq "ALL";
706              
707             # run plugins
708 0           my $before = gettimeofday();
709 0           my %RESULTS;
710 0           my @plugins = grep /\w/, split '\s*,\s*', $plugins;
711              
712 0           $self->prepare_stable_system;
713 0           foreach (@plugins)
714             {
715 0           my @resultkeys = split(qr/::|\./, $_);
716 0           my $res = $self->run_plugin($_);
717 0           eval "\$RESULTS{results}{".join("}{", @resultkeys)."} = \$res"; ## no critic
718             }
719 0           $self->prepare_fast_system; # simply set to max, as restore_stable_system() is no reliable approach anyway
720              
721 0           my $after = gettimeofday();
722 0           $RESULTS{perlformance}{overall_runtime} = $after - $before;
723 0           $RESULTS{perlformance}{config}{fastmode} = $fastmode;
724 0           $RESULTS{perlformance}{config}{use_forks} = $useforks;
725              
726             # Perl Config
727 0 0         if ($showconfig)
728             {
729             # Config
730 0           my @cfgkeys;
731 0           push @cfgkeys, @{$CONFIG_KEYS{$_}} foreach 1..$showconfig;
  0            
732             $RESULTS{perl_config} =
733             {
734 0           map { $_ => $Config{$_} } sort @cfgkeys
  0            
735             };
736              
737             # Config::Perl::V
738 0           $RESULTS{perl_config_v} = Config::Perl::V::myconfig;
739             }
740              
741             # Perl Config
742 0 0         if ($platforminfo)
743             {
744 0           $RESULTS{platform_info} = { $self->_get_platforminfo };
745             }
746              
747             # Codespeed data blocks
748 0 0         if ($codespeed)
749             {
750 0           $RESULTS{codespeed} = $self->generate_codespeed_data(\%RESULTS);
751             }
752              
753             # TAP or BenchmarkAnythingData blocks
754 0 0 0       if ($tap or $tap_plan or $benchmarkanything)
      0        
755             {
756 0           $RESULTS{BenchmarkAnythingData} = $self->generate_BenchmarkAnythingData_data(\%RESULTS, $codespeed);
757             }
758              
759 0           unbless (\%RESULTS);
760 0           return \%RESULTS;
761             }
762              
763             sub print_outstyle_yaml
764             {
765 0     0 0   my ($self, $RESULTS) = @_;
766              
767 0           require YAML;
768 0           return YAML::Dump($RESULTS);
769             }
770              
771             sub print_outstyle_json
772             {
773 0     0 0   my ($self, $RESULTS) = @_;
774              
775 0           require JSON;
776 0           return JSON->new->allow_nonref->pretty->encode( $RESULTS );
777             }
778              
779             sub print_outstyle_yamlish
780             {
781 0     0 0   my ($self, $RESULTS) = @_;
782              
783 0           require Data::YAML::Writer;
784              
785 0           my $output = '';
786 0           my $indent = $self->{options}{indent};
787 0           my $yw = Data::YAML::Writer->new;
788 0     0     $yw->write($RESULTS, sub { $output .= shift()."\n" });
  0            
789 0           $output =~ s/^/" "x$indent/emsg; # indent
  0            
790              
791 0           my $tapdescription = $self->{options}{tapdescription};
792 0 0         $output = "ok $tapdescription\n".$output if $tapdescription;
793 0           return $output;
794             }
795              
796             sub find_interesting_result_paths
797             {
798 0     0 0   my ($self, $RESULTS) = @_;
799              
800 0           my @all_keys = ();
801              
802 0           my $benchmarks = dpathi($RESULTS)->isearch("//Benchmark");
803              
804 0           while ($benchmarks->isnt_exhausted) {
805 0           my @keys;
806 0           my $benchmark = $benchmarks->value;
807 0           my $ancestors = $benchmark->isearch ("/::ancestor");
808              
809 0           while ($ancestors->isnt_exhausted) {
810 0           my $key = $ancestors->value->first_point->{attrs}{key};
811 0 0         push @keys, $key if defined $key;
812             }
813 0           pop @keys;
814 0           push @all_keys, join(".", reverse @keys);
815             }
816 0           return @all_keys;
817             }
818              
819             sub print_outstyle_summary
820             {
821 0     0 0   my ($self, $RESULTS) = @_;
822              
823 0           my $output = '';
824              
825 0           my @run_plugins = $self->find_interesting_result_paths($RESULTS);
826 0           my $len = max map { length } @run_plugins;
  0            
827 0           $len += 1+length($metric_prefix);
828              
829 0           foreach (sort @run_plugins) {
830 2     2   21 no strict 'refs'; ## no critic
  2         5  
  2         1435  
831 0           my $res = $self->_plugin_results($_, $RESULTS);
832 0   0       $output .= sprintf("%-${len}s : %f\n", join(".", $metric_prefix, $_), ($res || 0));
833             }
834 0           return $output;
835             }
836              
837             sub print_results
838             {
839 0     0 0   my ($self, $RESULTS) = @_;
840 0 0         return if $self->{options}{quiet};
841              
842 0           my $outstyle = lc $self->{options}{outstyle};
843 0 0         $outstyle = "summary" unless $outstyle =~ qr/^(summary|yaml|yamlish|json)$/;
844 0           my $sub = "print_outstyle_$outstyle";
845              
846 0           my $output = $self->$sub($RESULTS);
847              
848             # tap
849 0           my $tap_plan = lc $self->{options}{tap_plan};
850 0           my $tap_headers = lc $self->{options}{tap_headers};
851 0           my $lead_tap = '';
852 0 0         $lead_tap .= "1..$tap_plan\n" if $tap_plan;
853 0 0         if ($tap_headers) {
854 0           $lead_tap .= "# Test-suite-name: benchmark-perlformance\n";
855 0           $lead_tap .= "# Test-machine-name: "._get_hostname."\n";
856             }
857              
858 0           $output = $lead_tap.$output;
859              
860 0 0         if (my $outfile = $self->{options}{outfile})
    0          
861             {
862 0 0         open my $OUTFILE, ">", $outfile or do {
863 0           warn "Can not open $outfile. Printing to STDOUT.\n";
864 0           print $output;
865             };
866 0           print $OUTFILE $output;
867 0           close $OUTFILE;
868             }
869             elsif ($self->{options}{benchmarkanything_report})
870             {
871 0           my $ba_reporter;
872              
873 0           eval {
874 0           require BenchmarkAnything::Reporter;
875 0           $ba_reporter = BenchmarkAnything::Reporter->new(verbose => $self->{options}{verbose});
876 0           $ba_reporter->report({BenchmarkAnythingData => $RESULTS->{BenchmarkAnythingData}});
877             };
878 0 0         if ($@)
879             {
880 0           print STDERR "# Could not add results to storage: $@\n";
881              
882 0           require JSON;
883 0           require File::Path;
884 0           require File::Temp;
885 0           require File::Basename;
886              
887 0           my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
888              
889 0           my $result_dir = File::Basename::dirname($ba_reporter->{config}{cfgfile});
890 0 0         if (! -w $result_dir) {
891 0           require File::HomeDir;
892 0           $result_dir = File::HomeDir->my_home;
893             }
894 0 0         if (! -w $result_dir) {
895 0           require File::Temp;
896 0           $result_dir = tempdir(CLEANUP => 0);
897             }
898              
899 0           my $timestamp1 = sprintf("%04d-%02d-%02d", 1900+$year, $mon, $mday);
900 0           my $timestamp2 = sprintf("%02d-%02d-%02d", $hour, $min, $sec);
901 0           my $result_path = "$result_dir/unreported_results/$timestamp1";
902              
903 0           File::Path::make_path($result_path);
904              
905 0           my ($FH, $result_file) = File::Temp::tempfile ("$timestamp2-XXXX", DIR => $result_path, SUFFIX => ".json");
906 0           print STDERR "# Writing them to file: $result_file\n";
907 0           print $FH JSON->new->allow_nonref->pretty->encode({BenchmarkAnythingData => $RESULTS->{BenchmarkAnythingData}});
908             }
909             }
910             else
911             {
912 0           print $output;
913             }
914             }
915              
916             1;
917              
918             __END__
919              
920             =pod
921              
922             =encoding UTF-8
923              
924             =head1 NAME
925              
926             Benchmark::Perl::Formance - Perl 5 performance benchmarking framework
927              
928             =head1 ABOUT
929              
930             This benchmark suite tries to run some stressful programs and outputs
931             values that you can compare against other runs of this suite,
932             e.g. with other versions of Perl, modified compile parameter, or
933             another set of dependent libraries.
934              
935             =head1 BUGS
936              
937             =head2 No invariant dependencies
938              
939             This distribution only contains the programs to run the tests and
940             according data. It uses a lot of libs from CPAN with all their
941             dependencies but it does not contain invariant versions of those used
942             dependency libs.
943              
944             If total invariance is important to you, you are responsible to
945             provide that invariant environment by yourself. You could, for
946             instance, create a local CPAN mirror with CPAN::Mini and never upgrade
947             it. Then use that mirror for all your installations of Benchmark::Perl::Formance.
948              
949             On the other side this could be used to track the performance of your
950             installation over time by continuously upgrading from CPAN.
951              
952             =head2 It is not scientific
953              
954             The benchmarks are basically just a collection of already existing
955             interesting things like large test suites found on CPAN or just
956             starting long running tasks that seem to stress perl features. It does
957             not really guarantee accuracy of only raw Perl features, i.e., it does
958             not care for underlying I/O speed and does not preallocate ressources
959             from the OS before using them, etc.
960              
961             This is basically because I just wanted to start, even without
962             knowledge about "real" benchmark science.
963              
964             Anyway, feel free to implement "real" benchmark ideas and send me
965             patches.
966              
967             =head1 AUTHOR
968              
969             Steffen Schwigon <ss5@renormalist.net>
970              
971             =head1 COPYRIGHT AND LICENSE
972              
973             This software is copyright (c) 2021 by Steffen Schwigon.
974              
975             This is free software; you can redistribute it and/or modify it under
976             the same terms as the Perl 5 programming language system itself.
977              
978             =cut