Files
cmsPRO/test.php
2026-02-22 21:59:33 +01:00

698 lines
29 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Server Benchmark single file
* Autor: ChatGPT (GPT-5 Thinking)
* Wersja: 2025-09-02
*/
declare(strict_types=1);
error_reporting(E_ALL);
ini_set('display_errors', '1');
@set_time_limit(0);
// ============================ KONFIGURACJA DB ============================
// Uzupełnij poniżej lub przekaż przez env (DB_DSN, DB_USER, DB_PASS).
$DB_DSN = getenv('DB_DSN') ?: 'mysql:host=localhost;port=3306;dbname=host117523_cmspro;charset=utf8mb4';
$DB_USER = getenv('DB_USER') ?: 'host117523_cmspro';
$DB_PASS = getenv('DB_PASS') ?: '3sJADeqKHLqHddfavDeR';
// ============================ USTAWIENIA DOMYŚLNE ============================
$defaults = [
'cpu_iterations' => 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 (0100) =================================
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();
?>
<!doctype html>
<html lang="pl">
<head>
<meta charset="utf-8">
<title>Server Benchmark (PHP score + small-ops)</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; margin: 24px; color:#222; }
h1 { margin: 0 0 8px; font-size: 22px; }
.note { background:#fff3cd; border:1px solid #ffeeba; padding:10px 12px; border-radius:8px; margin:12px 0; }
fieldset { border:1px solid #ddd; padding:12px; border-radius:10px; margin-bottom:16px; }
legend { padding:0 8px; color:#555; }
label { display:block; margin:6px 0 2px; color:#333; font-size:13px; }
input[type="text"], input[type="number"] { width:220px; padding:6px 8px; border:1px solid #ccc; border-radius:6px; }
.grid { display:grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap:16px; }
button { padding:8px 12px; border:0; background:#2563eb; color:#fff; border-radius:8px; cursor:pointer; }
button.secondary { background:#6b7280; }
table { width:100%; border-collapse: collapse; margin:12px 0; }
th, td { border:1px solid #eee; padding:8px 10px; text-align:left; font-size:13px; }
th { background:#f9fafb; }
code { background:#f3f4f6; padding:2px 6px; border-radius:6px; }
.section { margin: 24px 0; }
.error { color:#b91c1c; font-weight:600; }
.small { color:#666; font-size:12px; }
.scorebox { background:#f0f9ff; border:1px solid #bae6fd; padding:12px; border-radius:12px; }
.scorenum { font-size:32px; font-weight:800; }
.chip { display:inline-block; padding:2px 8px; border-radius:999px; background:#eef2ff; border:1px solid #c7d2fe; margin-right:6px; }
</style>
</head>
<body>
<h1>Server Benchmark (PHP score + small-ops)</h1>
<div class="note">
<strong>Uwaga:</strong> Testy korzystają z katalogu tymczasowego (<code><?=h($info['temp_dir'])?></code>) i tabeli <code>bench_tmp</code> (DSN: <code><?=h($DB_DSN)?></code>).
</div>
<details>
<summary><strong>Informacje o środowisku</strong></summary>
<table>
<tr><th>PHP</th><td><?=h($info['php_version'])?> (<?=h($info['sapi'])?>)</td></tr>
<tr><th>System</th><td><?=h($info['os'])?></td></tr>
<tr><th>OPcache</th><td>enabled: <?=h($info['opcache_enabled'])?> | memory: <?=h($info['opcache_memory'])?></td></tr>
<tr><th>Limity</th><td>memory_limit: <?=h($info['memory_limit'])?> | upload_max_filesize: <?=h($info['upload_max_filesize'])?> | post_max_size: <?=h($info['post_max_size'])?></td></tr>
<tr><th>Temp dir</th><td><?=h($info['temp_dir'])?></td></tr>
<tr><th>Dysk (temp)</th><td>free: <?=h($info['disk_free_space'])?> / total: <?=h($info['disk_total_space'])?></td></tr>
<tr><th>Rozszerzenia</th><td class="small"><?=h($info['extensions'])?></td></tr>
</table>
</details>
<form method="post">
<fieldset>
<legend>Parametry</legend>
<div class="grid">
<div>
<label>CPU: powtórzenia</label>
<input type="number" name="cpu_iterations" min="1" value="<?=h($params['cpu_iterations'])?>">
<label>CPU: rozmiar (liczba operacji)</label>
<input type="number" name="cpu_size" min="1000" value="<?=h($params['cpu_size'])?>">
</div>
<div>
<label>DYSK: przebiegi</label>
<input type="number" name="disk_passes" min="1" value="<?=h($params['disk_passes'])?>">
<label>DYSK: pliki na rozmiar</label>
<input type="number" name="disk_files" min="1" value="<?=h($params['disk_files'])?>">
<label>DYSK: rozmiary (np. 64K,1M,8M)</label>
<input type="text" name="disk_sizes" value="<?=h($params['disk_sizes'])?>">
</div>
<div>
<label>DB: liczba wierszy</label>
<input type="number" name="db_rows" min="100" value="<?=h($params['db_rows'])?>">
<label>Small-ops: liczba plików (4KB)</label>
<input type="number" name="smallops_files" min="100" value="<?=h($params['smallops_files'])?>">
</div>
</div>
<div style="margin-top:12px; display:flex; gap:8px; flex-wrap:wrap;">
<button name="run" value="cpu">Start CPU</button>
<button name="run" value="disk">Start Dysk</button>
<button name="run" value="smallops">Start FS small-ops</button>
<button name="run" value="db">Start DB</button>
<button name="run" value="memory">Start Pamięć</button>
<button name="run" value="all" class="secondary">Uruchom wszystko</button>
<button name="run" value="score" class="secondary">Policz SCORE (uruchomi brakujące)</button>
</div>
</fieldset>
</form>
<?php if ($error): ?>
<div class="section error">Błąd: <?=h($error)?></div>
<?php endif; ?>
<?php if (!empty($results['cpu'])): ?>
<div class="section">
<h2>Wyniki CPU</h2>
<table>
<thead><tr><th>Iteracja</th><th>Numeric [s]</th><th>Array [s]</th><th>Hash [s]</th><th>Łącznie [s]</th><th>Checksum</th><th>Acc sample</th></tr></thead>
<tbody>
<?php foreach ($results['cpu']['runs'] as $r): ?>
<tr>
<td><?=h($r['iteration'])?></td>
<td><?=number_format($r['numeric_s'], 6)?></td>
<td><?=number_format($r['array_s'], 6)?></td>
<td><?=number_format($r['hash_s'], 6)?></td>
<td><?=number_format($r['total_s'], 6)?></td>
<td><code><?=h($r['checksum'])?></code></td>
<td><?=h($r['acc_sample'])?></td>
</tr>
<?php endforeach; ?>
</tbody>
<tfoot>
<tr>
<th>Średnio</th>
<th><?=number_format($results['cpu']['avg']['numeric_s'], 6)?></th>
<th><?=number_format($results['cpu']['avg']['array_s'], 6)?></th>
<th><?=number_format($results['cpu']['avg']['hash_s'], 6)?></th>
<th><?=number_format($results['cpu']['avg']['total_s'], 6)?></th>
<th colspan="2">CPU size: <?=h($results['cpu']['size'])?></th>
</tr>
</tfoot>
</table>
</div>
<?php endif; ?>
<?php if (!empty($results['disk'])): ?>
<div class="section">
<h2>Wyniki Dysk (sekwencyjne)</h2>
<table>
<thead>
<tr>
<th>Rozmiar pliku</th><th>Plików</th>
<th>Zapis: bajty</th><th>Zapis: czas [s]</th><th>Zapis: prędkość</th><th>MB/s</th>
<th>Odczyt: bajty</th><th>Odczyt: czas [s]</th><th>Odczyt: prędkość</th><th>MB/s</th>
</tr>
</thead>
<tbody>
<?php foreach ($results['disk'] as $row): ?>
<tr>
<td><?=h(human_bytes((float)$row['size']))?></td>
<td><?=h($row['files'])?></td>
<td><?=h(number_format($row['written_bytes']))?></td>
<td><?=h(number_format($row['write_time_s'], 6))?></td>
<td><?=h($row['write_speed'])?></td>
<td><?=h(number_format($row['write_MBps'],2))?></td>
<td><?=h(number_format($row['read_bytes']))?></td>
<td><?=h(number_format($row['read_time_s'], 6))?></td>
<td><?=h($row['read_speed'])?></td>
<td><?=h(number_format($row['read_MBps'],2))?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div class="small">Pliki robocze po teście są automatycznie usuwane.</div>
</div>
<?php endif; ?>
<?php if (!empty($results['smallops'])): ?>
<div class="section">
<h2>Wyniki FS small-ops (małe pliki ~4KB)</h2>
<table>
<tbody>
<tr><th>Docelowa liczba plików</th><td><?=h($results['smallops']['files'])?></td></tr>
<tr><th>Utworzono</th><td><?=h($results['smallops']['created'])?></td></tr>
<tr><th>Czas tworzenia</th><td><?=number_format($results['smallops']['create_time_s'], 6)?> s (<?=number_format($results['smallops']['create_ops_s'],0)?> ops/s)</td></tr>
<tr><th>Czas odczytu</th><td><?=number_format($results['smallops']['read_time_s'], 6)?> s (<?=number_format($results['smallops']['read_MBps'],2)?> MB/s)</td></tr>
<tr><th>Usunięto</th><td><?=h($results['smallops']['deleted'])?> (<?=number_format($results['smallops']['delete_ops_s'],0)?> ops/s)</td></tr>
<tr><th>Rozmiar pojedynczego pliku</th><td><?=h(human_bytes($results['smallops']['file_size']))?></td></tr>
</tbody>
</table>
</div>
<?php endif; ?>
<?php if (!empty($results['db'])): ?>
<div class="section">
<h2>Wyniki Baza danych</h2>
<table>
<tbody>
<tr><th>INSERT wierszy</th><td><?=h($results['db']['insert_rows'])?></td></tr>
<tr><th>INSERT czas</th><td><?=number_format($results['db']['insert_time_s'], 6)?> s (<?=number_format($results['db']['insert_rows_s'],0)?> rows/s)</td></tr>
<tr><th>SELECT agregaty</th><td><?=number_format($results['db']['select_aggr_s'], 6)?> s</td></tr>
<tr><th>SELECT fetch</th><td><?=number_format($results['db']['select_fetch_s'], 6)?> s, pobrano <?=h($results['db']['fetched_rows'])?> wierszy</td></tr>
<tr><th>UPDATE</th><td><?=h($results['db']['update_affected'])?> wierszy w <?=number_format($results['db']['update_time_s'], 6)?> s</td></tr>
<tr><th>DELETE</th><td><?=h($results['db']['delete_affected'])?> wierszy w <?=number_format($results['db']['delete_time_s'], 6)?> s</td></tr>
<tr><th>Transakcja łączny czas</th><td><?=number_format($results['db']['transaction_s'], 6)?> s (inv: <?=number_format($results['db']['txn_inv_s'],2)?> 1/s)</td></tr>
</tbody>
</table>
</div>
<?php endif; ?>
<?php if (!empty($results['memory'])): ?>
<div class="section">
<h2>Wyniki Pamięć</h2>
<table>
<tbody>
<tr><th>Alokowano</th><td><?=h($results['memory']['allocated'])?></td></tr>
<tr><th>Czas alokacji</th><td><?=number_format($results['memory']['alloc_time'], 6)?> s (<?=number_format($results['memory']['approx_MBps'],0)?> MB/s)</td></tr>
<tr><th>Czas przejścia</th><td><?=number_format($results['memory']['walk_time'], 6)?> s</td></tr>
<tr><th>Checksum</th><td><code><?=h($results['memory']['checksum'])?></code></td></tr>
</tbody>
</table>
</div>
<?php endif; ?>
<?php if (!empty($score)): ?>
<div class="section scorebox">
<h2>Hosting Score</h2>
<div class="scorenum"><?=h($score['score'])?> / 100</div>
<div class="small" style="margin:8px 0 12px;">
<span class="chip">DB: <?=isset($score['parts']['DB'])?number_format($score['parts']['DB'],1):'—'?></span>
<span class="chip">Dysk (seq): <?=isset($score['parts']['DISK_seq'])?number_format($score['parts']['DISK_seq'],1):'—'?></span>
<span class="chip">Dysk (small-ops): <?=isset($score['parts']['DISK_small'])?number_format($score['parts']['DISK_small'],1):'—'?></span>
<span class="chip">CPU: <?=isset($score['parts']['CPU'])?number_format($score['parts']['CPU'],1):'—'?></span>
<span class="chip">Pamięć: <?=isset($score['parts']['MEM'])?number_format($score['parts']['MEM'],1):'—'?></span>
</div>
<div class="small">
Wagi (DB 45%, Dysk 30% = 20% seq + 10% small-ops, CPU 20%, Pamięć 5%).<br>
Wagi są automatycznie renormalizowane, jeśli nie uruchomisz wszystkich testów.
</div>
</div>
<?php endif; ?>
<div class="section small">
Tipy:
<ul>
<li>Chcesz bardziej „żyłować” dysk? Dodaj większe rozmiary (np. <strong>32M,64M</strong>) i zwiększ liczbę plików.</li>
<li>Jeśli test DB jest wolny, sprawdź <code>innodb_flush_log_at_trx_commit</code>, <code>sync_binlog</code> i I/O dysku.</li>
<li>Małe pliki (~4KB) świetnie ujawniają limity IOPS/metadata ważne przy cache, thumbnailach, logach.</li>
<li>Baseliney do SCORE zmienisz w tablicy <code>$BASE</code> na górze pliku.</li>
</ul>
</div>
</body>
</html>