ThinkPHP 經典的 G() 函數以極簡 API 實現「標記開始/結束 → 統計耗時/內存」,是輕量級性能剖析的首選工具。本文深度解析其原理,提供增強版實現(支持多段計時、內存單位自動換算、PHP 8+ 類型提示),並示範實戰用法。
function G($start, $end = '', $dec = 4) {
static $_info = [];
static $_mem = [];
if (is_float($end)) {
// 記錄時間戳(微秒)
$_info[$start] = $end;
} elseif (!empty($end)) {
// 統計區間
if (!isset($_info[$end])) $_info[$end] = microtime(true);
if (defined('MEMORY_LIMIT_ON') && MEMORY_LIMIT_ON && $dec === 'm') {
if (!isset($_mem[$end])) $_mem[$end] = memory_get_usage();
return number_format(($_mem[$end] - $_mem[$start]) / 1024); // KB
} else {
return number_format($_info[$end] - $_info[$start], $dec); // 秒,保留 $dec 位小數
}
} else {
// 標記開始
$_info[$start] = microtime(true);
if (defined('MEMORY_LIMIT_ON') && MEMORY_LIMIT_ON) {
$_mem[$start] = memory_get_usage();
}
}
}
<?php
declare(strict_types=1);
/**
* 輕量級性能分析器
* 用法:
* GTimer::mark('start');
* // ... code ...
* GTimer::mark('end');
* echo GTimer::elapsed('start', 'end', 6); // 秒,6 位小數
* echo GTimer::memory('start', 'end'); // KB
*/
class GTimer {
private static array $time = [];
private static array $mem = [];
/** 標記時間點(自動記錄內存) */
public static function mark(string $name): void {
self::$time[$name] = microtime(true);
self::$mem[$name] = memory_get_usage(true); // 真實分配內存
}
/** 獲取兩點間耗時(秒) */
public static function elapsed(string $start, string $end = '', int $precision = 6): float {
$end = $end ?: 'now';
if ($end === 'now') self::mark('now');
if (!isset(self::$time[$start]) || !isset(self::$time[$end])) {
throw new InvalidArgumentException("標記 '$start' 或 '$end' 不存在");
}
return round(self::$time[$end] - self::$time[$start], $precision);
}
/** 獲取兩點間內存增長(自動單位:B/KB/MB/GB) */
public static function memory(string $start, string $end = ''): string {
$end = $end ?: 'now';
if ($end === 'now') self::mark('now');
$diff = self::$mem[$end] - self::$mem[$start];
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$i = 0;
while (abs($diff) >= 1024 && $i < count($units) - 1) {
$diff /= 1024;
$i++;
}
return round($diff, 2) . ' ' . $units[$i];
}
/** 重置所有標記 */
public static function reset(): void {
self::$time = [];
self::$mem = [];
}
}
<?php
require 'GTimer.php';
GTimer::mark('request_start');
// 模擬業務邏輯
$data = [];
for ($i = 0; $i < 100000; $i++) {
$data[] = md5($i);
}
GTimer::mark('data_prepared');
// 數據庫查詢模擬
usleep(50000); // 50ms
GTimer::mark('db_done');
// 輸出報告
echo "總耗時: " . GTimer::elapsed('request_start', 'db_done') . " 秒\n";
echo "準備階段: " . GTimer::elapsed('request_start', 'data_prepared') . " 秒\n";
echo "DB 階段: " . GTimer::elapsed('data_prepared', 'db_done') . " 秒\n";
echo "內存增長: " . GTimer::memory('request_start', 'db_done') . "\n";
// 輸出示例:
// 總耗時: 0.123456 秒
// 準備階段: 0.073456 秒
// DB 階段: 0.050000 秒
// 內存增長: 8.45 MB
| 特性 | 原版 G() | GTimer 類 |
|---|---|---|
| API 風格 | 單函數多態 | 靜態方法,語義清晰 |
| 多段計時 | 支持(不同標記名) | 支持,更直觀 |
| 內存單位 | 固定 KB | 自動換算 B/KB/MB/GB |
| 類型安全 | 無 | PHP 8+ 嚴格類型 |
| 錯誤處理 | 靜默返回 | 拋出異常 |
| 依賴常量 | MEMORY_LIMIT_ON | 無,直接用 memory_get_usage(true) |
index.php)第一行調用 GTimer::mark('bootstrap')。X-Request-Time、X-Memory-Usage)或日誌系統。microtime/memory_get_usage 開銷(雖極小但非零)。G() 函數以「靜態數組 + microtime + memory_get_usage」三板斧構建了極簡性能剖析器。理解其原理後,可根據項目需求輕鬆擴展為類版本、集成到中間件、對接 APM 系統(如 SkyWalking、Pinpoint),實現全鏈路可觀測。