تست هاست اشتراکی؛ منابع واقعی سرویس را بسنجید
یک فایل PHP روی هاست قرار دهید و عملکرد پردازش، حافظه و فایل را بررسی کنید؛ همراه با راهنمای تشخیص تفاوت منابع سرور، محدودیت حساب و نتیجه واقعی آزمون.

هاست تازه خریدهاید. در صفحه فروش نوشته شده «پردازنده قدرتمند، رم بالا و دیسک NVMe»، اما سرعت نصب وردپرس یا باز شدن پیشخوان چندان راضیکننده نیست. از کجا بفهمید مشکل از سرویس است، تنظیمات سایت ایراد دارد یا انتظار شما با امکانات پلن هماهنگ نیست؟
تست هاست اشتراکی به این سؤال کمک میکند، به شرط آنکه عددها را درست بخوانیم. نمایش ۶۴ پردازنده در یک فایل PHP به معنی داشتن ۶۴ هسته برای سایت شما نیست؛ همانطور که خواندن یک فایل با سرعت چند هزار مگابایت در ثانیه، نسل SSD سرور را ثابت نمیکند.
در این راهنما یک اسکریپت مستقل میسازیم که از طریق دامنه اجرا میشود، چند آزمون کوتاه میگیرد و گزارش فارسی میدهد. به نصب وردپرس، دسترسی SSH، رمز پایگاه داده یا ساخت دستی کلید امنیتی احتیاج ندارید. ابزار اطلاعات قابل دسترس را از نتایج اندازهگیری جدا میکند و هرجا مدرک کافی نداشته باشد، صریحاً مینویسد «نامشخص».
آیا میتوان منابع واقعی هاست را با یک فایل PHP فهمید؟
بخشی از منابع و محدودیتها را میتوان دید؛ همه آنها را نمیتوان با قطعیت تشخیص داد. در هاست اشتراکی سه موضوع متفاوت وجود دارد:
| موضوع | نمونه | برداشت درست |
|---|---|---|
| منابع قابل مشاهده سرور یا کانتینر | مدل پردازنده، تعداد CPU منطقی، حافظه گزارششده کرنل | اطلاعات محیطی که PHP میبیند؛ ممکن است چندین حساب در آن شریک باشند |
| محدودیتهای اجرای برنامه | memory_limit و زمان اجرای PHP | سقفها و تنظیمات همین محیط PHP |
| سهم حساب و عملکرد عملی | محدودیت CPU و IO حساب، سرعت اجرای آزمون | سهم حساب را با پنل تطبیق میدهیم؛ عملکرد را در همان لحظه اندازه میگیریم |
ممکن است سرور ۱۲۸ گیگابایت RAM داشته باشد، حساب شما سقف دو گیگابایت داشته باشد و هر درخواست PHP اجازه مصرف ۲۵۶ مگابایت حافظه بگیرد. این سه عدد میتوانند همزمان درست باشند. طبق مستندات PHP درباره memory_limit، این گزینه مقدار حافظه مجاز برای تخصیص توسط اسکریپت را محدود میکند؛ ظرفیت کل رم حساب را مشخص نمیکند.
در سرویسهای مبتنی بر CloudLinux نیز CPU، حافظه، IO، IOPS و تعداد پردازشها میتوانند محدودیتهای جدا داشته باشند. بنابراین برای بررسی ادعای فروشنده باید نتیجه ابزار، مشخصات پلن و بخش مصرف منابع پنل را کنار هم گذاشت. مستندات رسمی محدودیتهای CloudLinux همین تفکیک را توضیح میدهد.
اسکریپت چه چیزهایی را بررسی میکند؟
گزارش دو بخش اصلی دارد: مشخصات قابل مشاهده و آزمونهای عملکرد. جدول زیر نشان میدهد هر بخش چه کاربردی دارد و چه نتیجهای نباید از آن گرفت.
| بررسی | خروجی ابزار | محدودیت تفسیر |
|---|---|---|
| محیط PHP | نسخه، روش اجرا، تنظیمات حافظه و آپلود، افزونهها، تنظیمات OPcache و JIT | نمایش تنظیمات JIT به معنی اثبات فعال بودن آن برای تمام درخواستها نیست |
| سیستمعامل | خانواده سیستم، کرنل و نام توزیع در صورت دسترسی | ممکن است محیط کانتینر دیده شود؛ اطلاعات سختافزار فیزیکی تضمین نمیشود |
| پردازنده | مدل و تعداد CPU منطقی قابل مشاهده | تعداد هسته اختصاصیافته به حساب محسوب نمیشود |
| حافظه | حافظه قابل مشاهده کرنل، مصرف PHP و سقف آن | رم آزاد یا تضمینشده حساب را تعیین نمیکند |
| محدودیت cgroup v2 | سقف عددی CPU و حافظه در گروه جاری و والدهای قابل مشاهده | ممکن است گروه مشترک باشد؛ محدودیتهای پنهان دیده نمیشوند |
| پردازش | تعداد تکرار محاسبه در ثانیه | یک آزمون تکرشتهای PHP است |
| بافر حافظه | نرخ ساخت و پرکردن بافر کوچک | پهنای باند خام RAM یا ظرفیت نهایی حساب نیست |
| فایل | نوشتن، خواندن گرم، بررسی صحت داده و تأخیر خواندنهای کوچک | کش و محدودیت حساب بر نتیجه اثر دارند |
| پاسخ HTTP | زمان سه رفتوبرگشت از مرورگر تا همین ابزار | پینگ خالص، سرعت دانلود یا ظرفیت پورت شبکه نیست |
اگر بخشی از اطلاعات در دسترس نباشد، ابزار برای پیدا کردن آن سراغ اجرای فرمان سیستم یا دور زدن محدودیت هاست نمیرود. بخش مربوطه ناشناخته میماند و آزمونهای قابل اجرا ادامه پیدا میکنند.

