-
Notifications
You must be signed in to change notification settings - Fork 1
Recipe CLI Benchmark
A drop-in bench.php you can place next to a workload and run with php bench.php. Prints elapsed time, memory delta, and peak memory in a one-shot summary at the end.
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use InitPHP\PerformanceMeter\PerformanceMeter;
PerformanceMeter::setPointer('boot');
// --- workload -----------------------------------------------------------
$rows = [];
for ($i = 0; $i < 100_000; $i++) {
$rows[] = ['id' => $i, 'hash' => hash('sha256', (string) $i)];
}
// ------------------------------------------------------------------------
PerformanceMeter::setPointer('done');
printf(
"rows=%d elapsed=%.4fs memory=%s peak=%s\n",
count($rows),
PerformanceMeter::elapsedTime('boot', 'done'),
PerformanceMeter::memoryUsage('boot', 'done'),
PerformanceMeter::peakMemoryUsage(),
);Sample output:
rows=100000 elapsed=0.1843s memory=19.84MB peak=21.12MB
-
Two checkpoints, not three.
'boot'and'done'bracket the entire workload. If you want sub-phase breakdowns, add more checkpoints — see Loop Profiling below. -
peakMemoryUsage()does not need checkpoints. It is a single, stateless call that reads PHP's high-water mark. Useful for spotting transient allocations that come and go before you can measure them with a delta. -
One
printfline. Easy togrep, easy to feed into shell pipelines, easy to compare between runs.
If the workload has distinct phases you want to measure separately:
PerformanceMeter::setPointer('boot');
PerformanceMeter::setPointer('load:start');
$rows = load_rows();
PerformanceMeter::setPointer('load:end');
PerformanceMeter::setPointer('transform:start');
$transformed = array_map($transform, $rows);
PerformanceMeter::setPointer('transform:end');
PerformanceMeter::setPointer('write:start');
write_rows($transformed);
PerformanceMeter::setPointer('write:end');
PerformanceMeter::setPointer('done');
printf(
"load=%ss transform=%ss write=%ss total=%ss peak=%s\n",
PerformanceMeter::elapsedTime('load:start', 'load:end'),
PerformanceMeter::elapsedTime('transform:start', 'transform:end'),
PerformanceMeter::elapsedTime('write:start', 'write:end'),
PerformanceMeter::elapsedTime('boot', 'done'),
PerformanceMeter::peakMemoryUsage(),
);The total line is not just load + transform + write — it also includes anything that happened between phases (boot overhead, printf calls, etc.). Both numbers are useful.
A single-shot measurement is noisy. To get a more stable number, wrap the workload in an outer loop and divide:
$iterations = 10;
PerformanceMeter::setPointer('s');
for ($i = 0; $i < $iterations; $i++) {
workload();
}
PerformanceMeter::setPointer('e');
printf(
"%d iterations: total=%ss per-iteration=%ss\n",
$iterations,
PerformanceMeter::elapsedTime('s', 'e', 6),
PerformanceMeter::elapsedTime('s', 'e', 6) / $iterations,
);For statistical rigour (warm-up runs, outlier rejection, confidence intervals), reach for phpbench/phpbench instead. PerformanceMeter is best for "is this twice as fast or twice as slow?", not "is this 3 % faster?".
Useful as a coarse smoke test in CI: fail the build if the workload ever exceeds a budget.
PerformanceMeter::setPointer('s');
workload();
PerformanceMeter::setPointer('e');
$elapsed = PerformanceMeter::elapsedTime('s', 'e');
$budget = 1.0; // seconds
if ($elapsed > $budget) {
fwrite(STDERR, sprintf("FAIL: workload took %.4fs, budget was %.2fs\n", $elapsed, $budget));
exit(1);
}
fwrite(STDOUT, sprintf("OK: workload took %.4fs (budget %.2fs)\n", $elapsed, $budget));
exit(0);Pin the budget loose enough that CI runner variability does not produce false positives — generally 2 × to 3 × the median local time is a reasonable starting point.
- Cron Job Timing Log — the same idea applied to scheduled jobs.
- A/B Comparison — comparing two candidate implementations.
-
Use Cases & Comparison — when to graduate from this script to
phpbench.
initphp/performance-meter · MIT License · part of the InitPHP family
Source · Issues · Discussions · Packagist · Changelog · Contributing · Security Policy
Getting Started
Reference
Recipes
Migration & Help