10, 'cpu_size' => 2000000, // liczba operacji w teście CPU 'disk_passes' => 1, 'disk_files' => 8, 'disk_sizes' => '64K,1M,8M,16MB,32MB', 'db_rows' => 500000, 'smallops_files' => 200000, // liczba małych plików (4KB) ]; // ============================ BASELINE'Y DO SCORE =========================== // Dobierz wg swojej infrastruktury; score jest skalowany i "capowany". $BASE = [ 'CPU_ops_per_s' => 1_000_000.0, // CPU: ~1e6 "umownych" operacji/s = 100 pkt 'DISK_read_MBps' => 400.0, // Dysk odczyt 400 MB/s => 100 pkt 'DISK_write_MBps' => 300.0, // Dysk zapis 300 MB/s => 100 pkt 'DISK_smallops_ops_s' => 5000.0, // FS small-ops (twórz/usuń/odczyt) ~5k/s => 100 pkt 'DB_insert_rows_s' => 20000.0, // DB insert 20k rows/s => 100 pkt 'DB_txn_inv_s' => 2.0, // 1 / transaction_time_s: 2 (czyli ~0.5s) => 100 pkt 'MEM_alloc_MBps' => 2000.0, // Alokacja pamięci ~2000 MB/s => 100 pkt ]; // Wagi (sumują się do 1). Dysk rozbijamy: 0.20 sequential + 0.10 small-ops $WEIGHTS = [ 'DB' => 0.45, 'DISK_seq' => 0.20, 'DISK_small' => 0.10, 'CPU' => 0.20, 'MEM' => 0.05, ]; // ============================ POMOCNICZE =================================== function now(): float { return microtime(true); } function dt(float $t0): float { return microtime(true) - $t0; } function human_bytes(float $bytes): string { $units = ['B','KB','MB','GB','TB']; $i = 0; while ($bytes >= 1024 && $i < count($units)-1) { $bytes /= 1024; $i++; } return sprintf('%.2f %s', $bytes, $units[$i]); } function human_rate(float $bytes, float $seconds): string { if ($seconds <= 0) return '∞ B/s'; return human_bytes($bytes / $seconds) . '/s'; } function parse_size_to_bytes(string $val): int { $val = trim($val); if ($val === '') return 0; $u = strtoupper(substr($val, -1)); $n = (float)$val; switch ($u) { case 'K': return (int)($n * 1024); case 'M': return (int)($n * 1024 * 1024); case 'G': return (int)($n * 1024 * 1024 * 1024); default: return (int)$n; } } function sys_temp_dir(): string { $dir = sys_get_temp_dir(); if (!is_dir($dir) || !is_writable($dir)) $dir = __DIR__; return rtrim($dir, DIRECTORY_SEPARATOR); } function rnd_data(int $bytes): string { if ($bytes <= 0) return ''; try { return random_bytes($bytes); } catch (Throwable $e) { $chunk = md5((string)mt_rand(), true); $out = ''; while (strlen($out) < $bytes) $out .= $chunk; return substr($out, 0, $bytes); } } function php_info_summary(): array { $opcache = function_exists('opcache_get_status') ? (opcache_get_status(false) ?: []) : []; return [ 'php_version' => PHP_VERSION, 'sapi' => PHP_SAPI, 'os' => php_uname(), 'memory_limit' => ini_get('memory_limit'), 'upload_max_filesize' => ini_get('upload_max_filesize'), 'post_max_size' => ini_get('post_max_size'), 'opcache_enabled' => ($opcache['opcache_enabled'] ?? false) ? 'yes' : 'no', 'opcache_memory' => isset($opcache['memory_usage']['used_memory']) ? human_bytes((float)$opcache['memory_usage']['used_memory']).' used / '.human_bytes((float)$opcache['memory_usage']['free_memory']).' free' : 'n/a', 'extensions' => implode(', ', get_loaded_extensions()), 'temp_dir' => sys_temp_dir(), 'disk_free_space' => human_bytes((float)@disk_free_space(sys_temp_dir())), 'disk_total_space' => human_bytes((float)@disk_total_space(sys_temp_dir())), ]; } // ============================ TEST: CPU ===================================== function bench_cpu(int $iterations, int $size): array { $results = []; for ($i = 1; $i <= $iterations; $i++) { $t0 = now(); $acc = 0.0; for ($k = 1; $k <= $size; $k++) { $acc += sqrt($k) + sin($k) + log($k + 1); } $t_num = dt($t0); $t1 = now(); $arr = []; for ($k = 0; $k < (int)($size / 10); $k++) $arr[] = ($k * 2654435761) % 1_000_003; shuffle($arr); sort($arr, SORT_NUMERIC); $t_arr = dt($t1); $t2 = now(); $hash = ''; $chunk = str_repeat('A', 1024); for ($k = 0; $k < (int)($size / 1000); $k++) $hash = hash('sha256', $hash . $chunk . $k, true); $t_hash = dt($t2); $total = dt($t0); $results[] = [ 'iteration' => $i, 'numeric_s' => $t_num, 'array_s' => $t_arr, 'hash_s' => $t_hash, 'total_s' => $total, 'checksum' => substr(bin2hex($hash), 0, 16), 'acc_sample'=> round($acc, 4), ]; } $avg = [ 'numeric_s' => array_sum(array_column($results, 'numeric_s'))/count($results), 'array_s' => array_sum(array_column($results, 'array_s'))/count($results), 'hash_s' => array_sum(array_column($results, 'hash_s'))/count($results), 'total_s' => array_sum(array_column($results, 'total_s'))/count($results), ]; return ['runs' => $results, 'avg' => $avg, 'size' => $size]; } // ============================ TEST: DYSK ==================================== function bench_disk(int $passes, int $filesPerSize, array $sizesBytes): array { $baseDir = sys_temp_dir() . DIRECTORY_SEPARATOR . 'php_bench_' . getmypid() . '_' . uniqid(); if (!@mkdir($baseDir, 0777, true) && !is_dir($baseDir)) throw new RuntimeException("Nie mogę utworzyć katalogu: $baseDir"); $summary = []; try { foreach ($sizesBytes as $size) { $wBytesTotal = 0; $wTimeTotal = 0.0; $rBytesTotal = 0; $rTimeTotal = 0.0; for ($p = 0; $p < $passes; $p++) { // ZAPIS $t0 = now(); for ($i = 0; $i < $filesPerSize; $i++) { $fn = $baseDir . DIRECTORY_SEPARATOR . "file_{$size}_{$p}_{$i}.bin"; $h = fopen($fn, 'wb'); if (!$h) throw new RuntimeException("Nie mogę zapisać pliku: $fn"); $block = rnd_data(min($size, 1024 * 1024)); $written = 0; while ($written < $size) { $chunk = min($size - $written, strlen($block)); $w = fwrite($h, substr($block, 0, $chunk)); if ($w === false) throw new RuntimeException("Błąd zapisu do $fn"); $written += $w; } fflush($h); fclose($h); $wBytesTotal += $size; } $wTimeTotal += dt($t0); // ODCZYT $t1 = now(); for ($i = 0; $i < $filesPerSize; $i++) { $fn = $baseDir . DIRECTORY_SEPARATOR . "file_{$size}_{$p}_{$i}.bin"; $h = fopen($fn, 'rb'); if (!$h) throw new RuntimeException("Nie mogę odczytać pliku: $fn"); while (!feof($h)) { $data = fread($h, 1024 * 1024); if ($data === false) throw new RuntimeException("Błąd odczytu z $fn"); $rBytesTotal += strlen($data); } fclose($h); } $rTimeTotal += dt($t1); } $summary[] = [ 'size' => $size, 'files' => $filesPerSize * $passes, 'written_bytes' => $wBytesTotal, 'write_time_s' => $wTimeTotal, 'write_speed' => human_rate($wBytesTotal, $wTimeTotal), 'write_MBps' => ($wTimeTotal>0 ? ($wBytesTotal/$wTimeTotal)/(1024*1024) : INF), 'read_bytes' => $rBytesTotal, 'read_time_s' => $rTimeTotal, 'read_speed' => human_rate($rBytesTotal, $rTimeTotal), 'read_MBps' => ($rTimeTotal>0 ? ($rBytesTotal/$rTimeTotal)/(1024*1024) : INF), ]; } } finally { $it = @new RecursiveIteratorIterator(new RecursiveDirectoryIterator($baseDir, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST); if ($it) foreach ($it as $file) { if ($file->isDir()) @rmdir($file->getPathname()); else @unlink($file->getPathname()); } @rmdir($baseDir); } return $summary; } // ============================ TEST: FS SMALL-OPS ============================ function bench_small_ops(int $files, int $sizeBytes = 4096): array { $dir = sys_temp_dir() . DIRECTORY_SEPARATOR . 'php_smallops_' . getmypid() . '_' . uniqid(); if (!@mkdir($dir, 0777, true) && !is_dir($dir)) throw new RuntimeException("Nie mogę utworzyć katalogu: $dir"); $data = rnd_data($sizeBytes); $created = 0; $tCreate0 = now(); for ($i=0; $i<$files; $i++) { $fn = $dir . DIRECTORY_SEPARATOR . "s_$i.bin"; $h = fopen($fn, 'wb'); if (!$h) break; fwrite($h, $data); fclose($h); $created++; } $tCreate = dt($tCreate0); $readBytes = 0; $tRead0 = now(); for ($i=0; $i<$created; $i++) { $fn = $dir . DIRECTORY_SEPARATOR . "s_$i.bin"; $readBytes += filesize($fn); file_get_contents($fn); } $tRead = dt($tRead0); $deleted = 0; $tDel0 = now(); for ($i=0; $i<$created; $i++) { $fn = $dir . DIRECTORY_SEPARATOR . "s_$i.bin"; if (@unlink($fn)) $deleted++; } $tDel = dt($tDel0); @rmdir($dir); return [ 'files' => $files, 'created' => $created, 'create_time_s' => $tCreate, 'create_ops_s' => ($tCreate>0? $created/$tCreate : INF), 'read_time_s' => $tRead, 'read_MBps' => ($tRead>0? ($readBytes/$tRead)/(1024*1024) : INF), 'deleted' => $deleted, 'delete_time_s' => $tDel, 'delete_ops_s' => ($tDel>0? $deleted/$tDel : INF), 'file_size' => $sizeBytes, ]; } // ============================ TEST: DB ====================================== function db_connect(string $dsn, string $user, string $pass): PDO { return new PDO($dsn, $user, $pass, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ]); } function bench_db(string $dsn, string $user, string $pass, int $rows): array { $pdo = db_connect($dsn, $user, $pass); $pdo->exec(" CREATE TABLE IF NOT EXISTS bench_tmp ( id INT AUTO_INCREMENT PRIMARY KEY, payload VARBINARY(255) NOT NULL, val INT NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB "); $pdo->exec("TRUNCATE TABLE bench_tmp"); $t_total0 = now(); $pdo->beginTransaction(); $t0 = now(); $stmt = $pdo->prepare("INSERT INTO bench_tmp (payload, val) VALUES (?, ?)"); for ($i = 0; $i < $rows; $i++) $stmt->execute([rnd_data(64), $i]); $t_insert = dt($t0); $t1 = now(); $sum = $pdo->query("SELECT COUNT(*) AS c, SUM(val) AS s, AVG(val) AS a FROM bench_tmp")->fetch(); $t_select_aggr = dt($t1); $t2 = now(); $stmt2 = $pdo->query("SELECT * FROM bench_tmp WHERE val % 97 = 0"); $rowsFetched = $stmt2->fetchAll(); $t_select_fetch = dt($t2); $t3 = now(); $upd = $pdo->exec("UPDATE bench_tmp SET val = val + 1 WHERE id % 50 = 0"); $t_update = dt($t3); $t4 = now(); $del = $pdo->exec("DELETE FROM bench_tmp WHERE id % 100 = 0"); $t_delete = dt($t4); $pdo->commit(); $t_total = dt($t_total0); return [ 'insert_rows' => $rows, 'insert_time_s' => $t_insert, 'insert_rows_s' => ($t_insert>0? $rows/$t_insert : INF), 'select_aggr_s' => $t_select_aggr, 'select_fetch_s' => $t_select_fetch, 'fetched_rows' => count($rowsFetched), 'update_affected' => $upd, 'update_time_s' => $t_update, 'delete_affected' => $del, 'delete_time_s' => $t_delete, 'transaction_s' => $t_total, 'txn_inv_s' => ($t_total>0? 1/$t_total : INF), ]; } // ============================ DODATKOWE: PAMIĘĆ ============================= function bench_memory(int $blocks = 200, int $blockSize = 256 * 1024): array { $t0 = now(); $arr = []; for ($i = 0; $i < $blocks; $i++) $arr[] = rnd_data($blockSize); $allocTime = dt($t0); $t1 = now(); shuffle($arr); $hash = 'x'; foreach ($arr as $b) $hash = md5($hash . substr($b, 0, 32), true); $walkTime = dt($t1); $bytes = (float)$blocks * $blockSize; unset($arr); return [ 'allocated' => human_bytes($bytes), 'allocated_B' => $bytes, 'alloc_time' => $allocTime, 'walk_time' => $walkTime, 'approx_MBps' => ($allocTime>0? ($bytes/$allocTime)/(1024*1024) : INF), 'checksum' => substr(bin2hex($hash), 0, 16), ]; } // ============================ SCORE (0–100) ================================= function clamp100(float $v): float { return max(0.0, min(100.0, $v)); } function calc_scores(array $all, array $BASE, array $WEIGHTS): array { $parts = []; $weights = $WEIGHTS; // CPU score – przeliczamy „umowne operacje/s”: cpu_size / avg_total_s if (!empty($all['cpu'])) { $ops_s = ($all['cpu']['avg']['total_s']>0 ? $all['cpu']['size'] / $all['cpu']['avg']['total_s'] : INF); $parts['CPU'] = clamp100(($ops_s / $BASE['CPU_ops_per_s']) * 100); } // DISK sequential – średnia z MB/s (read+write) po rozmiarach if (!empty($all['disk'])) { $r=[];$w=[]; foreach ($all['disk'] as $row){ $r[]=$row['read_MBps']; $w[]=$row['write_MBps']; } $avgR = (!empty($r)? array_sum($r)/count($r) : 0); $avgW = (!empty($w)? array_sum($w)/count($w) : 0); $sR = ($avgR/$BASE['DISK_read_MBps'])*100; $sW = ($avgW/$BASE['DISK_write_MBps'])*100; $parts['DISK_seq'] = clamp100(($sR + $sW)/2); } // DISK small-ops – bierzemy średnią z create_ops_s, delete_ops_s i przeliczamy vs baseline if (!empty($all['smallops'])) { $ops = []; if (is_finite($all['smallops']['create_ops_s'])) $ops[] = $all['smallops']['create_ops_s']; if (is_finite($all['smallops']['delete_ops_s'])) $ops[] = $all['smallops']['delete_ops_s']; // Odczyt też ważny – przeliczamy read_MBps na „ops/s” przeskalowując do 4KB if (is_finite($all['smallops']['read_MBps'])) { $ops[] = ($all['smallops']['read_MBps'] * 1024 * 1024) / max(1, $all['smallops']['file_size']); } $avgOps = (!empty($ops)? array_sum($ops)/count($ops) : 0); $parts['DISK_small'] = clamp100(($avgOps / $BASE['DISK_smallops_ops_s']) * 100); } // DB – 60% insert_rows_s + 40% txn_inv_s (1/transaction_time) if (!empty($all['db'])) { $sIns = clamp100(($all['db']['insert_rows_s'] / $BASE['DB_insert_rows_s']) * 100); $sTxn = clamp100(($all['db']['txn_inv_s'] / $BASE['DB_txn_inv_s']) * 100); $parts['DB'] = clamp100(0.6*$sIns + 0.4*$sTxn); } // MEM – alokacja MB/s if (!empty($all['memory'])) { $mbps = $all['memory']['approx_MBps']; $parts['MEM'] = clamp100(($mbps / $BASE['MEM_alloc_MBps']) * 100); } // Renormalizacja wag (używamy tylko dostępnych komponentów) $activeWeights = 0.0; foreach ($weights as $k=>$w) if (isset($parts[$k])) $activeWeights += $w; $score = 0.0; if ($activeWeights > 0) { foreach ($parts as $k=>$val) { $score += $val * ($weights[$k] / $activeWeights); } } return ['parts'=>$parts, 'score'=>round($score, 1)]; } // ============================ ROUTING / UI ================================== function getParam(string $key, $default) { if (isset($_POST[$key])) return $_POST[$key]; if (isset($_GET[$key])) return $_GET[$key]; return $default; } $action = (string)getParam('run', ''); $params = [ 'cpu_iterations' => (int)getParam('cpu_iterations', $defaults['cpu_iterations']), 'cpu_size' => (int)getParam('cpu_size', $defaults['cpu_size']), 'disk_passes' => (int)getParam('disk_passes', $defaults['disk_passes']), 'disk_files' => (int)getParam('disk_files', $defaults['disk_files']), 'disk_sizes' => (string)getParam('disk_sizes', $defaults['disk_sizes']), 'db_rows' => (int)getParam('db_rows', $defaults['db_rows']), 'smallops_files' => (int)getParam('smallops_files', $defaults['smallops_files']), ]; $diskSizes = array_filter(array_map('trim', explode(',', $params['disk_sizes'])), fn($v) => $v !== ''); $diskBytes = array_map('parse_size_to_bytes', $diskSizes); $results = []; $error = null; try { if ($action === 'cpu' || $action === 'all' || $action === 'score') { $results['cpu'] = bench_cpu($params['cpu_iterations'], max(1000, $params['cpu_size'])); } if ($action === 'disk' || $action === 'all' || $action === 'score') { $results['disk'] = bench_disk( max(1, $params['disk_passes']), max(1, $params['disk_files']), array_map(fn($v) => max(4096, (int)$v), $diskBytes ?: [64*1024, 1024*1024, 8*1024*1024]) ); } if ($action === 'smallops' || $action === 'all' || $action === 'score') { $results['smallops'] = bench_small_ops(max(100, $params['smallops_files'])); } if ($action === 'db' || $action === 'all' || $action === 'score') { $results['db'] = bench_db($DB_DSN, $DB_USER, $DB_PASS, max(100, $params['db_rows'])); } if ($action === 'memory' || $action === 'all' || $action === 'score') { $results['memory'] = bench_memory(); } } catch (Throwable $e) { $error = $e->getMessage(); } // SCORE (na żywo liczymy z tego, co mamy w $results) $score = (!empty($results) ? calc_scores($results, $BASE, $WEIGHTS) : null); // ============================ RENDER HTML =================================== function h($s) { return htmlspecialchars((string)$s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); } $info = php_info_summary(); ?> Server Benchmark (PHP – score + small-ops)

