PHP性能監控測試---XHProf介紹
Xhprof----非死book開源的,輕量級的PHP性能分析工具: 包括函數的調用次數,花費的時間(自身花費時間和包含內部函數花費的時間),所占內存/CPU,所占內存的峰值及所占百分比
這個真的是灰常的好用可以非常快的知道性能瓶頸在哪個文件的哪個函數,然后針對性的做優化:給個截圖具體說明
這是業務管理的部分,從路線圖中黃色和紅的檢測的過程中發現,這個函數竟然掉了99次數據庫,查看源代碼,竟然有連表查詢,然后查詢放到了foreach中,導致了很多次小的查詢。接下來就剩下針對性的優化了。
-
導言
XHProf是一個分層PHP性能分析工具。它報告函數級別的請求次數和各種指標,包括阻塞時間,CPU時間和內存使用情 況。一個函數的開銷,可細分成調用者和被調用者的開銷。原始數據收集部分是用純C實現的,是一個名叫xhprof的 Zend擴展 。XHProf有一個簡單的HTML的用戶界面( PHP寫成的)。基于瀏覽器的性能分析用戶界面能更容易查看,或是與同行們分享成果。也能繪制調用關系圖。
XHProf報告往往可以有助于理解被執行的代碼的結構。的等級性質的報告可用來確定,例如,什么鏈要求導致了所謂的特殊 功能得到。
XHProf可以比較兩次運行的結果(又名“差異報告”)或是從多次運行得到的匯總數據。差異和匯總報告,就像單一運行報 告一樣,也提供“平板”以及“分層”的性能分析視圖。
XHProf 是一種輕量級的性能分析工具。在數據收集階段,它記錄調用次數的追蹤和包容性的指標弧在動態 callgraph的一個程序。它獨有的數據計算的報告/后處理階段。在數據收集時,XHProfd通過檢測循環來處理遞歸的函數調用,并通過給遞歸調用 中每個深度的調用一個有用的命名來避開死循環。
XHProf的輕量級性質和匯聚功能,使得它非常適合用于收集“生產環境”的性能統計數據的統計。[見用于生產環境的補充 說明。 ]
例如,XHProfLive (不屬于開源的工具包),是非死book使用的一個全系統的性能監測系統,建立在XHProf的基礎之上 。 XHProfLive不斷收集函數級別的Profiler資料,這些資料來自生產環境中運行中的示例頁面[用xhprof來收集] 。然后XHProfLive匯總指定的時候,頁面類型,來取得各個頁面的性能數據,可以幫助解決各種問題,如:特定的某個網頁的函數級的性能情況怎樣?在 所有網頁中,或者對某一具體的網頁,foo 函數的開銷如何?在過去的一小時/天/周中,哪些函數返回的次數最多?某個頁面/函數的執行時間的變化趨勢如何?等等。
由非死book最初開發的XHProf在2009年3月開放源代碼。
-
XHProf概況
XHProf提供的功能:
-
load::lib/common.php - 內核加載和編譯文件的工作。[注:如果您使用的了PHP的opcode 緩存比如APC之類的,只有當緩存失效時才會去編譯。
-
run_init::lib/common.php -由于包含操作引起的初始化操作等。
-
哪些函數調用了指定的函數,各自調了多少次?
-
一個特定的函數調用了哪些函數?
-
在被一個特定的函數調用時,某個函數一共耗去了多少時間 。
-
扁平的性能概要(截 圖 )
提供函數級別的性能信息,例如調用次數,其中包括/獨家壁時間,內存使用情況,和CPU時間。
-
分層剖析(父/子{0視圖){/0} ( 截 圖 )
對每一個函數,它提供了一個斷點,個崩潰的要求和時間每母公司(來電)和兒童(被調用) ,如:
-
差異報告
出于各種原因,您可能需要比較從兩個XHProf運行中得到的數據-找出是什么造成了從一個版本到另一個版本的退 步,或是評估您將要做的優化的性能提升。
阿差異報告考慮兩分,作為輸入,并提供兩個平面功能級別差異的信息,和層次信息(細目差異的父母/兒童職能)的每 個功能。
差異報告里的“平板”視圖指出了最主要的退步或改進。
點擊差異報告平板視圖中的函數,進入“層次”(或父/子)視圖。我們可以得到的具體的父子函數的差異。
-
Callgraph視圖
性能數據也可以通過callgraph視圖來查看 。callgraph 會高亮顯示程序的關鍵路徑。
-
內存剖析
XHProf的內存剖析模式能有助于跟蹤那些申請大量內存的函數。
值得澄清的是,XHProfu并不嚴格跟蹤所有分配/釋放內存的操作。相反,它使用了一種簡化思路。他記錄了每個 函數進入和結束后的內存的增減。它還記錄每個函數分配內存的峰值的變化。
-
XHProf記錄include, include_once, require and require_once require_once操作,就像他們是函數一樣。那些被包含文件的名字被用來產生假的函數的名字。
-
術語表
-
特殊函數的命名約定
-
限制
真正的層次剖析器會在每一個數據采集點記錄完全的調用堆棧。接下來,能夠回答這些問題,如:第三次foo()調用 的開銷是多少?或是當調用棧是a()->b()->bar()時bar()函數的開銷是多少?
XHProf只記錄一級的調用上下文,因此只能回答關于一級一級函數調用的問題。事實證明,在實踐中這是最主要的 利用情況。
為了使這個更具體,看看下面的例子。
假設你有: 1 call from a() --> c() 1 call from b() --> c() 50 calls from c() --> d()
雖然XHProf可以告訴你, d()被c()調用了50次,卻不能告訴你,有多少次分別是由a()和b()引起的。[我們可以推測,也許有25次是因為a(),有25次是由于b(), 但未必如此。 ]
然而在實踐中,這沒什么大不了。
-
load::<filename> 和 run_init::<filename>:
XHProf 跟蹤PHP的include/require操作,和跟蹤函數調用一樣。
例如, include “lib/common.php"; 操作看起來像調了兩個XHProf函數:
-
foo@<n> :意味著這是一個foo()函數的遞歸調用。<n>代表遞歸深度。遞歸可能是直接的,(比如由于foo() --> foo() ) ,也可能是間接的(如由于foo()-> goo() ->foo()。
-
Exclusive Time/Self Time :[函數執行本身的時間花費。]不包括子樹執行時間。
-
Wall時間 :又名經過的時間或掛鐘時間。
-
CPU時間 : CPU時間在用戶空間+ CPU時間在內核空間
-
安裝XHProf擴展
這個擴展在"extension"子目錄里。
注:Windows的版本還沒有實現。目前我們已經在Linux/FreeBSD 上測試了xhprof。
0.9.2及以上版本的XHProf也正計劃著移植到Mac OS下。[我們在Mac OS 10.5下測試過。]
注: XHProf使用RDTSC指令(時間戳計數器)來實現一個真正的低資源消耗的計數器[針對elapsed time]。因此目前xhprof還 只適用于x86架構。此外,因為RDTSC的數據不能在CPU間同步,在剖析時xhprof會將程序綁定在單個 的CPU。
如果SpeedStep技術是打開的,XHProf的基于RDTSC定時器的功能就不能正 常工作了。這項技術在某些英特爾處理器上是可用的。[注:蘋果臺式機和筆記本電腦一般都將SpeedStep技術預設開啟。使用XHProf,您需要禁用 SpeedStep技術。 ]
下面的步驟,應該在Linux / Unix環境下進行。
% cd <xhprof_source_directory>/extension/ % phpize % ./configure --with-php-config=<path to php-config > % make % make install % make test
php.ini文件 :您可以更新您的php.ini文件來自動加載您的擴展。將以下內容添加到你的php.ini文件。
[xhprof] extension=xhprof.so ; ; directory used by default implementation of the iXHProfRuns ; interface (namely, the XHProfRuns_Default class) for storing ; XHProf runs. ; xhprof.output_dir=<directory_for_storing_xhprof_runs >
-
使用XHProf來做性能分析
用下面的示例程序來試著產生一些原始的性能數據:
foo.php
<?php function bar($x) { if ($x > 0) { bar($x - 1); } } function foo() { for ($idx = 0; $idx < 2; $idx++ ) { bar($idx); $x = strlen("abc"); } } // start profiling // run program foo(); // stop profiler // display raw xhprof data for the profiler run print_r($xhprof_data);
運行上面的測試程序:
% php -dextension=xhprof.so foo.php
你應該得到的輸出如下:
Array ( [foo==>bar] => Array ( [ct] => 2 # 2 calls to bar() from foo() [wt] => 27 # inclusive time in bar() when called from foo() ) [foo==>strlen] => Array ( [ct] => 2 [wt] => 2 ) [bar==>bar@1] => Array # a recursive call to bar() ( [ct] => 1 [wt] => 2 ) [main()==>foo] => Array ( [ct] => 1 [wt] => 74 ) [main()==>xhprof_disable] => Array ( [ct] => 1 [wt] => 0 ) [main()] => Array # fake symbol representing root ( [ct] => 1 [wt] => 83 ) )
注:The raw data only contains "inclusive" metrics. For example, the wall time metric in the raw data represents inclusive time in microsecs. Exclusive times for any function are computed during the analysis/reporting phase.
注意:默認情況下只有調用次數和占用時間是記錄了的。您也可以選擇同時記錄CPU 時間和/或單純記錄內存使用量。將下面這一行:
xhprof_enable();
換成:
xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY); ;
您應該得到如下的輸出:
Array ( [foo==>bar] => Array ( [ct] => 2 # number of calls to bar() from foo() [wt] => 37 # time in bar() when called from foo() [cpu] => 0 # cpu time in bar() when called from foo() [mu] => 2208 # change in PHP memory usage in bar() when called from foo() [pmu] => 0 # change in PHP peak memory usage in bar() when called from foo() ) [foo==>strlen] => Array ( [ct] => 2 [wt] => 3 [cpu] => 0 [mu] => 624 [pmu] => 0 ) [bar==>bar@1] => Array ( [ct] => 1 [wt] => 2 [cpu] => 0 [mu] => 856 [pmu] => 0 ) [main()==>foo] => Array ( [ct] => 1 [wt] => 104 [cpu] => 0 [mu] => 4168 [pmu] => 0 ) [main()==>xhprof_disable] => Array ( [ct] => 1 [wt] => 1 [cpu] => 0 [mu] => 344 [pmu] => 0 ) [main()] => Array ( [ct] => 1 [wt] => 139 [cpu] => 0 [mu] => 5936 [pmu] => 0 ) )
在分析時跳過內置函數
默認情況下, PHP的內置函數(如strlen )也被分析了。如果您不希望分析內置函數(為了進一步減少性能分析的開銷貌相或減小產生的原始數據) ,您可以使用XHPROF_FLAGS_NO_BUILTINS標 志,例如:
// do not profile builtin functionsxhprof_enable ( XHPROF_FLAGS_NO_BUILTINS ) ;
在性能分析時忽略特定函數( 0.9.2或更高版本支持)
從 0.9.2版本的xhprof ,你可以告訴XHProf在性能分析時忽略一些指定函數。這樣,您就可以忽略,像功能用于間接函數調用,如call_user_func和 call_user_func_array的 函數 。這些不必要的中間層,因為他們亂七八糟的父-子關系和間接的互相調用,使XHProf報告難以理解。
要設定要忽略的函數列表,可以在分析時給xhprof_enable函 數 指定第二個參數[是個可選參數] 。例如,
// elapsed time profiling; ignore call_user_func* during profiling xhprof_enable(0, array('ignored_functions' => array('call_user_func', 'call_user_func_array'))); or, // elapsed time + memory profiling; ignore call_user_func* during profiling xhprof_enable(XHPROF_FLAGS_MEMORY, array('ignored_functions' => array('call_user_func', 'call_user_func_array')));
-
設置XHProf用戶界面
-
index.php :查看一個單一運行或差異報告。
-
callgraph.php :以圖片的形式查看一次XHProf運行的調用關系圖。
-
typeahead.php :在XHProf的報告中被后臺調用來進行函數的自動補全。
-
PHP源碼目錄結構
XHProf的用戶界面的由PHP實現。代碼存放在兩個子目錄中: xhprof_html/和xhprof_lib/ 。
xhprof_html目錄包含了3個頂級PHP頁面。
該xhprof_lib目錄包含進行分析和顯示的支持代碼 (計算 扁平的性能信息,計算diffs,從多次運行中匯總數據等等) 。
-
Web服務器配置:您需要確保您的Web服務器有權限讀取xhprof_html/目 錄可,并且能運行PHP腳本。
-
管理XHProf運行
客戶端能很靈活地保存他們從XHProf運行中得到的 XHProf原始數據。用戶界面層 的XHProf提供了一個 iXHProfRuns接口(見xhprof_lib / utils / xhprof_runs.php ),客戶端可以利用。這使得客戶端能夠告訴用戶界面層,如何獲取XHProf運行后產生的對應數據。
XHProf的UI 庫自帶的有一個基于文件的iXHProfRUns接口實現,即“ XHProfRuns_Default”(見xhprof_lib/utils/xhprof_runs.php)。這個自帶的實現將XHProf運行結 果存在ini配置的xhprof.output_dir參 數指定的某個目錄下。
一次XHProf運行,必須用一個命名空間和運行編號來唯一確定。
一)XHProf數據的永久保存 :
假設您使用的iXHProfRuns接口的XHProfRuns_Default這 個默認實現方式,一個典型的XHProf運行并保存結果的操作可能就是這樣子的:
// start profiling xhprof_enable(); // run program .... // stop profiler $xhprof_data = xhprof_disable(); // // Saving the XHProf run // using the default implementation of iXHProfRuns. // include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_lib.php"; include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_runs.php"; $xhprof_runs = new XHProfRuns_Default(); // Save the run under a namespace "xhprof_foo". // // **NOTE**: // By default save_run() will automatically generate a unique // run id for you. [You can override that behavior by passing // a run id (optional arg) to the save_run() method instead.] // $run_id = $xhprof_runs->save_run($xhprof_data, "xhprof_foo"); echo "---------------\n". "Assuming you have set up the http based UI for \n". "XHProf at some address, you can view run at \n". "http://<xhprof-ui-address >/index.php?run=$run_id&source=xhprof_foo\n". "---------------\n";
上述代碼就會將運行結果保存在xhprof.output_dir ini參數指定的特定目錄下。文件的名稱可能會是49bafaa3a3f66.xhprof_foo類似的東 東 ;兩部分分別是運行編號( “ 49bafaa3a3f66 ” )和命名空間( “ xhprof_foo ” )。[如果您想創建/指定自己的run ID (如數據庫中的序列號,或時間戳) ,您可以通過明確的給save_run方法 指定一個run id。
b )使用自己實現的iXHProfRuns
如果您希望您的XHProf運行存儲在不同的數據庫(例如用一個壓縮格式,或者其他什么地方比如數據庫等),您需實現一個類,這個實必須實現 iXHProfRuns ( )接口。
您還需要修改“xhprof_html/"目錄中3個主要的PHP入口文件 (index.php , callgraph.php , typeahead.php ),使用新的類而不是默認的類XHProfRuns_Default 。改變3個文件的這一行:
$xhprof_runs_impl = new XHProfRuns_Default();
您還需要“include”聲明了上述class的文件。
-
從用戶界面中訪問各次運行
一)看單一運行報告
要查看run id是<run_id>和命名空間是<namespace>的報告,訪問URL:
http://<xhprof-ui-address>/index.php?run=<run_id>&source=<namespace>
例如,
http://<xhprof-ui-address>/index.php?run=49bafaa3a3f66&source=xhprof_foo
二)查看diff報告
要查看命名空間<namespace>下runid分別是< run_id1>和<run_id2>的兩個報告,訪問URL:
http://<xhprof-ui-address>/index.php?run1=<run_id1>&run2=<run_id2>&source=<namespace>
三)匯總報告
您也可以指定一組run id來匯總得到您想要的報告視圖。
如果你有三個XHProf運行,都在"benchmark‘命名空間下,run id分別是1,2,3。要查看這些運行的匯總報告:
http://<xhprof-ui-address>/index.php?run=1,2,3&source=benchmark
加權匯總 :進一步假設,上述3個運特分別對應三種程序,p1.php,p2.php和p3.php ,通常以20%,30%,50%概率混合:要查看匯總報告所對應的加權平均數這些運行使用:
http://<xhprof-ui-address>/index.php?run=1,2,3&wts=20,30,50&source=benchmark
-
在生產環境中使用XHProf的注意 事 項
這里給出一些意見和準則。您的情況可能會有所不同:
-
CPU的計時器( getrusage )在Linux上開銷很大。為了在函數級別更有用,這個是粗粒度的(毫秒精確度,而不是微秒水平)。因此,使用XHPROF_FLAGS_CPU模式時, 在報告里,數字上的誤差往往會更高。
我們建議在生產環境中使用 "占用時間+內存" 來做性能分析。[注:內存性能分析模式的額外開銷很低。 ]
// elapsed time profiling (default) + memory profiling xhprof_enable(XHPROF_FLAGS_MEMORY);
-
對隨機抽樣的部分page/request來做性能分析,對于獲取您的生產環境的負載 情況特征數據,其實足夠好了了。
分析萬分之一的請求,可以用下面的代碼:
if (mt_rand(1, 10000) == 1) { xhprof_enable(XHPROF_FLAGS_MEMORY); $xhprof_on = true; }
request結束時(或是在退出函數中) ,您可以這么做:
if ($xhprof_on) { // stop profiler $xhprof_data = xhprof_disable(); // save $xhprof_data somewhere (say a central DB) ... }
然后,您就可以用xhprof_aggregate_runs()來按時間(比如,每5 分鐘/每小時/每天),或是按頁面類型/request類型來匯總。
-
輕量級采樣模式
Xhprof擴展還提供了一個輕量級采樣模式 。采樣間隔為0.1秒。采樣記錄了整個函數調用堆棧。如果想以增加極低的負載作代價來進行性能監控和診斷,采樣模式就是你想要的。
XHProf擴展提供的與采樣模式相關的函數有xhprof_sample_enable()和xhprof_sample_disable() 。
[ 待定 :關于采樣模式更詳細的文檔。 ]
-
附加功能
xhprof_lib/utils/xhprof_lib.php文 件包含額外的庫函數,可用于維護/匯總XHProf運行結果。
例如:
-
xhprof_aggregate_runs() :可用于多次XHProf運行結果匯總到一個單一的運行。這可以幫助您使用XHProf來建立一個全系統“的函數級別”的性能監測工具 。 [例如,您可以在生產環境中定期抽樣XHProf的數據,產生小時/日級別的報告。 ]
-
xhprof_prune_run() :匯總大量XHProf運行結果(特別是如果它們對應不同類型的程序)將可能導致callgraph規模變得太大。您可以使用xhprof_prune_run功 能來修剪的callgraph數據中只占總運行時間中很小比例的子樹。
-
依賴性
-
JQuery的Javascript :在提示和函數名稱補齊上 ,我們利用了JQuery這個JavaScript庫。JQuery是基于MIT和GPL licencse ( http://docs.jquery.com/Licensing ) 的。XHProf用到的JQuery相關代碼,在xhprof_html/jquery子 目錄。
-
dot(圖像生成工具) :調用關系視圖工具( [查看Callgraph ] )這個功能依靠dot工具實現。 “點”是一種繪制/生成有向圖的工具。
-
鳴謝
查詢分析結果的HTML用戶界面受到了Oracle 的存儲過程語言PL/SQL里的一個相似的小工具的啟發。但是僅僅如此,他們的內部性能分析過程是完全不同的。