常見問題
多次調用xhprof_enable方法,最後生效的配置是哪個?
當你在一次請求中多次調用xhprof_enable方法,只有第一次調用時進行的設定能生效。在調用
xhprof_disable()後,你又可以使用xhprof_enable方法進行設定。
$i = 0;
function good(){
global $i;
$i++;
if ($i < 2) {
good();
}
}
function func() {
good();
}
$start_time = microtime(true);
xhprof_enable(XHPROF_FLAGS_NO_BUILTINS);
xhprof_enable(XHPROF_FLAGS_MEMORY + XHPROF_FLAGS_CPU + XHPROF_FLAGS_NO_BUILTINS);
for ($i = 0; $i < 100; $i++) {
func();
}
good();
$rst = xhprof_disable();
var_dump($rst);
輸出內容為:
array(5) {
["good==>good@1"]=>
array(2) {
["ct"]=>
int(1)
["wt"]=>
int(70)
}
["func==>good"]=>
array(2) {
["ct"]=>
int(50)
["wt"]=>
int(121)
}
["main()==>func"]=>
array(2) {
["ct"]=>
int(50)
["wt"]=>
int(135)
}
["main()==>good"]=>
array(2) {
["ct"]=>
int(1)
["wt"]=>
int(0)
}
["main()"]=>
array(2) {
["ct"]=>
int(1)
["wt"]=>
int(237)
}
}
可見,列印的內容,並沒有cpu和memory的資訊。
輸出內容中的ct,wt,cpu,mu, pmu 都代表什麼意思
ct 表示 調用的次數
wt 表示 函數方法執行的時間耗時。相當於,在調用前記錄一個時間,函數方法調用完畢後,計算時間差。
cpu 表示 函數方法執行消耗的cpu時間。和wt的差別在於,當進程讓出cpu使用權後,將不再計算cpu時間。通過調用系統調用getrusage擷取進程的佔用cpu資料。
mu 表示 函數方法所使用的記憶體。相當於,在調用前記錄一個記憶體佔用,函數方法調用完畢後,計算記憶體差。調用的是zend_memory_usage擷取記憶體佔用情況。
pmu 表示 函數方法所使用的記憶體峰值。調用的是zend_memory_peak_usage擷取記憶體情況。
輸出內容中good==>good@1 是什麼意思
==>表示一個調用關係。由於帶@,說明是一個遞迴調用。@後面的數字是遞迴調用的深度。
如何設定xhprof_enable的參數,減少效能消耗
xhprof_enable提供了三個常量,用於設定你是否需要統計PHP內建函數,都統計那些指標。
三個常量如下:
XHPROF_FLAGS_NO_BUILTINS
設定這個常量後,將不統計PHP內建函數。畢竟PHP的內建函數效能一般都不錯。沒必要再消耗效能去統計。所以,建議設定。
XHPROF_FLAGS_CPU
設定這個常量後,會統計進程佔用CPU時間。由於CPU時間是通過調用系統調用getrusage擷取,導致效能比較差。開啟這個選項後,大概效能下降一半。因此,如果對cpu耗時不是特別敏感的情況下,建議不要啟用這個選項。
XHPROF_FLAGS_MEMORY
設定這個常量後,將會統計記憶體佔用情況。由於擷取記憶體情況,使用的是zend_memory_usage和zend_memory_peak_usage,並不是系統調用。因此,對效能影響不大。如果需要對記憶體使用量情況進行分析的情況下,可以開啟。
效能分析原理
如何?對各個函數方法效能資料記錄
目前xhprof會對,載入PHP檔案,執行PHP函數方法,和執行eval方法進行效能資料記錄。正好,這些在PHP核心中,有對應的函數進行處理。當你調用xhprof_enable方法時,會把預設的方法替換為xhprof的方法。來看看相關代碼吧。
static void hp_begin(long level, long xhprof_flags)
{
if (!hp_globals.enabled)
{
int hp_profile_flag = 1;
hp_globals.enabled = 1;
hp_globals.xhprof_flags = (uint32) xhprof_flags;
/* Replace zend_compile with our proxy */
/* 處理載入PHP檔案 */
/* 先把zend引擎預設處理方法儲存到_zend_compile_file變數中。*/
_zend_compile_file = zend_compile_file;
/* 在把xhprof相對應的方法賦值給zend_compile_file。
這樣,每次載入PHP檔案時,就會執行xhprof相應的方法。*/
zend_compile_file = hp_compile_file;
/* Replace zend_compile_string with our proxy */
/* 處理eval代碼的執行 */
_zend_compile_string = zend_compile_string;
zend_compile_string = hp_compile_string;
/*init the execute pointer*/
/* 處理 函數方法的執行 */
_zend_execute_ex = zend_execute_ex;
zend_execute_ex = hp_execute_ex;
.........
}
}
/*那我們看下,hp_compile_file方法,又是如何?的*/
ZEND_DLEXPORT zend_op_array* hp_compile_file(zend_file_handle *file_handle, int type)
{
const char *filename;
char *func;
int len;
zend_op_array *ret;
int hp_profile_flag = 1;
filename = hp_get_base_filename(file_handle->filename);
len = sizeof("load") - 1 + strlen(filename) + 3;
func = (char *) emalloc(len);
snprintf(func, len, "load::%s", filename);
//方法執行前記錄當前各項效能如數,如cpu 記憶體等
BEGIN_PROFILING(&hp_globals.entries, func, hp_profile_flag);
//開始zend引擎相應的方法,負載檔案
ret = _zend_compile_file(file_handle, type);
if (hp_globals.entries)
{
//負載檔案完畢後,再次記錄當前各項效能資料。以便以後計算差值。
END_PROFILING(&hp_globals.entries, hp_profile_flag);
}
efree(func);
return ret;
}
xhprof在實現的時候,效能方面做了哪些最佳化
擷取時間時,為了效能,使用了彙編來擷取時間戳記計數器。時間秒 = 時間戳記計數器值 / CPU主頻。
正是這種實現,導致目前xhprof還只適用於x86架構。此外,因為RDTSC的資料不能在CPU間同步,所以,xhprof會將進程綁定在單個CPU上。
如果SpeedStep技術是開啟的,XHProf的基於RDTSC定時器的功能就不能正常工作了。這項技術在某些英特爾處理器上是可用的。[註:蘋果台式機和膝上型電腦一般都將SpeedStep技術預設開啟。使用XHProf,您需要禁用SpeedStep技術。 ]
inline uint64 cycle_timer()
{
uint32 __a, __d;
uint64 val;
asm volatile("rdtsc" : "=a" (__a), "=d" (__d));
(val) = ((uint64) __a) | (((uint64) __d) << 32);
return val;
}