Server Benchmark (PHP – score + small-ops)

Uwaga: Testy korzystają z katalogu tymczasowego () i tabeli bench_tmp (DSN: ).
Informacje o środowisku
PHP ()
System
OPcacheenabled: | memory:
Limitymemory_limit: | upload_max_filesize: | post_max_size:
Temp dir
Dysk (temp)free: / total:
Rozszerzenia
Parametry
Błąd:

Wyniki – CPU

IteracjaNumeric [s]Array [s]Hash [s]Łącznie [s]ChecksumAcc sample
Średnio CPU size:

Wyniki – Dysk (sekwencyjne)

Rozmiar plikuPlików Zapis: bajtyZapis: czas [s]Zapis: prędkośćMB/s Odczyt: bajtyOdczyt: czas [s]Odczyt: prędkośćMB/s
Pliki robocze po teście są automatycznie usuwane.

Wyniki – FS small-ops (małe pliki ~4KB)

Docelowa liczba plików
Utworzono
Czas tworzenia s ( ops/s)
Czas odczytu s ( MB/s)
Usunięto ( ops/s)
Rozmiar pojedynczego pliku

Wyniki – Baza danych

INSERT – wierszy
INSERT – czas s ( rows/s)
SELECT – agregaty s
SELECT – fetch s, pobrano wierszy
UPDATE wierszy w s
DELETE wierszy w s
Transakcja – łączny czas s (inv: 1/s)

Wyniki – Pamięć

Alokowano
Czas alokacji s ( MB/s)
Czas przejścia s
Checksum

Hosting Score

/ 100
DB: Dysk (seq): Dysk (small-ops): CPU: Pamięć:
Wagi (DB 45%, Dysk 30% = 20% seq + 10% small-ops, CPU 20%, Pamięć 5%).
Wagi są automatycznie renormalizowane, jeśli nie uruchomisz wszystkich testów.
Tipy: