Benchmark-MCE
view release on metacpan or search on metacpan
lib/Benchmark/MCE.pm view on Meta::CPAN
my %stats = suite_run(\%options);
Runs the benchmark suite given the C<%options> and prints results. Returns a hash
with run stats that looks like this:
%stats = (
$bench_name_1 => {times => [ ... ], scores => [ ... ]},
...
_total => {times => [ ... ], scores => [ ... ]},
_opt => {iter => $iterations, threads => $no_threads, ...}
);
Note that the times reported will be average times per thread (or per function
call if you prefer), however the scores reported (if a reference time is supplied)
are sums across all threads. So you expect for ideal scaling 1 thread vs 2 threads
to return the same times, double the scores.
=head3 Options:
=over 4
lib/Benchmark/MCE.pm view on Meta::CPAN
=back
=item * C<threads> (Int; default 1):
Parallel benchmark threads. They are L<MCE> workers, so not 'threads' in the technical
sense. Each of the benchmarks defined will launch on each of the threads, hence the
total workload is multiplied by the number of C<threads>. Times will be averaged
across threads, while scores will be summed.
=item * C<iter> (Int; default 1):
Number of suite iterations (with min/max/avg at the end when > 1).
=item * C<include> (Regex):
Only run benchmarks whose names match regex.
=item * C<exclude> (Regex):
Skip benchmarks whose names match regex.
=item * C<filter> (CodeRef):
Custom filter callback for finer control. It receives C<($opt, $bench, $bench_def)>
and should return true to run a benchmark.
lib/Benchmark/MCE.pm view on Meta::CPAN
Do not run under L<MCE::Loop> (sets C<threads=1>, C<scale=1>).
=back
=head2 C<calc_scalability>
my %scal = calc_scalability(\%stat_single, \%stat_multi, $keep_outliers?);
Given the C<%stat_single> results of a single-threaded C<suite_run> and C<%stat_multi>
results of a multi-threaded run, will calculate, print and return the multi-thread
scalability (including averages, ranges etc for multiple iterations).
Unless C<$keep_outliers> is true, the overall scalability is an average after droping
Benchmarks that are non-scaling outliers (over 2*stdev less than the mean).
The result hash return looks like this:
%scal = (
bench_name => $bench_avg_scalability,
...
_total => $total_avg_scalability
lib/Benchmark/MCE.pm view on Meta::CPAN
. _pad(sprintf("%2.0f", $scal[-1]), 24) . "\n")
if @perf;
}
die "No bench times recorded" unless @perf;
_print(("-"x40)."\n");
my @avg1 = _min_max_avg($stats1->{_total}->{$display});
my @avg2 = _min_max_avg($stats2->{_total}->{$display});
_print(__PACKAGE__, " summary ($cnt benchmark");
_print("s") if $cnt > 1;
_print(" x$opt->{scale} scale") if $opt->{scale} > 1;
_print(", $opt->{iter} iterations") if $opt->{iter} > 1;
_print(", $opt2->{threads} threads):\n");
$opt->{f} .= "s" if $opt->{time};
my $f = $opt->{time} ? '%.3f' : '%.0f';
$f = $opt->{iter} > 1 ? "$opt->{f}\t($f - $f)" : $opt->{f};
@avg1 = $opt->{iter} > 1 ? ($avg1[2], $avg1[0], $avg1[1]) : ($avg1[2]);
@avg2 = $opt->{iter} > 1 ? ($avg2[2], $avg2[0], $avg2[1]) : ($avg2[2]);
_print(_pad("Single:").sprintf($f, @avg1)."\n");
_print(_pad("Multi:").sprintf($f, @avg2)."\n");
my @newperf = $outliers ? @perf : _drop_outliers(\@perf, -1);
my @newscal = $outliers ? @scal : _drop_outliers(\@scal, -1);
lib/Benchmark/MCE.pm view on Meta::CPAN
"%2.1f%% \t(%.0f%% - %.0f%%)", $scal[2], $scal[0], $scal[1]
)
. "\n"
);
return %scal;
}
sub _init_options {
my $opt = shift;
$opt->{iter} ||= $opt->{iterations} || 1;
$opt->{bench} ||= $opt->{benchmarks} || $opt->{extra_bench};
die "No benchmarks defined" unless $opt->{bench} && %{$opt->{bench}};
foreach my $b (keys %{$opt->{bench}}) {
if (!ref($opt->{bench}->{$b})) { # string
my $f = eval "sub { $opt->{bench}->{$b} }";
die "Error compiling benchmark '$b': $@" if $@;
$opt->{bench}->{$b} = $f;
}
$opt->{bench}->{$b} = [$opt->{bench}->{$b}]
if ref($opt->{bench}->{$b}) eq 'CODE'; # wrap coderef
lib/Benchmark/MCE.pm view on Meta::CPAN
my $r = !defined $benchmark->[1]
|| $out eq $benchmark->[1] ? 'Pass' : "Fail ($out)";
return $time, $r;
}
sub _total_stats {
my $opt = shift;
my $stats = shift;
my $display = $opt->{time} ? 'times' : 'scores';
my $title = $opt->{time} ? 'Time (sec)' : 'Score';
_print( "Aggregates ($opt->{iter} iterations"
. ($opt->{threads} > 1 ? ", $opt->{threads} threads" : "") . "):\n"
. _pad("Benchmark", 24)
. _pad("Avg $title")
. _pad("Min $title")
. _pad("Max $title"));
_print(_pad("stdev %")) if $opt->{stdev};
_print(_pad("Pass %")) unless $opt->{no_check};
_print("\n");
foreach my $bench (sort keys %{$opt->{bench}}) {
%stats2 = %stats1;
$stats2{_opt} = {%{$stats1{_opt}}};
$stats2{_opt}->{threads} = 2;
@std = capture {calc_scalability(\%stats1, \%stats2)};
like($std[0], qr/Single:\s*\d+\s*\(\d+ - \d+/, 'Min Max');
$stats1{_opt}->{iter} = 1;
@std = capture {calc_scalability(\%stats1, \%stats2)};
unlike($std[0], qr/scale/, 'No scale listed');
unlike($std[0], qr/iterations/, 'No iterations listed');
$stats1{_opt}->{iter} = 2;
$stats1{_opt}->{time} = 1;
$stats1{_opt}->{scale} = 2;
$stats2{_opt}->{scale} = 2;
@std = capture {calc_scalability(\%stats2, \%stats1)};
like($std[0], qr/scale/, 'Scale listed');
like($std[0], qr/iterations/, 'Iterations listed');
@std = capture {
suite_run({
threads => 1,
quick => 1,
iter => 2,
no_mce => 1,
include => 'DCT',
benchmarks => $bench
})
};
like($std[0], qr/2 iterations\)/, 'Aggregate');
@std = capture {
suite_run({
time => 1,
iterations => 1,
duration => 1,
no_mce => 1,
sleep => 1,
include => 'prove',
extra_bench => $bench
})
};
like($std[0], qr/Overall Time/, 'Single');
like($std[0], qr/0s of 1s/, 'Duration');
( run in 0.972 second using v1.01-cache-2.11-cpan-71847e10f99 )