isCli = php_sapi_name() === "cli"; $this->memory_limit = ini_get("memory_limit"); $this->version = '1.0.2'; } public function addTest(string $name, int $count, callable $function) { $this->tests[] = [ "name" => $name, "count" => $count, "func" => $function, ]; } public function runTests() { ob_start(); $this->startTime = microtime(true); $this->startMemory = memory_get_usage(); foreach ($this->tests as $test) { gc_collect_cycles(); $startMem = memory_get_usage(); $start = microtime(true); try { $resultBuffer = $test["func"]($test["count"]); unset($resultBuffer); $time = $this->formatTime(microtime(true) - $start); $memory = $this->formatSize(max(0, memory_get_usage() - $startMem)); } catch (\Throwable $e) { $time = "Error: " . $e->getMessage(); $memory = "-"; } $this->results[] = [ "name" => $test["name"], "time" => $time, "memory" => $memory, ]; gc_collect_cycles(); } $this->results_all = [ "time" => $this->formatTime(microtime(true) - $this->startTime), "memory" => $this->formatSize(memory_get_peak_usage(true)), "bufferedContent" => ob_get_clean(), ]; } private function formatSize($bytes) { if ($bytes == 0) { return "0 B"; } if ($bytes < 1024) { return $bytes . " B"; } if ($bytes < 1048576) { return round($bytes / 1024, 2) . " KB"; } return round($bytes / 1048576, 2) . " MB"; } private function formatTime($seconds) { return number_format($seconds, 4) . " s"; } public function printResult() { // Set headers for web browsers if not already sent if (!headers_sent() && !$this->isCli) { header("Expires: Wed, 11 Jan 1984 05:00:00 GMT"); header("Cache-Control: no-cache, must-revalidate, max-age=0"); } // Output CSS styles echo $this->generateStyles(); // Gather system information for display $info = [ "PHP" => PHP_VERSION, "OS" => php_uname("s") . " " . php_uname("r") . " (" . php_uname("m") . ")", "SAPI" => php_sapi_name(), "Server" => $_SERVER["SERVER_SOFTWARE"] ?? "CLI", "Mem Limit" => ini_get("memory_limit"), "Time Limit" => ini_get("max_execution_time") . "s", "OPCache" => (function_exists("opcache_get_status") && is_array(opcache_get_status()) && @opcache_get_status()["opcache_enabled"]) ? "On" : "Off", "JIT" => (function_exists("opcache_get_status") && is_array(opcache_get_status()) && !empty(opcache_get_status()["jit"]["enabled"])) ? "On" : "Off", ]; // Main container - minimalist light design echo '
'; // Header bar with version and repository link echo '
'; echo '
'; echo 'PHP Benchmark v'.$this->version.''; echo 'GitHub: gr8man'; echo '
'; echo "Total Time: {$this->results_all["time"]} | Peak Mem: {$this->results_all["memory"]}"; echo '
'; echo '
'; // Left Column: System Info with row separators echo '
'; echo '

System Info

'; echo ''; foreach ($info as $key => $val) { echo ""; } echo '
{$key}{$val}
'; echo '
'; // Right Column: Benchmark Results table echo '
'; echo '

Test Results

'; echo ''; echo ""; foreach ($this->results as $result) { echo ""; echo ""; echo ""; echo ""; } echo '
Test NameTime
{$result["name"]}{$result["time"]}
'; echo '
'; echo '
'; } private function generateStyles() { return << .benchmark-container { font-family: ui-monospace, monospace; max-width: 900px; margin: 10px auto; color: #333; background: #fff; padding: 15px; border-radius: 6px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); border: 1px solid #ddd; font-size: 11px; line-height: 1.2; } .benchmark-header { display: flex; justify-content: space-between; align-items: center; padding: 14px 18px; background: #f8f9fa; border-radius: 6px; font-weight: bold; border: 1px solid #e9ecef; margin-bottom: 18px; font-size: 14px; } .header-left { display: flex; flex-direction: column; } .benchmark-title { color: #2c3e50; font-size: 18px; text-transform: uppercase; } .benchmark-version { font-size: 12px; color: #7f8c8d; text-transform: none; } .github-link { font-size: 10px; color: #3498db; text-decoration: none; font-weight: normal; margin-top: 3px; } .header-results { font-size: 12px; } .total-time { color: #d35400; } .peak-memory { color: #27ae60; } .benchmark-content { display: flex; gap: 18px; align-items: flex-start; } .system-info-column { flex: 1; min-width: 240px; } .results-column { flex: 2; } .section-title { margin: 0 0 8px; color: #2980b9; border-bottom: 2px solid #ecf0f1; padding-bottom: 5px; font-size: 12px; } .system-info-table { width: 100%; border-collapse: collapse; } .info-key { padding: 5px 0; color: #555; font-weight: 600; border-bottom: 1px solid #f1f1f1; font-size: 11px; } .info-value { padding: 5px 0; text-align: right; color: #333; border-bottom: 1px solid #f1f1f1; font-size: 11px; } .results-table { width: 100%; border-collapse: collapse; } .test-name-header { text-align: left; padding: 5px 0; color: #2c3e50; border-bottom: 1px solid #bdc3c7; font-size: 11px; } .test-time-header { text-align: right; color: #2c3e50; border-bottom: 1px solid #bdc3c7; font-size: 11px; } .test-name { padding: 5px 0; border-bottom: 1px solid #f1f1f1; color: #34495e; font-size: 11px; } .test-time { text-align: right; color: #d35400; border-bottom: 1px solid #f1f1f1; font-weight: 500; font-size: 11px; } CSS; } } $benchmark = new PhpBenchmark(); // Zmniejszono delikatnie limity, aby wasm nie przepełniał pamięci $benchmark->addTest("Math (Trigonometry & Powers)", 200000, function ($limit) { $r = 0; for ($i = 0; $i < $limit; $i++) { $a = $i * 2 + $i; $b = $i % 7; $r = sin($i) * cos($i); $t = tan($i); atan($i); $p = pow($i >> 2, 2); $s = sqrt($i); hypot($i, $b); $l = log($i + 1); exp($b); abs($i - $limit); ceil($i / 3.14); floor($i / 3.14); round($i / 3.14); is_finite($i); is_nan($i); pi(); } return $r; }); $benchmark->addTest("Heavy Geometry (Mesh Projections)", 20000, function ($limit) { $mesh = []; for ($v = 0; $v < 20; $v++) { $mesh[] = ["x" => sin($v), "y" => cos($v), "z" => $v * 0.1]; } $accumulatedArea = 0.0; for ($i = 0; $i < $limit; $i++) { $angle = $i * 0.0001; $cosA = cos($angle); $sinA = sin($angle); $minX = 9999.0; $maxX = -9999.0; $minY = 9999.0; $maxY = -9999.0; foreach ($mesh as &$vertex) { $rx = $vertex["x"] * $cosA - $vertex["z"] * $sinA; $rz = $vertex["x"] * $sinA + $vertex["z"] * $cosA; $ry = $vertex["y"] * $cosA - $rz * $sinA; $rz2 = $vertex["y"] * $sinA + $rz * $cosA; $vertex["x"] = $rx; $vertex["y"] = $ry; $vertex["z"] = $rz2; $zPerspective = $rz2 + 5.0; $px = $rx / $zPerspective; $py = $ry / $zPerspective; if ($px < $minX) $minX = $px; if ($px > $maxX) $maxX = $px; if ($py < $minY) $minY = $py; if ($py > $maxY) $maxY = $py; } $accumulatedArea += ($maxX - $minX) * ($maxY - $minY); } return $accumulatedArea; }); $benchmark->addTest("String (Manipulation & Regex)", 20000, function ($limit) { $string = "The quick brown fox jumps over the lazy dog"; $s = ""; for ($i = 0; $i < $limit; $i++) { $s = str_shuffle($string); $u = strtoupper($s); $r = strrev($u); strpos($string, "fox"); substr($string, 5, 10); str_replace(" ", "", $string); str_pad($string, 60, ".", STR_PAD_BOTH); trim(" " . $string . " "); $parts = explode(" ", $string); implode("-", $parts); md5($r); base64_encode($string); preg_match("/(quick|lazy)/", $string); preg_replace("/[aeiou]/i", "*", $string); preg_split("/\s+/", $string); } return $s; }); $benchmark->addTest("Loops & Logic (Heavy Branching)", 50000, function ($limit) { $x = 1; $y = 0; for ($i = 1; $i <= $limit; ++$i) { for ($j = 0; $j < 25; ++$j) { if (($i % 2 === 0 && $x % 3 !== 0) || $j % 7 === 0) { $x ^= $j << 1; } elseif ($j % 5 === 0 xor $x > 10000) { $x = $x * 3 + 1; } else { $x += $i >> 2; } switch (abs($x ^ $j) % 5) { case 0: $y += $i; break; case 1: case 2: $y -= $j * 0.5; break; case 3: $x = ~$x; break; default: $y ^= $x; } if ($x > 1000000 || $x < -1000000) { $x = $x % 10000; } } } return $x + $y; }); $benchmark->addTest("Object (Instantiation & Magic Methods)", 600000, function ($limit) { $checksum = 0; for ($i = 0; $i < $limit; $i++) { $obj = new class ($i) { private $id; public $val; public function __construct(int $id) { $this->id = $id; $this->val = "test"; } public function __get(string $name) { return $this->id * 2; } public function __set(string $name, $value) { $this->val = (string) $value; } }; $obj->dynamicProp = $i; $cloned = clone $obj; $checksum += $cloned->hiddenProp; // Szybkie zwolnienie z pamięci unset($obj, $cloned); } return $checksum; }); $benchmark->addTest("Arrays (Creation & Sorting)", 10000, function ($limit) { $a = []; $sum = 0; for ($i = 0; $i < $limit; $i++) { $a = range(0, 200); shuffle($a); sort($a); $a = array_flip($a); $sum += array_sum($a); unset($a); } return $sum; }); $benchmark->addTest("Data Processing (Filter & Sort)", 10000, function ($limit) { $data = []; for ($k = 0; $k < 100; $k++) { $data[] = ["id" => $k, "score" => $k * rand(1, 10), "cat" => $k % 5]; } $res = []; for ($i = 0; $i < $limit; $i++) { $temp = array_filter($data, function ($row) { return $row["score"] > 200 && $row["cat"] % 2 === 0; }); usort($temp, function ($a, $b) { return $b["score"] <=> $a["score"]; }); $res = $temp; unset($temp); } return $res; }); $benchmark->addTest("Recursion (Heavy Call Stack)", 1000000, function ($limit) { $result = 0; $heavyRecurse = function ($depth, $x, $y) use (&$heavyRecurse) { if ($depth <= 0) { return $x + $y; } return $heavyRecurse($depth - 1, $x + 1, $y) + $heavyRecurse($depth - 1, $x, $y + 1); }; $depth = 5; $callsPerLoop = 1 << $depth; $loops = intdiv($limit, $callsPerLoop); for ($i = 0; $i < $loops; $i++) { $result += $heavyRecurse($depth, $i, 1); } return $result; }); $benchmark->addTest("Hashing (Crypto & Bcrypt)", 20000, function ($limit) { $h = ""; $useHash = function_exists("hash"); for ($i = 0; $i < $limit; $i++) { if ($useHash) { $h = hash("sha256", "string to hash " . $i); } else { $h = sha1("string to hash " . $i); } if ($i % 500 === 0) { password_hash("password", PASSWORD_DEFAULT, ["cost" => 4]); } } return $h; }); $benchmark->addTest("JSON & Serialization", 100000, function ($limit) { $data = ["test" => 123, "array" => [1, 2, 3], "text" => "lorem ipsum"]; $s = ""; for ($i = 0; $i < $limit; $i++) { $j = json_encode($data); json_decode($j); $s = serialize($data); unserialize($s); } return $s; }); $benchmark->addTest("Rand::rand (Basic)", 100000, function ($limit) { $x = 0; for ($i = 0; $i < $limit; $i++) { $x = rand(0, 1000000); } return $x; }); $benchmark->addTest("Rand::mt_rand (Mersenne)", 100000, function ($limit) { $x = 0; for ($i = 0; $i < $limit; $i++) { $x = mt_rand(0, 1000000); } return $x; }); $benchmark->addTest("Rand::random_int (CSPRNG)", 100000, function ($limit) { $x = 0; for ($i = 0; $i < $limit; $i++) { $x = random_int(0, 1000000); } return $x; }); $benchmark->addTest("Rand::random_bytes", 100000, function ($limit) { $x = ""; for ($i = 0; $i < $limit; $i++) { $x = random_bytes(32); } return $x; }); $benchmark->addTest('IO::File Write', 10000, function ($limit) { $tmpFile = sys_get_temp_dir() . '/php_bench_io_test.txt'; $data = str_repeat('1234567890', 500); if (file_exists($tmpFile)) unlink($tmpFile); for ($i = 0; $i < $limit; $i++) { file_put_contents($tmpFile, $data, FILE_APPEND); } $size = file_exists($tmpFile) ? filesize($tmpFile) : 0; if (file_exists($tmpFile)) unlink($tmpFile); return $size; }); $benchmark->addTest('IO::File Read', 10000, function ($limit) { $tmpFile = sys_get_temp_dir() . '/php_bench_io_read.txt'; $data = str_repeat('1234567890', 500); file_put_contents($tmpFile, str_repeat($data, 20)); $content = ''; for ($i = 0; $i < $limit; $i++) { $content = file_get_contents($tmpFile); } if (file_exists($tmpFile)) unlink($tmpFile); return $content; }); $benchmark->addTest('Memory (Allocation & GC)', 50000, function ($limit) { $data = []; $startMem = memory_get_usage(); $maxRelativeAllocation = 30 * 1024 * 1024; $actualLimit = 0; for ($i = 0; $i < $limit; $i++) { if (memory_get_usage() - $startMem > $maxRelativeAllocation) break; $data[] = str_repeat(chr(65 + ($i % 26)), 100) . $i; $actualLimit++; } for ($i = 0; $i < $actualLimit; $i += 2) { unset($data[$i]); } for ($i = 0; $i < $actualLimit; $i += 2) { if (memory_get_usage() - $startMem > $maxRelativeAllocation) break; $data[$i] = [ 'id' => $i, 'token' => md5((string)$i), 'active' => true ]; } return $data; }); $benchmark->addTest("JIT: Mandelbrot", 100, function ($limit) { $r = 0; for ($i = 0; $i < $limit; $i++) { for ($y = -39; $y < 39; $y++) { for ($x = -39; $x < 39; $x++) { $cr = $x / 20.0; $ci = $y / 20.0; $zr = $cr; $zi = $ci; for ($n = 0; $n < 49; $n++) { $a = $zr * $zr - $zi * $zi + $cr; $zi = 2 * $zr * $zi + $ci; $zr = $a; if ($zr * $zr + $zi * $zi > 16) break; } $r += $n; } } } return $r; }); $benchmark->addTest("JIT: Bubble Sort (Pure PHP)", 100, function ($limit) { $sum = 0; for ($i = 0; $i < $limit; $i++) { $arr = range(500, 1); $n = count($arr); for ($j = 0; $j < $n - 1; $j++) { for ($k = 0; $k < $n - $j - 1; $k++) { if ($arr[$k] > $arr[$k + 1]) { $temp = $arr[$k]; $arr[$k] = $arr[$k + 1]; $arr[$k + 1] = $temp; } } } $sum += $arr[0]; } return $sum; }); $benchmark->addTest("JIT: Sieve of Eratosthenes", 2000, function ($limit) { $primesCount = 0; for ($i = 0; $i < $limit; $i++) { $max = 10000; $sieve = array_fill(0, $max, true); $sieve[0] = false; $sieve[1] = false; for ($p = 2; $p * $p < $max; $p++) { if ($sieve[$p]) { for ($j = $p * $p; $j < $max; $j += $p) { $sieve[$j] = false; } } } $primesCount += $sieve[9973] ? 1 : 0; } return $primesCount; }); $benchmark->addTest("OPcache: Repeated File Include", 1000, function ($limit) { $tmpFile = sys_get_temp_dir() . '/php_bench_opcache_test.php'; // Generujemy plik z dużą tablicą (np. 2000 elementów, każdy to 100-znakowy string) $largeArray = []; for ($i = 0; $i < 2000; $i++) { $largeArray['key_' . $i] = str_repeat('x', 100); } $content = "runTests(); $benchmark->printResult();