此前,閱讀過了不少關於 PHP 性能分析的文章,不過寫的都是一條一條的規則,並且,這些規則並無上下文,也沒有明確的實驗來體現出這些規則的優點,同時討論的也側重於一些語法要點。本文就改變 PHP 性能分析的角度,並經過實例來分析出 PHP 的性能方面須要注意和改進的點。php
在開始分析以前,咱們得掌握一些與性能分析相關的函數。這些函數讓咱們對程序性能有更好的分析和評測。git
1、性能分析相關的函數與命令github
1.一、時間度量函數算法
平時咱們經常使用 time() 函數,可是返回的是秒數,對於某段代碼的內部性能分析,到秒的精度是不夠的。因而要用 microtime 函數。而 microtime 函數能夠返回兩種形式,一是字符串的形式,一是浮點數的形式。不過須要注意的是,在缺省的狀況下,返回的精度只有4位小數。爲了得到更高的精確度,咱們須要配置 precision。小程序
以下是 microtime 的使用結果。bash
$start= microtime(true); echo $start."\n"; $end = microtime(true); echo $end."\n"; echo ($end-$start)."\n";
輸出爲:php7
bash-3.2# phptime.phpcomposer
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.二、內存使用相關函數
分析內存使用的函數有兩個: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.三、垃圾回收相關函數
在 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.三、PHP5.4 、PHP5.五、PHP5.6 、PHP7 下運行,獲得以下內存使用統計表。
圖表以下,PHP7 仍是內存使用效率最優的。
從上面的例子也能夠看出來,儘管在第一個例子中,PHP7 的高峯內存使用數是最多的,可是當內存使用得多時,PHP7 的內存優化就體現出來了。
這裏值得一提的是垃圾回收,儘管會使內存減小,可是會致使速度下降,由於垃圾回收也是須要消耗 CPU 等其餘系統資源的。Composer 項目就曾經由於在計算依賴前關閉垃圾回收,帶來成倍性能提高,引起廣大網友關注。詳見:
https://github.com/composer/composer/commit/ac676f47f7bbc619678a29deae097b6b0710b799
在常見的代碼和性能分析中,出了以上三類函數以外,還常使用的有堆棧跟蹤函數、輸出函數,這裏再也不贅述。