首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 程式執行時間精準測量:Microtime 與高效能計時器實作

     編輯:PHP 綜合知識

為什麼需要微秒級計時?

在效能分析、介面回應時間監控、演算法複雜度驗證等場景中,秒級精度(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";
?>

進階:整合 XHProf / Xdebug Profiler

手動插桿雖然靈活,但無法覆蓋全呼叫鏈。生產環境效能分析建議使用:

  • XHProf(Facebook 開源,PHP 7/8 相容分支:tideways/xhprof):函式級呼叫次數、耗時、記憶體、CPU 分析,支援生產環境採樣。
  • Xdebug Profiler:生成 cachegrind 格式檔案,配合 KCacheGrind / QCacheGrind 視覺化分析,適合開發環境深度剖析。
  • Blackfire.io:SaaS 級效能分析平台,零侵入、生產安全、CI/CD 整合。

常見誤區與最佳實踐

誤區正確做法
使用 time() 計算耗時精度僅 1 秒,必須用 microtime(true) 或 hrtime(true) (PHP 7.3+)
在迴圈中重複呼叫 microtime()函式呼叫本身有開銷,關鍵路徑外測量;必要時用 hrtime()(奈秒級、單調時鐘)
忽略 GC、OpCache、JIT 預熱影響效能測試前執行足夠預熱輪次;關閉 GC 或固定 gc_enable() 狀態
單次執行即下結論至少執行 10-100 次取中位數/百分位數,排除系統抖動
混淆「壁鐘時間」與「CPU 時間」microtime() 為壁鐘時間;CPU 時間需用 getrusage() 或專業 Profiler

PHP 7.3+ 新選擇: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 綜合指標,才能獲得可信、可行動的效能洞察。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved