NAME
Benchmark::MCE - Perl multi-core benchmarking framework
SYNOPSIS
use Benchmark::MCE;
# Run 2 benchmarks (custom functions) and time them on a single core:
my %stat_single = suite_run({
threads => 1,
bench => {
Bench1 => sub { ...code1... },
Bench2 => '...code2...' # String is also fine
}
);
# Run each across multiple cores.
# Use the extended (arrayref) definition to check for correctness of output.
my %stat_multi = suite_run({
threads => system_identity(1), # Workers count equal to system logical cores
bench => {
Bench1 => [\&code1, $expected_output1],
Bench2 => [\&code2, $expected_output2],
}
);
# Calculate the multi/single core scalability
my %scal = calc_scalability(\%stat_single, \%stat_multi);
DESCRIPTION
A benchmarking framework originally designed for the Benchmark::DKbench multi-core CPU benchmarking suite. Released as a stand-alone to be used for custom benchmarks of any type, as well as other kinds of stress-testing, throughput testing etc.
You define custom functions (usually with randomized workloads) that can be run on any number of parallel workers, using the low-overhead Many-Core Engine (MCE).
FUNCTIONS
system_identity
my $cores = system_identity($quiet?);
Prints out software/hardware configuration and returns the number of logical cores detected using System::CPU.
Any argument will suppress printout and will only return the number of cores.
suite_run
my %stats = suite_run(\%options);
Runs the benchmark suite given the %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.
Options:
bench
(HashRef) required: A hashref with keys being your unique custom benchmark names and values being arrays:name => [ $coderef, $expected?, $ref_time?, $quick_arg?, $normal_arg? ]
where:
$coderef
required: Reference to your benchmark function. See "BENCHMARK FUNCTIONS" for more details.$expected
: Expected output of the benchmark function on successful run (for PASS/FAIL - PASS will be always assumed is parameter is undefined).$ref_time
: Reference time in seconds for score of 1000.$quick_arg
: Argument to pass to the benchmark function inquick
mode (for workload scaling).$normal_arg
: Argument to pass to the benchmark function in normal mode (for workload scaling).
threads
(Int; default 1): Parallel benchmark threads. They are 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 ofthreads
. Times will be averaged across threads, while scores will be summed.iter
(Int; default 1): Number of suite iterations (with min/max/avg at the end when > 1).include
(Regex): Only run benchmarks whose names match regex.exclude
(Regex): Skip benchmarks whose names match regex.time
(Bool): Report time (sec) instead of score. Set to true byquick
or if at least one benchmark has no reference time declared. Otherwise score output is the default.quick
(Bool; default 0): Use each benchmark's quick argument and implytime=1
.scale
(Int; default 1): Scale the bench workload (number of calls of the benchmark functions) by x times. Forced to 1 withquick
orno_mce
.stdev
(Bool; default 0): Show relative standard deviation (foriter
> 1).sleep
(Int; default 0): Number of seconds to sleep after each benchmark run.duration
(Int, seconds): Minimum duration in seconds for suite run (overridesiter
).srand
(Int; default 1): Define a fixed seed to keep runs reproducible when your benchmark functions userand
. The seed will be passed tosrand
before each call to a benchmark function. Set to 0 to skip rand seeding.no_pass
(Bool; default 0): Do check for Pass/Fail even if reference output is defined.no_mce
(Bool; default 0): Do not run under MCE::Loop (setsthreads=1
,scale=1
).
calc_scalability
my %scal = calc_scalability(\%stat_single, \%stat_multi, $keep_outliers?);
Given the %stat_single
results of a single-threaded suite_run
and %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 $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
);
suite_calc
my ($stats, $stats_multi, $scal) = suite_calc(\%suite_run_options, , $keep_outliers?);
Convenience function that combines 3 calls, suite_run with threads=>1
, suite_run with threads=>system_identity(1)
and calc_scalability with the results of those two, returning hashrefs with the results of all three calls.
For single-core systems (or when system_identity(1)
does not return > 1) only $stats
will be returned.
BENCHMARK FUNCTIONS
The benchmark functions will be called with two parameters that you can choose to take advantage of. The first one is what you define as either the $quick_arg
or $normal_arg
, with the intention being to have a way to run a quick
mode that lets you test with smaller workloads. The second argument will be an integer that's the chunk number from MCE::Loop - it will be 1 for the call on the first thread, 2 from the second thread etc, so your function may track which worker/chunk is running.
The function may return a string, usually a checksum, that will be checked against the (optional) $expected
parameter to show a Pass/Fail (useful for verifying correctness, stress testing, etc.).
Example:
use Benchmark::MCE;
use Math::Trig qw/:great_circle :pi/;
sub great_circle {
my $size = shift || 1; # Optionally have an argument that scales the workload
my $chunk = shift; # Optionally use the chunk number
my $dist = 0;
$dist +=
great_circle_distance(rand(pi), rand(2 * pi), rand(pi), rand(2 * pi))
for 1 .. $size;
return $dist; # Returning a value is optional for the Pass/Fail functionality
}
my %stats = suite_run({
bench => { 'Math::Trig' => # A unique name for the benchmark
[
\&great_circle, # Reference to bench function
'3144042.81433949', # Reference output - determines Pass/Fail (optional)
5.5, # Seconds to complete in normal mode for score = 1000 (optional)
1000000, # Argument to pass for quick mode (optional)
5000000 # Argument to pass for normal mode (optional)
]},
}
);
STDOUT / QUIET MODE
Normally function calls will print results to STDOUT
as well as return them. You can suppress STDOUT by setting:
$Benchmark::MCE::QUIET = 1;
NOTES
The framework uses a monotonic timer for non-Windows systems with at least v1.9764 of Time::HiRes
($Benchmark::MCE::MONO_CLOCK
will be true).
AUTHOR
Dimitrios Kechagias, <dkechag at cpan.org>
BUGS
Please report any bugs or feature requests on GitHub.
GIT
https://github.com/SpareRoom/Benchmark-MCE
LICENSE AND COPYRIGHT
Copyright (c) 2021-2025 Dimitrios Kechagias. Copyright (c) 2025 SpareRoom.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.