الگوریتم تست چگونه کار میکند؟
هدف ابزار، مقایسه اولیه و پیدا کردن نشانههای قابل پیگیری است. برای همین، بهجای پر کردن رم یا نوشتن فایلهای چندگیگابایتی، کار را در دوازده درخواست پیاپی انجام میدهد: سه نمونه پردازش، سه نمونه حافظه، سه نمونه فایل و سه درخواست برای اندازهگیری رفتوبرگشت HTTP.
پردازش و حافظه با حجم محدود
در هر نمونه پردازش، یک محاسبه عددی مشخص با بودجه زمانی حدود ۱۸۰ میلیثانیه تکرار میشود. تعداد تکرار و زمان واقعی ذخیره میشوند. این نرخ به نسخه PHP، تنظیمات اجرا، زمانبندی پردازنده و بار همان لحظه وابسته است؛ ابزار از آن تعداد هستههای خریداریشده را استخراج نمیکند.
در آزمون حافظه، بافر حداکثر دو MiB ساخته و پر میشود. حجم بافر با توجه به سقف PHP کاهش مییابد و اگر حاشیه کافی وجود نداشته باشد، آزمون رد میشود. تخصیص حافظه تا مرز خطا عمداً انجام نمیشود، چون میتواند درخواستهای سایت را مختل کند و باز هم سقف تجاری پلن را با اطمینان ثابت نمیکند.
تست فایل در محل واقعی نصب ابزار
اسکریپت فایل موقت را کنار خودش میسازد؛ بنابراین اگر آن را در پوشه سایت قرار دهید، همان فایلسیستم بررسی میشود. هر نمونه حداکثر چهار MiB داده تصادفی مینویسد و در محیط محدود، حجم کمتر میشود. داده تصادفی کمک میکند نتیجه به ذخیرهسازی یک رشته تکراری و بسیار فشردهپذیر وابسته نباشد.
ابتدا نوشتن همراه با fflush اندازهگیری میشود. اگر fsync در دسترس باشد و موفق شود، نرخ نوشتن با احتساب زمان آن هم جدا ثبت میشود. fflush بهتنهایی اثبات نمیکند که داده روی رسانه ذخیرهسازی پایدار شده است. حتی نتیجه موفق fsync نیز مدل سختافزار یا صحت عملکرد تمام لایههای مجازی زیرین را مشخص نمیکند.
سپس فایل بلافاصله خوانده میشود. نام این نتیجه «خواندن گرم» است، چون داده ممکن است از کش سیستم پاسخ داده شود. محتوای خواندهشده با داده اولیه تطبیق داده میشود؛ نتیجه ناقص یا دارای خطای صحت، وارد جمعبندی سرعت فایل نمیشود.
در ادامه، تا ۱۲۸ خواندن چهار KiB از موقعیتهای مختلف همان فایل انجام میشود. اگر نمونه کافی وجود داشته باشد، صدک ۹۵ زمان خواندن ثبت میشود؛ یعنی تقریباً ۹۵ درصد این خواندنها تأخیری کمتر یا برابر آن داشتهاند. این عدد، تأخیر دسترسی گرم از PHP است و نباید آن را تأخیر خام SSD یا IOPS واقعی پلن نامید.
جمعبندی بدون امتیازسازی
ابزار برای نرخهای اندازهگیریشده، میانه، کمینه، بیشینه و دامنه نوسان را نشان میدهد. دامنه نوسان از اختلاف بیشترین و کمترین مقدار، تقسیم بر میانه، به دست میآید. سه نمونه برای بررسی اولیه مفیدند، اما برای اعلام پایداری بلندمدت سرویس کافی نیستند.
اسکریپت محدودیت تعداد اجرا دارد: حداکثر شش شروع در ۲۴ ساعت و حداقل یک دقیقه فاصله میان شروعها. این شمارنده در فایلی کنار ابزار ثبت میشود و با باز کردن مرورگر دیگر از نو شروع نمیشود. بودجه زمانی حلقهها هم به معنی تضمین پایان هر درخواست در همان زمان نیست؛ یک عملیات فایل یا fsync ممکن است در سطح سیستم بیشتر معطل شود.
آموزش اجرای تست هاست اشتراکی از طریق دامنه
اسکریپت را روی سرویس خودتان و در زمان کمترافیک اجرا کنید. اگر شرایط استفاده میزبان، اجرای بنچمارک را محدود کرده، همان شرایط را رعایت کنید. آزمون کوتاه است، اما مصرف پردازنده و خواندنونوشتن آن صفر نیست.
- وارد File Manager هاست شوید. در پوشه همان دامنه، معمولاً
public_html، یک فایل با نامwb-host-benchmark.phpبسازید. برای اجرای ابزار لازم نیست مجوز پوشه را روی ۷۷۷ قرار دهید. - کد کامل بخش بعد را بدون علامتهای سه بکتیک داخل فایل قرار دهید و با UTF-8 ذخیره کنید. میتوانید بهجای ساخت فایل، نسخه آماده همین اسکریپت را آپلود کنید.
- نشانی زیر را با دامنه خود باز کنید:
https://example.com/wb-host-benchmark.php
- گزینه اجرای تست روی سرویس خودتان را فعال کنید و «شروع تست» را بزنید. فقط یک زبانه باز باشد. مرورگر باید جاوااسکریپت و کوکی را پشتیبانی کند؛ کلید داخلی درخواستها خودکار ساخته میشود.
- بعد از پایان، گزارش JSON را دریافت کنید. سپس فایل اسکریپت و فایل
wbbench-state-….phpمربوط به آن را از File Manager حذف کنید. فایلهای داده معمولاً خودکار پاک میشوند؛ اگر اجرای PHP ناگهان قطع شد، باقیمانده فایلهایwbbench-data-….binهمین ابزار را هم بررسی و حذف کنید.
این صفحه ورود مدیریتی ندارد؛ هرکسی که نشانی را داشته باشد میتواند آن را باز کند و در محدوده مجاز تست بگیرد. آدرس را منتشر نکنید. اگر پنل امکان رمزدار کردن پوشه را دارد، استفاده از آن مناسب است. انقضای ۲۴ساعته ابزار، جای حذف فایل پس از کار را نمیگیرد. نکتههای مرتبط با نگهداری فایلهای مدیریتی موقت را در بخش امنیت سایت هم دنبال کنید.
کد کامل اسکریپت بررسی منابع هاست
این کد مستقل است و فایل تنظیمات وردپرس، رمزها یا پایگاه داده را نمیخواند. فقط فایلهای موقت خودش و یک فایل کوچک کنترل تعداد اجرا را میسازد. اطلاعات فنی اختیاری در حد دسترسی مجاز PHP خوانده میشوند؛ درخواست خارجی و ارسال گزارش به سایت دیگری در کد وجود ندارد.
حداقل نحو در نظر گرفتهشده PHP 5.6 است. با این حال، هیچ اسکریپتی روی تمام تنظیمات هاست تضمین اجرا ندارد. نشست PHP، امکانات پایه فایل و قفلگذاری، JSON، توابع هش و یک روش تولید تصادفی امن باید در دسترس باشند. این سازگاری نحوی، توصیه به استفاده از نسخه قدیمی PHP نیست.
<?php
/* Wbweb shared hosting microbenchmark 1.0. PHP 5.6 syntax; no shell, no database. */
define('WBH_VERSION', '1.0');
define('WBH_MAX_RUNS', 6);
define('WBH_COOLDOWN', 60);
define('WBH_STEPS', 12);
$wbhBase = realpath(dirname(__FILE__));
function wbh_e($s) { return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
function wbh_call($fn, $args, $default = null) {
if (!is_callable($fn)) return $default;
try { return @call_user_func_array($fn, $args); }
catch (Exception $e) { return $default; }
catch (Throwable $e) { return $default; }
}
function wbh_read($path, $limit = 65536) {
if (!is_callable('file_get_contents') || !wbh_call('is_readable', array($path), false)) return null;
$v = wbh_call('file_get_contents', array($path, false, null, 0, $limit + 1));
return is_string($v) && strlen($v) <= $limit ? $v : null;
}
function wbh_ini($name) { $v = wbh_call('ini_get', array($name)); return $v === false ? null : $v; }
function wbh_bytes($v) {
if (!is_string($v) || !preg_match('/^\s*(\d+(?:\.\d+)?)\s*([KMGTP]?)B?\s*$/i', $v, $m)) return null;
$n = (float)$m[1]; $units = array(''=>0,'K'=>1,'M'=>2,'G'=>3,'T'=>4,'P'=>5);
return $n * pow(1024, $units[strtoupper($m[2])]);
}
function wbh_fmt($v) { return is_numeric($v) && $v >= 0 ? round($v / 1048576, 2) . ' MiB' : 'نامشخص'; }
function wbh_clock() {
$t = wbh_call('hrtime', array(true));
return is_numeric($t) ? $t / 1000000000 : microtime(true);
}
function wbh_random($n) {
$s = wbh_call('random_bytes', array($n));
if (is_string($s) && strlen($s) === $n) return $s;
if (is_callable('openssl_random_pseudo_bytes')) {
$strong = false; $s = @openssl_random_pseudo_bytes($n, $strong);
if ($strong && is_string($s) && strlen($s) === $n) return $s;
}
throw new Exception('تولید تصادفی امن در این هاست در دسترس نیست؛ تست فعال نشد.');
}
function wbh_out($data, $code = 200) {
http_response_code($code); header('Content-Type: application/json; charset=UTF-8');
$json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PARTIAL_OUTPUT_ON_ERROR);
echo $json === false ? '{"error":"JSON encoding failed"}' : $json; exit;
}
function wbh_post($k) { return isset($_POST[$k]) && is_string($_POST[$k]) ? $_POST[$k] : ''; }
function wbh_inside($path, $root) { return $path === $root || strpos($path, rtrim($root, '/') . '/') === 0; }
function wbh_mounts() {
$data = wbh_read('/proc/self/mountinfo', 262144); $rows = array();
if ($data === null) return $rows;
foreach (explode("\n", trim($data)) as $line) {
$s = explode(' - ', $line, 2); if (count($s) !== 2) continue;
$a = explode(' ', $s[0]); $b = explode(' ', $s[1]);
if (count($a) < 6 || count($b) < 2) continue;
$decode = array('\\040'=>' ', '\\011'=>"\t", '\\012'=>"\n", '\\134'=>'\\');
$rows[] = array('device'=>$a[2], 'root'=>strtr($a[3],$decode), 'mount'=>strtr($a[4],$decode), 'fs'=>$b[0], 'source'=>strtr($b[1],$decode));
}
return $rows;
}
function wbh_cgroup($mounts) {
$out = array('status'=>'نامشخص؛ این نتیجه به معنی منابع نامحدود نیست.', 'cpu_quota_cores'=>null,
'memory_ceiling_bytes'=>null, 'cpuset_visible'=>null, 'pids_max'=>null, 'io_max'=>null,
'scope'=>'cgroup جاری PHP و والدهای قابل مشاهده؛ لزوماً سهم اختصاصی این حساب نیست.');
$data = wbh_read('/proc/self/cgroup', 16384);
if ($data === null || !preg_match('/^0::([^\r\n]+)$/m', $data, $m)) return $out;
$group = $m[1]; if ($group[0] !== '/' || preg_match('~(?:^|/)\.\.(?:/|$)~', $group)) return $out;
foreach ($mounts as $mount) {
if ($mount['fs'] !== 'cgroup2' || !wbh_inside($group, $mount['root'])) continue;
$base = wbh_call('realpath', array($mount['mount']));
if (!$base || !wbh_inside($base, '/sys/fs/cgroup')) continue;
$rel = $mount['root'] === '/' ? $group : substr($group, strlen($mount['root']));
$dir = wbh_call('realpath', array($base . '/' . ltrim($rel, '/')));
if (!$dir || !wbh_inside($dir, $base)) continue;
$read = 0; $depth = 0; $out['cpuset_visible'] = trim((string)wbh_read($dir.'/cpuset.cpus.effective',4096));
$out['pids_max'] = trim((string)wbh_read($dir.'/pids.max',128));
$out['io_max'] = trim((string)wbh_read($dir.'/io.max',4096));
while ($depth++ < 32) {
$cpu = wbh_read($dir.'/cpu.max',128); $mem = wbh_read($dir.'/memory.max',128);
if ($cpu !== null || $mem !== null) ++$read;
if ($cpu !== null && preg_match('/^(\d+)\s+(\d+)/',trim($cpu),$q) && (float)$q[2] > 0) {
$v=(float)$q[1]/(float)$q[2]; $out['cpu_quota_cores']=$out['cpu_quota_cores']===null?$v:min($v,$out['cpu_quota_cores']);
}
if ($mem !== null && preg_match('/^\d+$/',trim($mem))) {
$v=(float)trim($mem); $out['memory_ceiling_bytes']=$out['memory_ceiling_bytes']===null?$v:min($v,$out['memory_ceiling_bytes']);
}
if ($dir === $base) break; $dir = dirname($dir);
}
if ($read) $out['status']='مقادیر قابل مشاهده خوانده شد؛ والدهای پنهان و محدودیتهای دیگر ممکن است مؤثر باشند.';
return $out;
}
return $out;
}
function wbh_storage($root, $mounts) {
$out=array('filesystem'=>null,'rotational'=>null,'hint'=>'نوع و نسل دیسک نامشخص است؛ از سرعت این آزمون حدس زده نمیشود.'); $best=null;
foreach($mounts as $m) if(wbh_inside($root,$m['mount']) && ($best===null || strlen($m['mount'])>strlen($best['mount']))) $best=$m;
if(!$best) return $out; $out['filesystem']=$best['fs'];
if(!preg_match('/^\d+:\d+$/',$best['device'])) return $out;
$p=wbh_call('realpath',array('/sys/dev/block/'.$best['device']));
if(!$p || !wbh_inside($p,'/sys/devices')) return $out;
$rot=wbh_read($p.'/queue/rotational',16);
if($rot===null && wbh_read($p.'/partition',16)!==null) $rot=wbh_read(dirname($p).'/queue/rotational',16);
if(trim((string)$rot)==='0') { $out['rotational']=0; $out['hint']='غیرچرخشی از دید کرنل؛ SSD یا ذخیرهسازی مجازی محتمل است. مدل، نسل و سختافزار زیرین اثبات نمیشوند.'; }
elseif(trim((string)$rot)==='1') { $out['rotational']=1; $out['hint']='چرخشی از دید کرنل؛ HDD محتمل است، اما لایه مجازی میتواند این ویژگی را ارائه کند.'; }
if(preg_match('~(?:^|/)nvme\d+n\d+~',$p)) $out['hint'].=' نام NVMe در مسیر دستگاه دیده شد؛ نسخه PCIe و نسل SSD مشخص نیست.';
return $out;
}
function wbh_info($root) {
$mounts=wbh_mounts(); $cpu=wbh_read('/proc/cpuinfo',262144); $mem=wbh_read('/proc/meminfo',32768);
$model=null; $count=null; $total=null; $distro=null;
if($cpu!==null) { if(preg_match('/^(?:model name|Hardware)\s*:\s*(.+)$/mi',$cpu,$m))$model=trim($m[1]); $count=preg_match_all('/^processor\s*:/m',$cpu,$ignored); if(!$count)$count=null; }
if($mem!==null && preg_match('/^MemTotal:\s*(\d+)\s*kB/m',$mem,$m))$total=(float)$m[1]*1024;
$os=wbh_read('/etc/os-release',16384); if($os!==null && preg_match('/^PRETTY_NAME=(.+)$/m',$os,$m))$distro=trim($m[1],"\"' \r");
$extensions=wbh_call('get_loaded_extensions',array(),array()); if(is_array($extensions))sort($extensions);
$free=wbh_call('disk_free_space',array($root)); $size=wbh_call('disk_total_space',array($root));
return array('php'=>PHP_VERSION,'sapi'=>PHP_SAPI,'int_bits'=>PHP_INT_SIZE*8,
'os'=>PHP_OS,'kernel'=>wbh_call('php_uname',array('r')),'distribution'=>$distro,
'php_memory_limit'=>wbh_ini('memory_limit'),'php_current_memory_bytes'=>wbh_call('memory_get_usage',array(true)),
'max_execution_time'=>wbh_ini('max_execution_time'),'upload_max_filesize'=>wbh_ini('upload_max_filesize'),
'post_max_size'=>wbh_ini('post_max_size'),'max_input_vars'=>wbh_ini('max_input_vars'),
'opcache_enable'=>wbh_ini('opcache.enable'),'opcache_memory'=>wbh_ini('opcache.memory_consumption'),
'jit'=>wbh_ini('opcache.jit'),'jit_buffer'=>wbh_ini('opcache.jit_buffer_size'),
'cpu_model_visible'=>$model,'logical_cpus_visible'=>$count,'ram_visible_bytes'=>$total,
'system_load'=>wbh_call('sys_getloadavg',array()),'filesystem_free_bytes'=>is_numeric($free)?$free:null,
'filesystem_size_bytes'=>is_numeric($size)?$size:null,'extensions'=>$extensions,
'cgroup'=>wbh_cgroup($mounts),'storage'=>wbh_storage($root,$mounts),
'account_entitlement'=>'سهم واقعی CPU، RAM، IO، IOPS و EP حساب باید با پنل یا مستندات میزبان تطبیق داده شود.',
'visibility_note'=>'نمای کرنل ممکن است متعلق به کل سرور یا کانتینر باشد. memory_limit سقف PHP است؛ فضای فایلسیستم سهمیه حساب نیست.');
}
function wbh_cpu() {
$t=wbh_clock();$n=0;$x=17;
do { for($i=0;$i<2048;$i++)$x=($x*13+17)%1000003; $n+=2048; $elapsed=wbh_clock()-$t; }
while($elapsed<0.18 && $n<10000000);
if($elapsed<=0)throw new Exception('زمانسنج معتبر نیست.');
return array('iterations'=>$n,'seconds'=>$elapsed,'iterations_per_second'=>$n/$elapsed,'checksum'=>$x,
'note'=>'تکدرخواست و تکرشته؛ وابسته به نسخه PHP، JIT و زمانبندی. تعداد هسته اختصاصی را تعیین نمیکند.');
}
function wbh_buffer_size($wanted) {
$used=wbh_call('memory_get_usage',array(true)); $limit=wbh_bytes(wbh_ini('memory_limit'));
if(!is_numeric($used) || $limit===null)return min($wanted,1048576);
$available=$limit-$used-8*1048576;
if($available<2*524288)return 0;
return (int)min($wanted,floor($available/2/524288)*524288);
}
function wbh_memory() {
$size=wbh_buffer_size(2*1048576);if(!$size)return array('skipped'=>'حاشیه کافی زیر memory_limit برای تست سبک وجود ندارد.');
$n=0;$t=wbh_clock();$x='';
do { $x=str_repeat(chr(65+($n%20)),$size);$n++;$elapsed=wbh_clock()-$t;unset($x); }
while($elapsed<0.12 && $n<128);
if($elapsed<=0)return array('skipped'=>'زمانسنج معتبر نیست.');
return array('buffer_bytes'=>$size,'fills'=>$n,'seconds'=>$elapsed,'fill_mib_per_second'=>$size*$n/1048576/$elapsed,
'peak_php_bytes'=>wbh_call('memory_get_peak_usage',array(true)),
'note'=>'سرعت پرکردن بافر PHP؛ نه پهنای باند خام RAM و نه آزمون حداکثر حافظه حساب.');
}
$GLOBALS['wbh_temp']=null;
function wbh_cleanup() {
$p=$GLOBALS['wbh_temp'];if(is_string($p)&&is_callable('unlink'))@unlink($p);
$GLOBALS['wbh_temp']=null;
}
register_shutdown_function('wbh_cleanup');
function wbh_io($root) {
foreach(array('fopen','fwrite','fread','fseek','fflush','fclose','unlink') as $fn)if(!is_callable($fn))return array('skipped'=>'تابع لازم برای I/O در دسترس نیست: '.$fn);
$size=wbh_buffer_size(4*1048576);if(!$size)return array('skipped'=>'حاشیه حافظه کافی برای آزمون فایل وجود ندارد.');
$path=$root.'/wbbench-data-'.bin2hex(wbh_random(12)).'.bin';$h=@fopen($path,'x+b');
if(!$h)return array('skipped'=>'ساخت فایل موقت کنار اسکریپت مجاز نیست.');
$GLOBALS['wbh_temp']=$path;wbh_call('chmod',array($path,0600));
try {
$payload=wbh_random($size);$offset=0;$t=wbh_clock();
while($offset<$size) {
$n=@fwrite($h,substr($payload,$offset,min(65536,$size-$offset)));
if(!is_int($n)||$n<=0)throw new Exception('نوشتن ناقص یا سهمیه/فضای ناکافی.');$offset+=$n;
if(wbh_clock()-$t>1.5)break;
}
if(!@fflush($h))throw new Exception('تخلیه بافر PHP ناموفق بود.');$write=wbh_clock()-$t;
$sync=null;$syncOK=null;
if(is_callable('fsync')){$s=wbh_clock();$syncOK=wbh_call('fsync',array($h),false);$sync=wbh_clock()-$s;}
if(@fseek($h,0)!==0)throw new Exception('بازگشت به ابتدای فایل ناموفق بود.');
$buf='';$t=wbh_clock();
while(strlen($buf)<$offset){$part=@fread($h,min(65536,$offset-strlen($buf)));if(!is_string($part)||$part==='')break;$buf.=$part;if(wbh_clock()-$t>1.5)break;}
$read=wbh_clock()-$t;$valid=strlen($buf)===$offset && $buf===substr($payload,0,$offset);unset($buf,$payload);
$lat=array();$t=wbh_clock();
if($valid && $offset>=4096)for($i=0;$i<128;$i++) {
$pos=(($i*104729)%((int)floor($offset/4096)))*4096;$s=wbh_clock();
if(@fseek($h,$pos)!==0)break;$part=@fread($h,4096);$dt=wbh_clock()-$s;
if(!is_string($part)||strlen($part)!==4096)break;$lat[]=$dt*1000;
if(wbh_clock()-$t>0.15)break;
}
sort($lat,SORT_NUMERIC);$p95=count($lat)>=20?$lat[(int)ceil(count($lat)*0.95)-1]:null;
return array('requested_bytes'=>$size,'written_bytes'=>$offset,'write_mib_per_second'=>$write>0?$offset/1048576/$write:null,
'warm_read_mib_per_second'=>$valid&&$read>0?$offset/1048576/$read:null,'data_verified'=>$valid,
'write_complete'=>$offset===$size,'flush_mode'=>$syncOK===true?'fflush + fsync':'fflush; دوام روی دیسک تأیید نشده',
'fsync_seconds'=>$syncOK===true?$sync:null,'write_and_fsync_mib_per_second'=>$syncOK===true&&$write+$sync>0?$offset/1048576/($write+$sync):null,
'random_warm_reads'=>count($lat),'random_warm_p95_ms'=>$p95,
'note'=>'فایل کوچک کنار اسکریپت؛ خواندن گرم و تحت اثر کش. این اعداد سرعت خام دیسک یا IOPS واقعی سرویس نیستند.');
} finally { @fclose($h);wbh_cleanup(); }
}
function wbh_stats($values) {
$v=array();foreach($values as $x)if(is_numeric($x)&&$x>=0)$v[]=(float)$x;
if(!$v)return null;sort($v,SORT_NUMERIC);$n=count($v);$med=$n%2?$v[(int)floor($n/2)]:($v[$n/2-1]+$v[$n/2])/2;
return array('samples'=>$n,'median'=>$med,'min'=>$v[0],'max'=>$v[$n-1],'spread_percent'=>$med>0?100*($v[$n-1]-$v[0])/$med:null);
}
function wbh_summary($samples) {
$fields=array('cpu'=>array('iterations_per_second'),'memory'=>array('fill_mib_per_second'),
'io'=>array('write_mib_per_second','warm_read_mib_per_second','write_and_fsync_mib_per_second','random_warm_p95_ms'));
$out=array();foreach($fields as $group=>$names)foreach($names as $name){$v=array();foreach($samples as $s)if($s['test']===$group&&isset($s['result'][$name])&&($group!=='io'||(!empty($s['result']['write_complete'])&&!empty($s['result']['data_verified']))))$v[]=$s['result'][$name];$out[$name]=wbh_stats($v);}
return $out;
}
// A small per-script state file enforces limits across fresh cookies and browsers.
function wbh_gate($root) {
foreach(array('fopen','flock','fstat','lstat','ftruncate','rewind','fwrite','fflush','fclose') as $fn)if(!is_callable($fn))throw new Exception('نگهداری محدودیت اجرای تست ممکن نیست: '.$fn);
$path=$root.'/wbbench-state-'.substr(hash('sha256',__FILE__),0,16).'.php';
clearstatcache(true,$path);$s=@lstat($path);
if($s && (($s['mode']&0170000)!==0100000||$s['nlink']!==1))throw new Exception('مسیر ثبت محدودیت امن نیست.');
$h=$s?@fopen($path,'r+b'):@fopen($path,'x+b');if(!$h)throw new Exception('فایل کنترل اجرا قابل ایجاد/باز کردن نیست؛ دسترسی نوشتن این پوشه را بررسی کنید.');
if(!@flock($h,LOCK_EX|LOCK_NB)){@fclose($h);throw new Exception('تست دیگری فعال است؛ کمی بعد تلاش کنید.');}
$f=@fstat($h);clearstatcache(true,$path);$now=@lstat($path);
if(!$f||!$now||$f['ino']!==$now['ino']||$f['dev']!==$now['dev']||$f['nlink']!==1||($now['mode']&0170000)!==0100000){@fclose($h);throw new Exception('هویت فایل کنترل اجرا تغییر کرده است.');}
wbh_call('chmod',array($path,0600));
$text=stream_get_contents($h,4096);$parts=explode("\n",(string)$text,2);$state=count($parts)===2?json_decode($parts[1],true):null;
if($f['size']>0 && !is_array($state)){@fclose($h);throw new Exception('فایل وضعیت ناقص است؛ آن را در File Manager بررسی کنید.');}
if(!$state)$state=array('since'=>time(),'runs'=>0,'last'=>0,'job'=>null,'step'=>0,'finished'=>true);
if(time()-$state['since']>=86400)$state=array('since'=>time(),'runs'=>0,'last'=>0,'job'=>null,'step'=>0,'finished'=>true);
return array($h,$state);
}
function wbh_save_gate($h,$state) {
$bytes="<?php http_response_code(404); exit; ?>\n".json_encode($state);
if(!@rewind($h)||!@ftruncate($h,0))throw new Exception('ثبت محدودیت اجرا ممکن نیست.');
$offset=0;while($offset<strlen($bytes)){$n=@fwrite($h,substr($bytes,$offset));if(!$n)throw new Exception('ثبت محدودیت اجرا ناقص ماند.');$offset+=$n;}
if(!@fflush($h))throw new Exception('ثبت وضعیت تأیید نشد.');
}
header('Cache-Control: no-store, private, max-age=0');header('X-Robots-Tag: noindex, nofollow, noarchive');
header('X-Content-Type-Options: nosniff');header('X-Frame-Options: DENY');header('Referrer-Policy: no-referrer');
header("Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; script-src 'self' 'unsafe-inline'; connect-src 'self'; base-uri 'none'; frame-ancestors 'none'");
if(!defined('PHP_VERSION_ID')||PHP_VERSION_ID<50600){http_response_code(503);exit('PHP 5.6+ required.');}
foreach(array('json_encode','json_decode','session_start','hash','hash_equals','microtime','stream_get_contents') as $fn)if(!is_callable($fn)){http_response_code(503);exit('Required function unavailable: '.htmlspecialchars($fn));}
wbh_call('ini_set',array('display_errors','0'));wbh_call('ini_set',array('session.use_strict_mode','1'));wbh_call('ini_set',array('session.use_only_cookies','1'));wbh_call('ini_set',array('session.cookie_samesite','Strict'));
$https=!empty($_SERVER['HTTPS'])&&strtolower((string)$_SERVER['HTTPS'])!=='off';
session_name('WBHB'.substr(hash('sha256',__FILE__),0,12));session_set_cookie_params(0,'/','',$https,true);
if(!@session_start()){http_response_code(503);exit('PHP session unavailable.');}
try {if(empty($_SESSION['wbh_nonce']))$_SESSION['wbh_nonce']=bin2hex(wbh_random(24));}
catch(Exception $e){http_response_code(503);exit(wbh_e($e->getMessage()));}
$nonce=$_SESSION['wbh_nonce'];
$stamp=wbh_call('filemtime',array(__FILE__));
if(!is_numeric($stamp)||time()-$stamp>86400){http_response_code(403);exit('مهلت ابزار تمام شده؛ برای اجرای تازه فایل را دوباره در ویرایشگر هاست ذخیره کنید.');}
if(isset($_SERVER['REQUEST_METHOD'])&&$_SERVER['REQUEST_METHOD']==='POST') {
if(!hash_equals($nonce,wbh_post('nonce')))wbh_out(array('error'=>'درخواست معتبر نیست؛ صفحه را تازه کنید.'),403);
$action=wbh_post('action');$h=null;
try {
if(!in_array($action,array('start','step','report'),true))throw new Exception('درخواست ناشناخته است.');
if($action==='report'){
if(empty($_SESSION['wbh_job']))throw new Exception('ابتدا تست را اجرا کنید.');$job=$_SESSION['wbh_job'];unset($job['secret']);
$job['summary']=wbh_summary($job['samples']);wbh_out(array('report'=>$job));
}
list($h,$gate)=wbh_gate($wbhBase);
if($action==='start') {
if($gate['runs']>=WBH_MAX_RUNS)throw new Exception('سقف شش اجرا در ۲۴ ساعت رسیده است.');
if(time()-$gate['last']<WBH_COOLDOWN)throw new Exception('بین دو اجرا دستکم یک دقیقه فاصله بگذارید.');
if(!$gate['finished'] && time()-$gate['last']<600)throw new Exception('اجرای دیگری هنوز فعال است؛ تا پایان یا انقضای دهدقیقهای آن صبر کنید.');
$secret=bin2hex(wbh_random(24));$gate['job']=hash('sha256',$secret);$gate['step']=0;$gate['last']=time();$gate['runs']++;$gate['finished']=false;
wbh_save_gate($h,$gate);
$_SESSION['wbh_job']=array('tool'=>'wbweb.ir','version'=>WBH_VERSION,'utc'=>gmdate('c'),'secret'=>$secret,'step'=>0,'info'=>wbh_info($wbhBase),'samples'=>array());
} else {
if(empty($_SESSION['wbh_job']))throw new Exception('اجرا در این مرورگر پیدا نشد.');
$job=&$_SESSION['wbh_job'];$step=$job['step'];
if(!is_string($gate['job'])||!hash_equals($gate['job'],hash('sha256',$job['secret']))||$step!==$gate['step']||$gate['finished']||time()-$gate['last']>600)throw new Exception('اجرای قبلی منقضی یا ناقص شده؛ تست را خودکار تکرار نکنید.');
$gate['step']++;if($gate['step']>=WBH_STEPS)$gate['finished']=true;wbh_save_gate($h,$gate);
$type=$step<3?'cpu':($step<6?'memory':($step<9?'io':'http'));
if($type==='cpu')$result=wbh_cpu();elseif($type==='memory')$result=wbh_memory();elseif($type==='io')$result=wbh_io($wbhBase);else $result=array('note'=>'زمان رفتوبرگشت این درخواست در مرورگر اندازهگیری میشود؛ پینگ ICMP یا ظرفیت شبکه نیست.');
$job['samples'][]=array('test'=>$type,'result'=>$result);$job['step']++;
}
@fclose($h);$h=null;unset($job);$job=$_SESSION['wbh_job'];unset($job['secret']);$job['summary']=wbh_summary($job['samples']);
wbh_out(array('report'=>$job,'done'=>$job['step']>=WBH_STEPS));
} catch(Exception $e){if(is_resource($h))@fclose($h);wbh_out(array('error'=>$e->getMessage()),400);}
catch(Throwable $e){if(is_resource($h))@fclose($h);wbh_out(array('error'=>'این مرحله در محیط هاست قابل اجرا نیست. جزئیات: '.$e->getMessage()),400);}
}
session_write_close();header('Content-Type: text/html; charset=UTF-8');
?>
<!doctype html><html lang="fa" dir="rtl"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>بررسی و بنچمارک هاست اشتراکی</title>
<style>body{font:15px/1.9 Tahoma,Arial,sans-serif;background:#f5f6f7;color:#25282b;margin:0}main{max-width:1050px;margin:30px auto;padding:18px}.box{background:white;border:1px solid #e1e5e8;border-radius:14px;padding:22px;margin-bottom:16px}h1{font-size:25px}h2{font-size:18px}button{font:inherit;padding:10px 18px;border:0;border-radius:8px;background:#1a9c58;color:white;cursor:pointer;margin:5px}button:disabled{opacity:.4}table{width:100%;border-collapse:collapse}td,th{padding:9px;border-bottom:1px solid #eee;text-align:right;vertical-align:top;word-break:break-word}.scroll{overflow:auto}.muted,footer{color:#62696f;font-size:13px}.bad{color:#a82020}progress{width:100%}pre{direction:ltr;text-align:left;white-space:pre-wrap;word-break:break-word;font-size:12px}footer{text-align:center;padding:14px}a{color:#1a9c58}</style></head><body><main>
<section class="box"><h1>منابع قابل مشاهده و عملکرد هاست</h1><p>سه نمونه کوتاه از پردازش PHP، حافظه و I/O فایل؛ همراه با گزارش منابعی که محیط اجازه دیدنشان را میدهد.</p><p class="muted">بدون نصب وردپرس و بدون اطلاعات پایگاه داده. این صفحه ورود اختصاصی ندارد؛ آدرس را عمومی نکنید و پس از کار فایل ابزار و فایل wbbench-state آن را حذف کنید. برای حفاظت بیشتر از پوشه رمزدار هاست استفاده کنید.</p><label><input type="checkbox" id="agree">روی سرویس خودم و در زمان کمترافیک، تست سبک را اجرا میکنم.</label><br><button id="start" disabled>شروع تست</button><button id="stop" disabled>توقف پس از این مرحله</button><button id="download" disabled>دریافت گزارش</button><progress id="progress" value="0" max="12"></progress><p id="status">آماده بررسی؛ نسخه <?=wbh_e(WBH_VERSION)?></p><p id="error" class="bad" role="alert"></p></section>
<section class="box"><h2>تنظیمات و نمای قابل مشاهده</h2><p class="muted">تعداد CPU و RAM قابل مشاهده با منابع اختصاصیافته به حساب یکی نیست. نبود اطلاعات، نشانه تقلب یا نامحدود بودن سرویس نیست.</p><div class="scroll"><table><tbody id="info"></tbody></table></div></section>
<section class="box"><h2>نتیجه تستهای کوتاه</h2><p class="muted">بالاتر بودن نرخ معمولاً بهتر است؛ نوسان کمتر فقط ثبات همین نمونههای کوتاه را نشان میدهد. نمره ساختگی برای کیفیت یا نوع دیسک تولید نمیشود.</p><div class="scroll"><table><thead><tr><th>آزمون</th><th>میانه</th><th>کمینه / بیشینه</th><th>تعداد / دامنه نوسان</th></tr></thead><tbody id="metrics"></tbody></table></div><p id="storage"></p><p id="rtt"></p><details><summary>نمونههای خام و بخشهای ردشده</summary><pre id="raw"></pre></details></section>
<footer>© <?=date('Y')?> <a href="https://wbweb.ir" rel="noopener noreferrer" target="_blank">wbweb.ir</a> — ابزار سنجش محدود هاست اشتراکی</footer>
</main><script>
(function(){'use strict';var nonce=<?=json_encode($nonce)?>,report=null,busy=false,halt=false,rtts=[];var $=function(id){return document.getElementById(id)};
var names={iterations_per_second:'محاسبات PHP — تکرار/ثانیه',fill_mib_per_second:'پرکردن بافر PHP — MiB/s',write_mib_per_second:'نوشتن فایل با fflush — MiB/s',warm_read_mib_per_second:'خواندن گرم — MiB/s',write_and_fsync_mib_per_second:'نوشتن با fsync موفق — MiB/s',random_warm_p95_ms:'میانه p95 خواندنهای گرم ۴ KiB — ms؛ کمتر بهتر'};
var infoNames={php:'نسخه PHP',sapi:'روش اجرای PHP',int_bits:'معماری عدد صحیح PHP',os:'خانواده سیستم',kernel:'کرنل قابل مشاهده',distribution:'توزیع قابل مشاهده',php_memory_limit:'memory_limit — سقف PHP',php_current_memory_bytes:'حافظه فعلی PHP',max_execution_time:'زمان مجاز PHP — ثانیه',upload_max_filesize:'حد فایل آپلود',post_max_size:'حد بدنه POST',max_input_vars:'حد متغیر ورودی',opcache_enable:'OPcache از تنظیمات',opcache_memory:'تنظیم حافظه OPcache — MiB',jit:'تنظیم JIT',jit_buffer:'بافر JIT',cpu_model_visible:'مدل CPU در نمای کرنل',logical_cpus_visible:'CPU منطقی قابل مشاهده، نه سهم حساب',ram_visible_bytes:'RAM در نمای کرنل',system_load:'بار محیط ۱ / ۵ / ۱۵ دقیقه',filesystem_free_bytes:'فضای آزاد فایلسیستم، نه سهمیه حساب',filesystem_size_bytes:'اندازه فایلسیستم',extensions:'افزونههای PHP',account_entitlement:'منابع واقعی حساب',visibility_note:'حدود تشخیص'};
function num(v){return typeof v==='number'?v.toLocaleString('en-US',{maximumFractionDigits:2}):'نامشخص'}
function readable(v,k){if(v===null||v===undefined||v===''||v===false)return 'نامشخص';if(/_bytes$/.test(k)&&typeof v==='number')return v>=1073741824?num(v/1073741824)+' GiB':num(v/1048576)+' MiB';return Array.isArray(v)?v.join(' / '):String(v)}
function row(parent,values){var tr=document.createElement('tr');values.forEach(function(v){var td=document.createElement('td');td.textContent=v;tr.appendChild(td)});parent.appendChild(tr)}
function render(){if(!report)return;$('info').textContent='';Object.keys(infoNames).forEach(function(k){row($('info'),[infoNames[k],readable(report.info[k],k)])});var cgNames={status:'وضعیت مشاهده cgroup v2',cpu_quota_cores:'سقف زمانی CPU گروه — معادل هسته، نه سهم اختصاصی',memory_ceiling_bytes:'سقف حافظه گروه و والدهای قابل مشاهده',cpuset_visible:'شناسه CPUهای مجاز گروه جاری',pids_max:'سقف تعداد وظایف در گروه جاری',io_max:'محدودیت I/O در گروه جاری — مقدار خام',scope:'دامنه تفسیر cgroup'};Object.keys(cgNames).forEach(function(k){row($('info'),[cgNames[k],readable(report.info.cgroup[k],k)])});row($('info'),['نوع فایلسیستم',readable(report.info.storage.filesystem,'filesystem')]);$('metrics').textContent='';Object.keys(names).forEach(function(k){var s=report.summary[k];row($('metrics'),[names[k],s?num(s.median):'اندازهگیری نشد',s?num(s.min)+' / '+num(s.max):'—',s?s.samples+' / '+num(s.spread_percent)+'%':'—'])});$('storage').textContent=report.info.storage.hint;$('raw').textContent=JSON.stringify(report.samples,null,2);$('progress').value=report.step;$('download').disabled=false}
async function request(action){var t=performance.now();var res=await fetch(location.pathname,{method:'POST',credentials:'same-origin',cache:'no-store',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:new URLSearchParams({action:action,nonce:nonce})});var data;try{data=await res.json()}catch(e){throw Error('پاسخ کامل نیست؛ تست را تکرار خودکار نکنید. محدودیت PHP یا لاگ خطا را بررسی کنید.')}if(!res.ok||data.error)throw Error(data.error||'درخواست ناموفق بود.');if(action==='step'&&data.report.step>9)rtts.push(performance.now()-t);return data}
$('agree').onchange=function(){$('start').disabled=busy||!$('agree').checked};
$('stop').onclick=function(){halt=true;$('status').textContent='پس از پایان درخواست جاری متوقف میشود.'};
$('start').onclick=async function(){if(busy)return;busy=true;halt=false;rtts=[];$('start').disabled=true;$('stop').disabled=false;$('error').textContent='';try{var d=await request('start');report=d.report;render();while(!d.done&&!halt){$('status').textContent='مرحله '+(report.step+1)+' از ۱۲';d=await request('step');report=d.report;render();await new Promise(function(r){setTimeout(r,160)})}report.browser_round_trip_ms=rtts;report.completed=!!d.done;$('rtt').textContent='زمان کل درخواست HTTP از این مرورگر (ms): '+rtts.map(num).join(' / ')+'؛ وابسته به شبکه و پاسخ PHP، نه ظرفیت پورت سرور.';$('status').textContent=halt?'متوقف شد؛ درخواست جاری به پایان رسیده است.':'تست تمام شد. گزارش را دریافت و فایلهای ابزار را از هاست حذف کنید.'}catch(e){$('error').textContent=e.message}finally{busy=false;$('start').disabled=!$('agree').checked;$('stop').disabled=true}};
$('download').onclick=function(){if(!report)return;var clean=JSON.parse(JSON.stringify(report));clean.browser_round_trip_ms=rtts;var u=URL.createObjectURL(new Blob([JSON.stringify(clean,null,2)],{type:'application/json;charset=utf-8'}));var a=document.createElement('a');a.href=u;a.download='wbweb-host-benchmark.json';a.click();setTimeout(function(){URL.revokeObjectURL(u)},1000)};
}());</script></body></html>
گزارش تست را چطور تفسیر کنیم؟
در تست هاست اشتراکی، مهمترین بخش بعد از اجرای کد شروع میشود: باید بدانیم هر عدد دقیقاً به کدام پرسش پاسخ میدهد. یک عدد بزرگ لزوماً خبر خوبی درباره کل سرویس نیست و یک خانه خالی هم نشانه کمفروشی محسوب نمیشود.
CPU زیاد در گزارش، به معنی هسته اختصاصی نیست
اگر ۳۲ CPU منطقی دیده میشود، این تعداد متعلق به نمای قابل مشاهده کرنل است. سهم شما ممکن است کسری از یک هسته یا ظرفیتی معادل چند هسته باشد. آزمون ما یک درخواست تکرشتهای اجرا میکند؛ پس دو برابر بودن تعداد هستههای پلن الزاماً نتیجه این بخش را دو برابر نمیکند.
در بخش cgroup v2، اگر محدودیت عددی قابل مشاهده باشد، ابزار نسبت سهم زمانی CPU به دوره را محاسبه میکند و سقف سختگیرانهتر والدهای قابل دسترس را هم در نظر میگیرد. مثلاً عدد ۰٫۵ میتواند نشاندهنده سقف زمانی معادل نصف یک CPU برای همان گروه باشد. این عدد تضمین اختصاص یک هسته فیزیکی یا تعلق انحصاری گروه به حساب شما نیست.
سه عدد متفاوت درباره حافظه
حافظه گزارششده کرنل، memory_limit و سقف قابل مشاهده cgroup را جدا بخوانید. مقدار -1 برای memory_limit فقط نبودن محدودیت در آن تنظیم PHP را نشان میدهد؛ محدودیت حساب، کانتینر یا سیستم همچنان میتواند وجود داشته باشد.
نرخ پرکردن بافر هم برای مقایسه همین عملیات مناسب است. از آن نمیتوان DDR4 یا DDR5 بودن RAM، فرکانس حافظه یا ظرفیت قابل استفاده سایت را نتیجه گرفت. اندازه بافر نمونهها در گزارش خام ثبت شده؛ مقایسه دو سرویس با اندازههای متفاوت را یک آزمایش کاملاً یکسان در نظر نگیرید.
سرعت فایل را با نام درستش بخوانید
سرعت خواندن گرم ممکن است بسیار بالا باشد، چون سیستم تازه همان فایل کوچک را نوشته است. نرخ نوشتن بدون fsync هم میتواند بیشتر بازتاب پذیرش داده در بافرها باشد. این دو عدد بهتنهایی معیار تشخیص HDD، SSD یا NVMe نیستند.
واحد نرخهای فایل در این ابزار MiB/s است؛ هر MiB برابر ۱٬۰۴۸٬۵۷۶ بایت است. برای مقایسه با عددی که میزبان با MB/s اعلام کرده، تفاوت واحد را در نظر بگیرید. همچنین سقف IO حساب ممکن است بر مجموع خواندن و نوشتن اعمال شود، در حالی که گزارش ما چند عملیات کوتاه مشخص را اندازه میگیرد.
فضای آزاد سرور، فضای باقیمانده پلن نیست
ممکن است ابزار صدها گیگابایت فضای آزاد نشان دهد، اما پلن شما فقط ده گیگابایت ظرفیت داشته باشد. توابع فضای دیسک معمولاً درباره فایلسیستم پاسخ میدهند؛ سهمیه حساب، محدودیت تعداد فایلها و فضای قابل مصرف شما را باید از پنل بخوانید. ابزار برای کشف سهمیه، دیسک را تا پر شدن امتحان نمیکند.
آیا نوع دیسک و نسل SSD قابل تشخیص است؟
از سرعت این تست، نمیتوان با اطمینان نوع دیسک یا نسل SSD را تعیین کرد. یک دیسک NVMe پشت محدودیت IO حساب ممکن است کند دیده شود؛ یک HDD با داده حاضر در کش نیز ممکن است خواندن بسیار سریعی نشان دهد. لایههای RAID، ذخیرهسازی شبکه و مجازیسازی هم میتوانند رابطه سرعت و سختافزار را تغییر دهند.
ابزار فقط اگر بتواند مسیر فایلسیستم را به دستگاه قابل مشاهده متصل کند، نشانه کرنل را بررسی میکند:
| نشانه | گزارش قابل دفاع |
|---|---|
rotational = 0 | دستگاه از دید کرنل غیرچرخشی است؛ SSD یا ذخیرهسازی مجازی محتمل است |
rotational = 1 | دستگاه از دید کرنل چرخشی است؛ HDD محتمل است، با در نظر گرفتن لایه مجازی |
| نام NVMe در مسیر دستگاه | نشانهای از رابط NVMe دیده شده؛ مدل فیزیکی و نسل PCIe مشخص نشده است |
| نبود دسترسی یا نگاشت معتبر | نوع و نسل دیسک نامشخص میماند |
این نشانهها درصد اطمینان ساختگی نمیگیرند. مثلاً عبارت «با احتمال ۹۲ درصد SSD نسل جدید است» بدون داده معتبر درباره مدل دستگاه، روش تشخیص قابل اتکایی نیست. برای تأیید نسل، مدل و رابط سختافزار باید اطلاعات مستند از میزبان دریافت کنید.

چطور دو سرویس را منصفانه مقایسه کنیم؟
برای مقایسه، نسخه یکسان اسکریپت را روی هر دو سرویس اجرا کنید. نسخه PHP، تنظیمات OPcache و JIT، اندازه فایل و بافر، و روش fflush یا fsync را ثبت کنید. اگر این شرایط متفاوت باشند، گزارش را مقایسه «دو محیط اجرا» بدانید، نه آزمایش خالص دو پردازنده یا دو دیسک.
یک اجرای آرام صبح و یک اجرای دیگر در ساعت شلوغتر میتواند سرنخ بهتری از یک نتیجه منفرد بدهد. هر بار گزارش را نگه دارید و همان زمان به نمودار مصرف منابع پنل نگاه کنید. در هر ۲۴ ساعت، سقف شش اجرا را رعایت کنید و چند تب یا چند نسخه ابزار را برای ایجاد بار همزمان باز نکنید.
مثال زیر فرضی است و امتیاز مرجع برای هاستهای بازار محسوب نمیشود:
| مشاهده در شرایط مشابه | برداشت اولیه | قدم بعد |
|---|---|---|
| نرخ پردازش عصر بهطور تکرارشونده کمتر است | افت عملکرد در آن بازه دیده میشود | نمودار محدودیت CPU و فعالیت سایت را هم بررسی کنید |
| خواندن گرم بسیار سریع و نوشتن همراه fsync کندتر است | دو مسیر متفاوت با نقش متفاوت کش اندازهگیری شدهاند | نتیجه را با سقف IO و توضیح میزبان تطبیق دهید |
| بیشتر اطلاعات سیستم نامشخص است، ولی تستها اجرا میشوند | محیط دسترسی اطلاعاتی محدودی دارد | مشخصات و حدود حساب را از پنل بگیرید |
| بنچمارک مناسب است، ولی صفحه وردپرس کند است | این آزمون مشکل کامل سایت را پوشش نداده است | پایگاه داده، افزونهها، کش و درخواستهای خارجی را بررسی کنید |
قبل از انتخاب یا تعویض هاست سیپنل، از فروشنده درباره سهم CPU، حافظه حساب، IO، IOPS، پردازشهای همزمان و محدودیت تعداد فایلها سؤال کنید. صرفاً نام پردازنده و عبارت «NVMe» برای توصیف تجربهای که یک حساب دریافت میکند کافی نیست.
نتیجه ضعیف را چطور با پشتیبانی مطرح کنیم؟
گزارش باید سؤال مشخص ایجاد کند. زمان اجرا با منطقه زمانی، نسخه PHP، فایل JSON، مشخصات پلن و تصویر مصرف منابع همان بازه را کنار هم بفرستید. اگر نتایج چند نوبت افت تکرارشونده دارند، آنها را هم ضمیمه کنید.
بهجای نسبت دادن فوری مشکل به نوع دیسک، بنویسید: «در این ساعتها، نرخ عملیات فایل در سه نمونه کاهش داشته است. لطفاً سقف IO و IOPS حساب و برخورد احتمالی با محدودیتها را بررسی کنید.» اگر عدد پنل با مشخصات خرید متفاوت است، همان اختلاف مشخص را مطرح کنید.
گزارش این ابزار شامل مشخصات فنی محیط است. پیش از انتشار عمومی آن، محتوایش را بررسی کنید؛ ارسال مستقیم برای پشتیبانی معمولاً برای پیگیری مشکل کافی است. نتیجه یک آزمون کوتاه بهتنهایی تخلف فروشنده یا سلامت کامل سرویس را ثابت نمیکند.
خطاهای رایج هنگام اجرای اسکریپت
| پیام یا وضعیت | علت محتمل و اقدام مناسب |
|---|---|
| خطای ۴۰۳ و نامعتبر بودن درخواست | صفحه را تازه کنید؛ کوکی و نشست PHP باید کار کنند. مسیر ابزار نباید نسخه کششده دریافت کند |
| خطای ۴۰۰ | متن خطای خود ابزار را بخوانید؛ فاصله اجرا، سقف روزانه یا وضعیت اجرای قبلی میتواند علت باشد |
| فایل کنترل اجرا ساخته نمیشود | دسترسی نوشتن PHP در پوشه یا سهمیه حساب را بررسی کنید؛ مجوز ۷۷۷ راهحل عمومی نیست |
| اطلاعات دیسک یا سیستم نامشخص است | ممکن است تابع غیرفعال یا مسیر سیستمی غیرقابل دسترس باشد؛ این وضعیت نباید بهتنهایی تست را متوقف کند |
| پاسخ ناقص یا خطای ۵۰۰ | لاگ PHP و محدودیت زمان یا حافظه را بررسی کنید؛ درخواست ناموفق را پشت سر هم تکرار نکنید |
| امکان تولید تصادفی امن یا نشست وجود ندارد | این پیشنیاز در محیط فعال نیست؛ پشتیبانی باید امکان اجرای امن را بررسی کند |
| مهلت ابزار تمام شده است | بیش از ۲۴ ساعت از زمان ذخیره فایل گذشته؛ برای نوبت جدید آن را دوباره در ویرایشگر هاست ذخیره کنید |
دکمه توقف، از شروع مراحل بعدی جلوگیری میکند؛ درخواست جاری را در میانه عملیات فایل قطع نمیکند. اگر اجرا نیمهتمام بماند، قفل منطقی آن حداکثر ده دقیقه از زمان شروع اعتبار دارد. برای رفع این انتظار، شمارنده اجرا را حذف یا تستهای متعدد ایجاد نکنید.
اگر آپلودر، زمان قدیمی فایل را حفظ کرده باشد، ممکن است همان ابتدا پیام انقضا ببینید؛ یک بار ذخیره دوباره فایل در ویرایشگر پنل کافی است. بعد از پایان بررسی، ابزار را از هاست حذف کنید.
سوالات متداول
آیا تست هاست اشتراکی مقدار دقیق رم و CPU خریداریشده را نشان میدهد؟
فقط وقتی محدودیت مرتبط بهطور معتبر در دسترس باشد میتوان بخشی از آن را گزارش کرد. اطلاعات کرنل ممکن است متعلق به کل سرور یا یک گروه مشترک باشد. تطبیق سهم واقعی حساب با پنل و مشخصات پلن همچنان لازم است.
آیا اسکریپت روی تمام هاستهای قدیمی و جدید اجرا میشود؟
خیر؛ چنین تضمینی درباره تمام نسخهها و تنظیمات ممکن نیست. کد با نحو PHP 5.6 نوشته شده و توابع اختیاری را بررسی میکند، اما محیط باید پیشنیازهای اصلی اجرای PHP و فایل را داشته باشد. مرورگر نسبتاً بهروز هم لازم است.
آیا سرعت بالا ثابت میکند هاست NVMe نسل جدید دارد؟
خیر. کش، محدودیت حساب و لایه ذخیرهسازی بر نتیجه اثر دارند. ابزار از سرعت، نسل دیسک را حدس نمیزند؛ فقط نشانههای قابل مشاهده کرنل را با توضیح محدودیتشان گزارش میکند.
آیا اجرای ابزار روی فایلها و وردپرس تغییری ایجاد میکند؟
کد فایلهای سایت و تنظیمات وردپرس را تغییر نمیدهد. فایل موقت آزمون و فایل کنترل تعداد اجرا را در پوشه خودش میسازد. آزمون مصرف منابع دارد، بنابراین آن را روی سرویس خودتان و در زمان کمترافیک اجرا کنید.
چرا تست خوب است، اما سایت همچنان کند باز میشود؟
این بنچمارک پایگاه داده، افزونههای وردپرس، بار همزمان کاربران، سرویسهای خارجی و رندر صفحه را آزمایش نمیکند. نتیجه خوب فقط درباره عملیات اندازهگیریشده است؛ برای تشخیص کندی سایت باید مسیر واقعی اجرای همان صفحه بررسی شود.


