在效能分析、介面回應時間監控、演算法複雜度驗證等場景中,秒級精度(time())遠遠不夠。PHP 提供 microtime() 取得「秒數 + 微秒數」的浮點時間戳,配合簡單封裝即可實現毫秒甚至微秒級的精準測量。
microtime(bool $getAsFloat = false)| 呼叫方式 | 回傳值範例 | 說明 |
|---|---|---|
microtime() | "0.12345600 1700000000" | 字串:「微秒 秒」,需自行分割轉換 |
microtime(true) | 1700000000.123456 | 浮點數:Unix 時間戳 + 微秒小數,直接可用於運算(推薦) |
<?php
/**
* 高精度程式碼執行時間測量器
* 支援:多區間計時、標籤記錄、統計摘要、記憶體峰值監控
*/
class Profiler {
private array $marks = [];
private ?float $startTime = null;
private ?float $endTime = null;
private int $startMem = 0;
private int $peakMem = 0;
/** 開始計時 */
public function start(): self {
$this->startTime = microtime(true);
$this->endTime = null;
$this->marks = [];
$this->startMem = memory_get_usage(true);
$this->peakMem = memory_get_peak_usage(true);
return $this;
}
/** 記錄一個檢查點 */
public function mark(string $label): self {
if ($this->startTime === null) {
throw new LogicException('請先呼叫 start()');
}
$this->marks[$label] = microtime(true);
$this->peakMem = max($this->peakMem, memory_get_peak_usage(true));
return $this;
}
/** 結束計時 */
public function stop(): self {
$this->endTime = microtime(true);
$this->peakMem = max($this->peakMem, memory_get_peak_usage(true));
return $this;
}
/** 取得總耗時(毫秒) */
public function getElapsedMs(): float {
if ($this->startTime === null) return 0;
$end = $this->endTime ?? microtime(true);
return round(($end - $this->startTime) * 1000, 3);
}
/** 取得區間耗時(毫秒) */
public function getIntervalMs(string $fromLabel, string $toLabel): ?float {
if (!isset($this->marks[$fromLabel], $this->marks[$toLabel])) return null;
return round(($this->marks[$toLabel] - $this->marks[$fromLabel]) * 1000, 3);
}
/** 取得記憶體增長(KB) */
public function getMemoryGrowthKb(): float {
return round((memory_get_usage(true) - $this->startMem) / 1024, 2);
}
/** 取得峰值記憶體(MB) */
public function getPeakMemoryMb(): float {
return round($this->peakMem / 1024 / 1024, 2);
}
/** 輸出完整報表 */
public function report(): array {
$report = [
'total_ms' => $this->getElapsedMs(),
'memory_growth_kb' => $this->getMemoryGrowthKb(),
'peak_memory_mb' => $this->getPeakMemoryMb(),
'marks' => [],
];
$prevLabel = 'start';
$prevTime = $this->startTime;
foreach ($this->marks as $label => $time) {
$report['marks'][] = [
'label' => $label,
'timestamp' => $time,
'since_start_ms' => round(($time - $this->startTime) * 1000, 3),
'since_prev_ms' => round(($time - $prevTime) * 1000, 3),
];
$prevLabel = $label;
$prevTime = $time;
}
if ($this->endTime) {
$report['marks'][] = [
'label' => 'end',
'timestamp' => $this->endTime,
'since_start_ms' => $this->getElapsedMs(),
'since_prev_ms' => round(($this->endTime - $prevTime) * 1000, 3),
];
}
return $report;
}
/** 靜態快速測量:傳入 callable,回傳 [result, elapsed_ms] */
public static function measure(callable $fn): array {
$profiler = new self();
$profiler->start();
$result = $fn();
$profiler->stop();
return [$result, $profiler->getElapsedMs()];
}
}
// 使用範例
$profiler = (new Profiler())->start();
// 模擬業務邏輯
usleep(50000); // 50ms
$profiler->mark('after_init');
$data = range(1, 10000);
$profiler->mark('after_data_prepare');
usleep(30000); // 30ms
$profiler->mark('after_processing');
$profiler->stop();
// 輸出報表
print_r($profiler->report());
/*
輸出範例:
Array
(
[total_ms] => 82.453
[memory_growth_kb] => 456.25
[peak_memory_mb] => 2.12
[marks] => Array
(
[0] => Array([label] => after_init, [since_start_ms] => 50.12, [since_prev_ms] => 50.12)
[1] => Array([label] => after_data_prepare, [since_start_ms] => 52.34, [since_prev_ms] => 2.22)
[2] => Array([label] => after_processing, [since_start_ms] => 82.45, [since_prev_ms] => 30.11)
[3] => Array([label] => end, [since_start_ms] => 82.45, [since_prev_ms] => 0.00)
)
)
*/
// 靜態快速測量
[$result, $ms] = Profiler::measure(function() {
// 被測程式碼
return array_sum(range(1, 1000000));
});
echo "結果:$result,耗時:$ms ms\n";
?>
手動插桿雖然靈活,但無法覆蓋全呼叫鏈。生產環境效能分析建議使用:
tideways/xhprof):函式級呼叫次數、耗時、記憶體、CPU 分析,支援生產環境採樣。cachegrind 格式檔案,配合 KCacheGrind / QCacheGrind 視覺化分析,適合開發環境深度剖析。| 誤區 | 正確做法 |
|---|---|
使用 time() 計算耗時 | 精度僅 1 秒,必須用 microtime(true) 或 hrtime(true) (PHP 7.3+) |
在迴圈中重複呼叫 microtime() | 函式呼叫本身有開銷,關鍵路徑外測量;必要時用 hrtime()(奈秒級、單調時鐘) |
| 忽略 GC、OpCache、JIT 預熱影響 | 效能測試前執行足夠預熱輪次;關閉 GC 或固定 gc_enable() 狀態 |
| 單次執行即下結論 | 至少執行 10-100 次取中位數/百分位數,排除系統抖動 |
| 混淆「壁鐘時間」與「CPU 時間」 | microtime() 為壁鐘時間;CPU 時間需用 getrusage() 或專業 Profiler |
hrtime(bool $asNumber = false)<?php
// 奈秒級單調時鐘,不受系統時間調整影響,適合高精度區間測量
$start = hrtime(true);
// ... 程式碼 ...
$end = hrtime(true);
$elapsedMs = ($end - $start) / 1_000_000; // 轉毫秒
echo "耗時:$elapsedMs ms\n";
// 陣列格式:[秒, 奈秒]
[$sec, $nsec] = hrtime(false);
?>
精準測量程式執行時間是效能優化的基石。microtime(true) 配合簡單封裝即可滿足大多數應用層面的計時需求;進階場景請引入 XHProf、Blackfire 等專業工具。記住:測量前先預熱、多次取樣取統計值、區分壁鐘/CPU 時間、關注記憶體與 CPU 綜合指標,才能獲得可信、可行動的效能洞察。