File Coverage

blib/lib/Benchmark/Perl/Formance.pm
Criterion Covered Total %
statement 65 408 15.9
branch 0 106 0.0
condition 0 35 0.0
subroutine 22 57 38.6
pod 0 19 0.0
total 87 625 13.9


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