在開始分析之前,我們得掌握一些與性能分析相關的函數。這些函數讓我們對程序性能有更好的分析和評測。
一、性能分析相關的函數與命令
1.1、時間度量函數
平時我們常用 time() 函數,但是返回的是秒數,對于某段代碼的內部性能分析,到秒的精度是不夠的。于是要用 microtime 函數。而 microtime 函數可以返回兩種形式,一是字符串的形式,一是浮點數的形式。不過需要注意的是,在缺省的情況下,返回的精度只有4位小數。為了獲得更高的精確度,我們需要配置 precision。
如下是 microtime 的使用結果。
$start= microtime(true);
echo $start."\n";
$end = microtime(true);
echo $end."\n";
echo ($end-$start)."\n";
輸出為:
bash-3.2# phptime.php
1441360050.3286
1441360050.3292
0.00053000450134277
而在代碼前面加上一行:
ini_set("precision", 16);
輸出為:
bash-3.2# phptime.php
1441360210.932628
1441360210.932831
0.0002031326293945312
除了 microtime 內部統計之外, 還可以使用 getrusage 來取得用戶態的事長。在實際的操作中,也常用 time 命令來計算整個程序的運行時長,通過多次運行或者修改代碼后運行,得到不同的時間長度以得到效率上的區別。 具體用法是:time phptime.php ,則在程序運行完成之后,不管是否正常結束退出,都會有相關的統計。
bash-3.2# time phptime.php
1441360373.150756
1441360373.150959
0.0002031326293945312
real 0m0.186s
user 0m0.072s
sys 0m0.077s
因為本文所討論的性能問題,往往分析上百萬次調用之后的差距與趨勢,為了避免代碼中存在一些時間統計代碼,后面我們使用 time 命令居多。
1.2、內存使用相關函數
分析內存使用的函數有兩個:memory_ get_ usage、memory_ get_ peak_usage,前者可以獲得程序在調用的時間點,即當前所使用的內存,后者可以獲得到目前為止高峰時期所使用的內存。所使用的內存以字節為單位。
$base_memory= memory_get_usage();
echo "Hello,world!\n";
$end_memory= memory_get_usage();
$peak_memory= memory_get_peak_usage();
echo $base_memory,"\t",$end_memory,"\t",($end_memory-$base_memory),"\t", $peak_memory,"\n";
輸出如下:
bash-3.2# phphelloworld.php
Hello,world!
224400 224568 168 227424
可以看到,即使程序中間只輸出了一句話,再加上變量存儲,也消耗了168個字節的內存。
對于同一程序,不同 PHP 版本對內存的使用并不相同,甚至還差別很大。
~~~
$baseMemory= memory_get_usage();
class User
{
private $uid;
function __construct($uid)
{
$this->uid= $uid;
}
}
for($i=0;$i<100000;$i++)
{
$obj= new User($i);
if ( $i% 10000 === 0 )
{
echo sprintf( '%6d: ', $i), memory_get_usage(), " bytes\n";
}
}
echo " peak: ",memory_get_peak_usage(true), " bytes\n";
在 PHP 5.2 中,內存使用如下:
[root@localhostphpperf]# php52 memory.php
0: 93784 bytes
10000: 93784 bytes
…… 80000: 93784 bytes
90000: 93784 bytes
peak: 262144 bytes
PHP 5.3 中,內存使用如下
[root@localhostphpperf]# phpmemory.php
0: 634992 bytes
10000: 634992 bytes
…… 80000: 634992 bytes
90000: 634992 bytes
peak: 786432 bytes
可見 PHP 5.3 在內存使用上要粗放了一些。
PHP 5.4 - 5.6 差不多,有所優化:
~~~
~~~
[root@localhostphpperf]# php56 memory.php
0: 224944 bytes
10000: 224920 bytes
…… 80000: 224920 bytes
90000: 224920 bytes
peak: 262144 bytes
而 PHP 7 在少量使用時,高峰內存的使用,增大很多。
[root@localhostphpperf]# php7 memory.php
0: 353912 bytes
10000: 353912 bytes
…… 80000: 353912 bytes
90000: 353912 bytes
peak: 2097152 bytes
~~~
從上面也看到,以上所使用的 PHP 都有比較好的垃圾回收機制,10萬次初始化,并沒有隨著對象初始化的增多而增加內存的使用。PHP7 的高峰內存使用最多,達到了接近 2M。
下面再來看一個例子,在上面的代碼的基礎上,我們加上一行,即如下加粗的一行:
~~~
$obj->self = $obj;
代碼如下:
$baseMemory= memory_get_usage();
class User
{
private $uid;
function __construct($uid)
{
$this->uid= $uid;
}
}
for($i=0;$i<100000;$i++)
{
$obj= new User($i);
$obj->self = $obj;
if ( $i% 5000 === 0 )
{
echo sprintf( '%6d: ', $i), memory_get_usage(), " bytes\n";
}
}
echo " peak: ",memory_get_peak_usage(true), " bytes\n";
~~~
這時候再來看看內存的使用情況,中間表格主體部分為內存使用量,單位為字節。
圖表如下:
PHP 5.2 并沒有合適的垃圾回收機制,導致內存使用越來越多。而5.3 以后內存回收機制導致內存穩定在一個區間。而也可以看見 PHP7 內存使用最少。把 PHP 5.2 的圖形去掉了之后,對比更為明顯。
可見 PHP7 不僅是在算法效率上,有大幅度的提升,在大批量內存使用上也有大幅度的優化(盡管小程序的高峰內存比歷史版本所用內存更多)。
1.3、垃圾回收相關函數
在 PHP 中,內存回收是可以控制的,我們可以顯式地關閉或者打開垃圾回收,一種方法是通過修改配置,zend.enable_gc=Off 就可以關掉垃圾回收。缺省情況下是 On 的。另外一種手段是通過 gc _enable()和gc _disable()函數分別打開和關閉垃圾回收。
比如在上面的例子的基礎上,我們關閉垃圾回收,就可以得到如下數據表格和圖表。
代碼如下:
~~~
gc_disable();
$baseMemory= memory_get_usage();
class User
{
private $uid;
function __construct($uid)
{
$this->uid= $uid;
}
}
for($i=0;$i<100000;$i++)
{
$obj= new User($i);
$obj->self = $obj;
if ( $i% 5000 === 0 )
{
echo sprintf( '%6d: ', $i), memory_get_usage(), " bytes\n";
}
}
echo " peak: ",memory_get_peak_usage(true), " bytes\n";
~~~
分別在 PHP 5.3、PHP5.4 、PHP5.5、PHP5.6 、PHP7 下運行,得到如下內存使用統計表。
圖表如下,PHP7 還是內存使用效率最優的。
從上面的例子也可以看出來,盡管在第一個例子中,PHP7 的高峰內存使用數是最多的,但是當內存使用得多時,PHP7 的內存優化就體現出來了。
這里值得一提的是垃圾回收,盡管會使內存減少,但是會導致速度降低,因為垃圾回收也是需要消耗 CPU 等其他系統資源的。Composer 項目就曾經因為在計算依賴前關閉垃圾回收,帶來成倍性能提升,引發廣大網友關注。詳見:
https://github.com/composer/composer/commit/ac676f47f7bbc619678a29deae097b6b0710b799
在常見的代碼和性能分析中,出了以上三類函數之外,還常使用的有堆棧跟蹤函數、輸出函數,這里不再贅述。
- PHP技術文章
- PHP中session和cookie的區別
- php設計模式(一):簡介及創建型模式
- php設計模式結構型模式
- Php設計模式(三):行為型模式
- 十款最出色的 PHP 安全開發庫中文詳細介紹
- 12個提問頻率最高的PHP面試題
- PHP 語言需要避免的 10 大誤區
- PHP 死鎖問題分析
- 致PHP路上的“年輕人”
- PHP網站常見安全漏洞,及相應防范措施總結
- 各開源框架使用與設計總結(一)
- 數據庫的本質、概念及其應用實踐(二)
- PHP導出MySQL數據到Excel文件(fputcsv)
- PHP中14種排序算法評測
- 深入理解PHP原理之--echo的實現
- PHP性能分析相關的函數
- PHP 性能分析10則
- 10 位頂級 PHP 大師的開發原則
- 30條爆笑的程序員梗 PHP是最好的語言
- PHP底層的運行機制與原理
- PHP 性能分析與實驗——性能的宏觀分析
- PHP7 性能翻倍關鍵大揭露
- 鳥哥:寫在PHP7發布之際一些話
- PHP與MySQL通訊那點事
- Php session內部執行流程的再次剖析
- 關于 PHP 中的 Class 的幾點個人看法
- PHP Socket 編程過程詳解
- PHP過往及現在及變革
- PHP吉祥物大象的由來
- PHP生成靜態頁面的方法
- 吊炸天的 PHP 7 ,你值得擁有!
- PHP開發中文件操作疑難問答
- MongoDB PHP Driver的連接處理解析
- PHP 雜談《重構-改善既有代碼的設計》之二 對象
- 在php中判斷一個請求是ajax請求還是普通請求的方法
- 使用HAProxy、PHP、Redis和MySQL支撐10億請求每周架構細節
- HTML、HTML5、XHTML、CSS、SQL、JavaScript、PHP、Web Services 是什么?
- 重構-改善既有代碼的設計
- PHP場景中getshell防御思路分享
- 移動互聯時代,你看看除了PHP你還會些什么
- 安卓系統上搭建本地php服務器環境
- PHP中常見的緩存技術!
- PHP里10個鮮為人知但卻非常有用的函數
- 成為一名PHP專家其實并不難
- PHP 命令行?是的,您可以!
- PHP開發提高效率技巧
- PHP八大安全函數解析
- PHP實現四種基本排序算法
- PHP開發中的中文編碼問題
- php.get.post
- php發送get、post請求的6種方法簡明總結
- 中高級PHP開發者應該掌握哪些技術?
- 前端開發
- web前端知識體系大全
- 前端工程與性能優化(下)
- 前端工程與性能優化(上)
- 2016 年技術發展方向
- Web應用檢查清單
- 如何成為一名優秀的web前端工程師
- 前端組件化開發實踐
- 移動端H5頁面高清多屏適配方案
- 2015前端框架何去何從
- 從前端看“百度遷徙”的技術實現(一)
- 從前端看“百度遷徙”的技術實現(二)
- 前端路上的旅行
- 大公司里怎樣開發和部署前端代碼?
- 5個經典的前端面試問題
- 前端工程師新手必讀
- 手機淘寶前端的圖片相關工作流程梳理
- 一個自動化的前端項目實現(附源碼)
- 前端代碼異常日志收集與監控
- 15年雙11手淘前端技術總結 - H5性能最佳實踐
- 深入理解javascript原型和閉包系列
- 一切都是對象
- 函數和對象的關系
- prototype原型
- 隱式原型
- instanceof
- 繼承
- 原型的靈活性
- 簡述【執行上下文】上
- 簡述【執行上下文】下
- this
- 執行上下文棧
- 簡介【作用域】
- 【作用域】和【上下文環境】
- 從【自由變量】到【作用域鏈】
- 閉包
- 完結
- 補充:上下文環境和作用域的關系
- Linux私房菜