Skip to content

Instantly share code, notes, and snippets.

@marxin
Created March 5, 2026 15:17
Show Gist options
  • Select an option

  • Save marxin/f266bd9cea0fa91beaea05ca94466bb1 to your computer and use it in GitHub Desktop.

Select an option

Save marxin/f266bd9cea0fa91beaea05ca94466bb1 to your computer and use it in GitHub Desktop.
<?php
declare(strict_types=1);
final class WPBenchmarkRunner
{
/** @var float max wall-clock runtime (seconds) before early stop */
private float $maximum_execution_time;
/** @var int return code when max runtime is reached */
private int $max_time_reached_return_code;
/** @var float benchmark start timestamp */
private float $start_time = 0.0;
public function __construct(float $maximumExecutionTime = 300.0, int $maxTimeReachedReturnCode = -1)
{
$this->maximum_execution_time = $maximumExecutionTime;
$this->max_time_reached_return_code = $maxTimeReachedReturnCode;
}
public function run(string $function, ?int $parameter = null)
{
if (!method_exists($this, $function)) {
throw new InvalidArgumentException("Unknown benchmark function: {$function}");
}
$this->start_time = microtime(true);
if ($parameter === null) {
return $this->{$function}();
}
return $this->{$function}($parameter);
}
private function hasExceededExecutionTime(): bool
{
return (microtime(true) - $this->start_time) > $this->maximum_execution_time;
}
private function random_string(int $len = 10): string
{
$avail_chars = '1234567890ABCDEFGHJIKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
$char_len = strlen($avail_chars);
$rand_string = '';
for ($sid_n = 0; $sid_n < $len; $sid_n++) {
$rand_string .= substr($avail_chars, rand(0, $char_len - 1), 1);
}
return $rand_string;
}
public function test_cpu_randbytes(int $s_max = 80000)
{
$a = null;
for ($i = 0; $i < 150; $i++) {
$a .= $this->random_string(10240);
}
$ahex = bin2hex($a);
$ahex_capital = '';
$ahex_max_index = max(strlen($ahex) - 1, 0);
$j_max = (int) ($s_max / 10000);
$i_max = (int) ($s_max / max($j_max, 1));
for ($j = 0; $j < $j_max; $j++) {
for ($i = 0; $i < $i_max; $i++) {
$ahex_capital .= strtoupper($ahex[rand(0, $ahex_max_index)]);
md5($ahex_capital);
if (strlen($ahex_capital) > 10240) {
$ahex_capital = '';
}
}
if ($this->hasExceededExecutionTime()) {
return $this->max_time_reached_return_code;
}
}
return true;
}
public function test_cpu_regex(int $i_max = 10, int $j_max = 30, int $s_length = 40480)
{
$data = [];
$mbEregReplaceAvailable = function_exists('mb_eregi_replace');
$mbStrlenAvailable = function_exists('mb_strlen');
for ($i = 0; $i < $i_max; $i++) {
$replace_value = $this->random_string(2);
$s = $replace_value . ', ';
while (($mbStrlenAvailable ? mb_strlen($s) : strlen($s)) < $s_length) {
$s .= $this->random_string(10) . ', ';
}
for ($j = 0; $j < $j_max; $j++) {
$replace_with = $this->random_string(2);
if ($mbEregReplaceAvailable) {
$s = mb_eregi_replace($replace_value, $replace_with, $s);
$s = mb_eregi_replace($replace_with, $replace_value, $s);
$s = mb_eregi_replace($replace_value, $replace_with, $s);
} else {
$pattern1 = '/' . preg_quote($replace_value, '/') . '/i';
$pattern2 = '/' . preg_quote($replace_with, '/') . '/i';
$s = preg_replace($pattern1, $replace_with, $s) ?? $s;
$s = preg_replace($pattern2, $replace_value, $s) ?? $s;
$s = preg_replace($pattern1, $replace_with, $s) ?? $s;
}
}
$data[] = $s;
}
if ($i_max === $j_max && $i_max === 1) {
return true;
}
$data_splitted_2 = [];
$data_splitted = [];
foreach ($data as $big_string) {
$data_splitted[] = explode(',', $big_string);
array_merge($data_splitted_2, preg_split('/[\s,]+/', $big_string) ?: []);
}
foreach ($data_splitted as $rk => $r_array) {
sort($data_splitted[$rk]);
}
if ($this->hasExceededExecutionTime()) {
return $this->max_time_reached_return_code;
}
foreach ($data_splitted_2 as $rk => $rv) {
$data_splitted_2[$rk] = md5(md5($rv));
}
if ($this->hasExceededExecutionTime()) {
return $this->max_time_reached_return_code;
}
sort($data_splitted_2);
if ($this->hasExceededExecutionTime()) {
return $this->max_time_reached_return_code;
}
foreach ($data_splitted as $rk => $r_array) {
foreach ($r_array as $sk => $sv) {
$data_splitted[$rk][$sk] = md5($sv);
}
}
unset($data);
return true;
}
private function wpbenchmark_fibonacci_recursive(int $n): int
{
if ($n <= 1) {
return $n;
}
if ($n % 5 === 0 && $this->hasExceededExecutionTime()) {
return $this->max_time_reached_return_code;
}
$left = $this->wpbenchmark_fibonacci_recursive($n - 1);
if ($left === $this->max_time_reached_return_code) {
return $this->max_time_reached_return_code;
}
$right = $this->wpbenchmark_fibonacci_recursive($n - 2);
if ($right === $this->max_time_reached_return_code) {
return $this->max_time_reached_return_code;
}
return $left + $right;
}
private function wpbenchmark_fibonacci_iterative(int $n): bool
{
$fib = [];
$fib[0] = 0;
$fib[1] = 1;
for ($i = 2; $i <= $n; $i++) {
$fib[$i] = $fib[$i - 1] + $fib[$i - 2];
}
return true;
}
public function test_fibo_recursive()
{
$result = $this->wpbenchmark_fibonacci_recursive(35);
if ($result === $this->max_time_reached_return_code) {
return $this->max_time_reached_return_code;
}
return true;
}
public function test_fibo_iterative()
{
for ($j = 0; $j < 1; $j++) {
for ($i = 1; $i < 10000; $i++) {
$this->wpbenchmark_fibonacci_iterative(5000);
}
if ($this->hasExceededExecutionTime()) {
return $this->max_time_reached_return_code;
}
}
return true;
}
public function test_cpu_floating_operations(int $iterations = 200000)
{
for ($j = 0; $j < 15; $j++) {
$result = 0.0;
for ($i = 0; $i < $iterations; $i++) {
$result += sin($i) * cos($i) / sqrt($i + 1);
}
}
return true;
}
}
$benchmarkDefinitions = [
// 'test_cpu_regex' => [
// 'name' => 'Operations with large text data',
// 'description' => '',
// 'default_parameter' => null,
// 'accepts_parameter' => true,
// ],
'test_cpu_randbytes' => [
'name' => 'Random binary data operations',
'description' => 'Test CPU and memory with random binary data generation and memory prefill',
'default_parameter' => 80000,
'accepts_parameter' => true,
],
'test_fibo_recursive' => [
'name' => 'Recursive mathematical calculations',
'description' => '',
'default_parameter' => null,
'accepts_parameter' => false,
],
'test_fibo_iterative' => [
'name' => 'Iterative mathematical calculations',
'description' => '',
'default_parameter' => null,
'accepts_parameter' => false,
],
'test_cpu_floating_operations' => [
'name' => 'Floating point operations',
'description' => '',
'default_parameter' => 800000,
'accepts_parameter' => true,
],
];
$selected = $argv[1] ?? 'all';
$customParameter = null;
$maxExecutionTimeArg = null;
if (is_numeric($selected)) {
// Backward compatibility: old style args were [iterations] [maxExecutionTime].
$selected = 'test_cpu_floating_operations';
$customParameter = (int) $argv[1];
$maxExecutionTimeArg = $argv[2] ?? null;
} else {
if ($selected === 'all') {
$maxExecutionTimeArg = $argv[2] ?? null;
} elseif (isset($benchmarkDefinitions[$selected]) && $benchmarkDefinitions[$selected]['accepts_parameter']) {
$customParameter = isset($argv[2]) ? (int) $argv[2] : null;
$maxExecutionTimeArg = $argv[3] ?? null;
} else {
$maxExecutionTimeArg = $argv[2] ?? null;
}
}
$maxExecutionTime = isset($maxExecutionTimeArg) ? (float) $maxExecutionTimeArg : 300.0;
if ($customParameter !== null && $customParameter <= 0) {
fwrite(STDERR, "Parameter must be a positive integer.\n");
exit(2);
}
if ($maxExecutionTime <= 0) {
fwrite(STDERR, "Max execution time must be a positive number.\n");
exit(2);
}
$runner = new WPBenchmarkRunner($maxExecutionTime);
$toRun = [];
if ($selected === 'all') {
$toRun = array_keys($benchmarkDefinitions);
} elseif (isset($benchmarkDefinitions[$selected])) {
$toRun[] = $selected;
} else {
fwrite(STDERR, "Unknown benchmark '{$selected}'. Available benchmarks:\n");
foreach ($benchmarkDefinitions as $function => $meta) {
fwrite(STDERR, " - {$function}: {$meta['name']}\n");
}
exit(2);
}
$labels = array_merge($toRun, ['total']);
$labelWidth = max(array_map('strlen', $labels)) + 5;
$totalElapsed = 0.0;
foreach ($toRun as $function) {
$meta = $benchmarkDefinitions[$function];
$parameter = $customParameter ?? $meta['default_parameter'];
$benchmarkStart = microtime(true);
$result = $runner->run($function, $parameter);
$elapsed = microtime(true) - $benchmarkStart;
$totalElapsed += $elapsed;
if ($result !== true) {
// Keep output minimal and predictable even when benchmark exits early.
}
echo str_pad($function, $labelWidth, '.', STR_PAD_RIGHT) . number_format($elapsed, 2, '.', '') . " s\n";
}
echo str_pad('total', $labelWidth, '.', STR_PAD_RIGHT) . number_format($totalElapsed, 2, '.', '') . " s\n";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment