天天看點

PHP 性能分析與實驗:性能的微觀分析

在開始分析之前,我們得掌握一些與性能分析相關的函數。這些函數讓我們對程式性能有更好的分析和評測。

PHP 性能分析與實驗:性能的微觀分析

一、性能分析相關的函數與指令

1.1、時間度量函數

如下是 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); 

1441360210.932628  

1441360210.932831  

0.0002031326293945312 

除了 microtime 内部統計之外, 還可以使用 getrusage 來取得使用者态的時長。在實際的操作中,也常用 time

指令來計算整個程式的運作時長,通過多次運作或者修改代碼後運作,得到不同的時間長度以得到效率上的差別。 具體用法是:time

phptime.php ,則在程式運作完成之後,不管是否正常結束退出,都會有相關的統計。

bash-3.2# time phptime.php 

1441360373.150756  

1441360373.150959  

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  

而 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;

代碼如下:

$obj->self = $obj; 

if ( $i% 5000 === 0 ) 

這時候再來看看記憶體的使用情況,中間表格主體部分為記憶體使用量,機關為位元組。

PHP 性能分析與實驗:性能的微觀分析

圖表如下:

PHP 性能分析與實驗:性能的微觀分析

php 5.2 并沒有合适的垃圾回收機制,導緻記憶體使用越來越多。而5.3 以後記憶體回收機制導緻記憶體穩定在一個區間。而也可以看見 php7 記憶體使用最少。把 php 5.2 的圖形去掉了之後,對比更為明顯。

PHP 性能分析與實驗:性能的微觀分析

可見 php7 不僅是在算法效率上,有大幅度的提升,在大批量記憶體使用上也有大幅度的優化(盡管小程式的高峰記憶體比曆史版本所用記憶體更多)。

1.3、垃圾回收相關函數

在 php

中,記憶體回收是可以控制的,我們可以顯式地關閉或者打開垃圾回收,一種方法是通過修改配置,zend.enable_gc=off 就可以關掉垃圾回收。

預設情況下是 on 的。另外一種手段是通過 gc _enable()和gc _disable()函數分别打開和關閉垃圾回收。

比如在上面的例子的基礎上,我們關閉垃圾回收,就可以得到如下資料表格和圖表。

gc_disable(); 

分别在 php 5.3、php5.4 、php5.5、php5.6 、php7 下運作,得到如下記憶體使用統計表。

PHP 性能分析與實驗:性能的微觀分析

圖表如下,php7 還是記憶體使用效率最優的。

PHP 性能分析與實驗:性能的微觀分析

從上面的例子也可以看出來,盡管在第一個例子中,php7 的高峰記憶體使用數是最多的,但是當記憶體使用得多時,php7 的記憶體優化就展現出來了。

這裡值得一提的是垃圾回收,盡管會使記憶體減少,但是會導緻速度降低,因為垃圾回收也是需要消耗 cpu 等其他系統資源的。composer 項目就曾經因為在計算依賴前關閉垃圾回收,帶來成倍性能提升,引發廣大網友關注。詳見:

<a href="https://github.com/composer/composer/commit/ac676f47f7bbc619678a29deae097b6b0710b799">https://github.com/composer/composer/commit/ac676f47f7bbc619678a29deae097b6b0710b799</a>

在常見的代碼和性能分析中,出了以上三類函數之外,還常使用的有堆棧跟蹤函數、輸出函數,這裡不再贅述。

二、php 性能分析10則

下面我們根據小程式來驗證一些常見的性能差别。

2.1、使用 echo 還是 print

在有的建議規則中,會建議使用 echo ,而不使用 print。說 print 是函數,而 echo

是文法結構。實際上并不是如此,print 也是文法結構,類似的文法結構,還有多個,比如 list、isset、require 等。不過對于

php 7 以下 php 版本而言,兩者确實有性能上的差别。如下兩份代碼:

for($i=0; $i&lt;1000000; $i++) 

echo("hello,world!"); 

print ("hello,world!"); 

在 php 5.3 中運作速度分别如下(各2次):

[root@localhostphpperf]# time php echo1.php &gt; /dev/null 

real 0m0.233s 

user 0m0.153s 

sys 0m0.080s 

real 0m0.234s 

user 0m0.159s 

sys 0m0.073s 

[root@localhostphpperf]# time phpecho.php&gt; /dev/null 

real 0m0.203s 

user 0m0.130s 

sys 0m0.072s 

user 0m0.128s 

sys 0m0.075s 

在 php5.3 版中效率差距10%以上。而在 php5.4 以上的版本中,差別不大,如下是 php7 中的運作效率。

[root@localhostphpperf]# time php7 echo.php&gt; /dev/null 

real 0m0.151s 

user 0m0.088s 

sys 0m0.062s 

real 0m0.145s 

user 0m0.084s 

sys 0m0.061s 

[root@localhostphpperf]# time php7 echo1.php &gt; /dev/null 

real 0m0.140s 

user 0m0.075s 

sys 0m0.064s 

real 0m0.146s 

user 0m0.077s 

sys 0m0.069s 

正如浏覽器前端的一些優化準則一樣,沒有啥特别通用的原則,往往根據不同的情況和版本,規則也會存在不同。

2.2、require 還是 require_once?

在一些正常的優化規則中,會提到,建議使用 require_ once 而不是 require,現由是 require_ once 會去檢測是否重複,而 require 則不需要重複檢測。

在大量不同檔案的包含中,require_ once 略慢于 require。但是 require_ once

的檢測是一項記憶體中的行為,也就是說即使有數個需要加載的檔案,檢測也隻是記憶體中的比較。而 require

的每次重新加載,都會從檔案系統中去讀取分析。因而 require_ once 會比 require 更佳。咱們也使用一個例子來看一下。

str.php 

global$str; 

$str= "china has a large population"; 

require.php 

for($i=0; $i&lt;100000; $i++) { 

require "str.php"; 

require_once.php 

require_once"str.php"; 

上面的例子,在 php7 中,require_ once.php 的運作速度是 require.php 的30倍!在其他版本也能得到大緻相同的結果。

[root@localhostphpperf]# time php7 require.php 

real 0m1.712s 

user 0m1.126s 

sys 0m0.569s 

real 0m1.640s 

user 0m1.113s 

sys 0m0.515s 

[root@localhostphpperf]# time php7 require_once.php 

real 0m0.066s 

user 0m0.063s 

sys 0m0.003s 

real 0m0.057s 

user 0m0.052s 

sys 0m0.004s 

從上可以看到,如果存在大量的重複加載的話,require_ once 明顯優于 require,因為重複的檔案不再有 io

操作。即使不是大量重複的加載,也建議使用 require_

once,因為在一個程式中,一般不會存在數以千百計的檔案包含,100次記憶體比較的速度差距,一個檔案包含就相當了。

2.3、單引号還是雙引号?

單引号,還是雙引号,是一個問題。一般的建議是能使用單引号的地方,就不要使用雙引号,因為字元串中的單引号,不會引起解析,進而效率更高。那來看一下實際的差别。

classuser 

private $username; 

private $age; 

function  __construct($uid, $username,$age){ 

$this-&gt;username = $username; 

$this-&gt;age = $age; 

function getuserinfo() 

return "uid:".$this-&gt;uid." username:".$this-&gt;username." age:".$this-&gt;age; 

function getuserinfosingle() 

return 'uid:'.$this-&gt;uid.' username:'.$this-&gt;username.' age'.$this-&gt;age; 

function getuserinfoonce() 

return "uid:{$this-&gt;uid}username:{$this-&gt;username} age:{$this-&gt;age}"; 

function getuserinfosingle2() 

return 'uid:{$this-&gt;uid} username:{$this-&gt;username} age:{$this-&gt;age}'; 

for($i=0; $i&lt;1000000;$i++) { 

$user = new user($i, "name".$i, $i%100); 

$user-&gt;getuserinfosingle(); 

在上面的 user 類中,有四個不同的方法,完成一樣的功能,就是拼接資訊傳回,看看這四個不同的方法的差別。

第一個、<code>getuserinfo</code> ,使用雙引号和屬性相拼接

[root@localhostphpperf]# time php7 string.php 

real 0m0.670s 

user 0m0.665s 

sys 0m0.002s 

real 0m0.692s 

user 0m0.689s 

real 0m0.683s 

user 0m0.672s 

第二個、<code>getuserinfosingle</code> ,使用單引号和屬性相拼接

real 0m0.686s 

user 0m0.683s 

sys 0m0.001s 

real 0m0.671s 

user 0m0.666s 

real 0m0.669s 

可見在拼接中,單雙引号并無明顯差别。

第三個、<code>getuserinfoonce</code>,不再使用句号<code>.</code>連接配接,而是直接引入在字元串中解析。

real 0m0.564s 

user 0m0.556s 

sys 0m0.006s 

real 0m0.592s 

user 0m0.587s 

real 0m0.563s 

user 0m0.559s 

從上面可見,速度提高了0.06s-0.10s,有10%-20%的效率提升。可見連綴效率更低一些。

第四個、<code>getuserinfosingle2</code> 雖然沒有達到我們真正想要的效果,功能是不正确的,但是在字元串中,不再需要解析變量和擷取變量值,是以效率确實有大幅度提升。

real 0m0.379s 

user 0m0.375s 

real 0m0.399s 

user 0m0.394s 

real 0m0.377s 

user 0m0.371s 

效率确實有了大的提升,快了50%。

那麼這個快,是由于不需要變量引用解析帶來的,還是隻要加入<code>$</code>天然的呢?我們再試着寫了一個方法。

functiongetuserinfosingle3() 

return "uid:{\$this-&gt;uid} username:{\$this-&gt;username} age:{\$this-&gt;age}"; 

得到如下運作時間:

real 0m0.385s 

user 0m0.381s 

real 0m0.382s 

user 0m0.380s 

real 0m0.386s 

發現轉義後的字元串,效率跟單引号是一緻的,從這裡也可以看見,單引号還是雙引号包含,如果不存在需要解析的變量,幾乎沒有差别。如果有需要解析的變量,你也不能光用單引号,要麼使用單引号和連綴,要麼使用内部插值,是以在這條規則上,不用太過糾結。

2.4、錯誤應該打開還是關閉?

在 php 中,有多種錯誤消息,錯誤消息的開啟是否會帶來性能上的影響呢?從直覺覺得,由于錯誤消息,本身會涉及到 io 輸出,無論是輸出到終端或者 error_log,都是如此,是以肯定會影響性能。我們來看看這個影響有多大。

error_reporting(e_error);

for($i=0; $i&lt;1000000;$i++) {

$str= "通常,$php中的垃圾回收機制,僅僅在循環回收算法确實運作時會有時間消耗上的增加。但是在平常的(更小的)腳本中應根本就沒有性能影響。

然而,在平常腳本中有循環回收機制運作的情況下,記憶體的節省将允許更多這種腳本同時運作在你的伺服器上。因為總共使用的記憶體沒達到上限。";

}

在上面的代碼中,我們涉及到一個不存在的變量,是以會報出 notice 錯誤:

notice: undefined variable: php 中的垃圾回收機制,僅僅在循環回收算法确實運作時會有時間消耗上的增加。但是在平常的 in xxxx/string2.php on line 10

如果把 e_ error 改成 e_ all 就能看到大量的上述錯誤輸出。

我們先執行 e_ error 版,這個時候沒有任何錯誤日志輸出。得到如下資料:

[root@localhostphpperf]# time php7 string2.php 

real 0m0.442s 

user 0m0.434s 

sys 0m0.005s 

real 0m0.487s 

user 0m0.484s 

real 0m0.476s 

user 0m0.471s 

再執行 e_ all 版,有大量的錯誤日志輸出,我們把輸出重定向到<code>/dev/null</code>

[root@localhostphpperf]# time php7 string2.php &gt; /dev/null 

real 0m0.928s 

user 0m0.873s 

sys 0m0.051s 

real 0m0.984s 

user 0m0.917s 

real 0m0.945s 

user 0m0.887s 

sys 0m0.056s 

可見慢了将近一倍。

如上可見,即使輸出沒有正式寫入檔案,錯誤級别打開的影響也是巨大的。線上上我們應該将錯誤級别調到 e_ error 這個級别,同時将錯誤寫入 error_ log,既減少了不必要的錯誤資訊輸出,又避免洩漏路徑等資訊,造成安全隐患。

2.5、正規表達式和普通字元串操作

在字元串操作中,有一條常見的規則,即是能使用普通字元串操作方法替代的,就不要使用正規表達式來處理,用 c 語言操作 pcre 做過正規表達式處理的童鞋應該清楚,需要先 compile,再 exec,也就是說是一個相對複雜的過程。現在就比較一下兩者的差别。

對于簡單的分隔,我們可以使用 explode 來實作,也可以使用正規表達式,比如下面的例子:

function microtime_ex() 

list($usec, $sec) = explode(" ", microtime()); 

return $sec+$usec; 

for($i=0; $i&lt;1000000; $i++) { 

microtime_ex(); 

耗時在0.93-1s之間。

[root@localhostphpperf]# time php7 pregstring.php 

real 0m0.941s 

user 0m0.931s 

sys 0m0.007s 

real 0m0.986s 

user 0m0.980s 

real 0m1.004s 

user 0m0.998s 

我們再将分隔語句替換成:

list($usec, $sec) = preg_split("#\s#", microtime()); 

得到如下資料,慢了近10-20%。

[root@localhostphpperf]# time php7 pregstring1.php 

real 0m1.195s 

user 0m1.182s 

real 0m1.222s 

user 0m1.217s 

real 0m1.101s 

user 0m1.091s 

再将語句替換成:

list($usec, $sec) = preg_split("#\s+#", microtime());

即比對一到多個空格,并沒有太多的影響。除了分隔外,查找我們也來看一個例子。

第一段代碼:

if(preg_match("#l#i", $str)) 

第二段代碼:

if(stripos($str, "l")!==false) 

這兩段代碼達到的效果相同,都是查找字元串中有無 l 或者 l 字元。

在 php 7 下運作效果如下:

[root@localhostphpperf]# time php7 pregstring2.php 

real 0m0.172s 

user 0m0.167s 

real 0m0.199s 

user 0m0.196s 

[root@localhostphpperf]# time php7 pregstring3.php 

real 0m0.185s 

user 0m0.182s 

real 0m0.184s 

user 0m0.181s 

兩者差別不大。再看看在 php5.6 中的表現。

[root@localhostphpperf]# time php56 pregstring2.php 

real 0m0.470s 

user 0m0.456s 

real 0m0.506s 

user 0m0.500s 

[root@localhostphpperf]# time php56 pregstring3.php 

real 0m0.348s 

user 0m0.342s 

real 0m0.376s 

user 0m0.364s 

可見在 php 5.6 中表現還是非常明顯的,使用正規表達式慢了20%。php7 難道是對已使用過的正規表達式做了緩存?我們調整一下代碼如下:

$pattern = "#".chr(ord('a')+$i%26)."#i"; 

if($ret = preg_match($pattern, $str)!==false) 

這是一個動态編譯的 pattern。

$pattern = "".chr(ord('a')+$i%26).""; 

if($ret = stripos($str, $pattern)!==false) 

在 php7 中,得到了如下結果:

real 0m0.351s 

user 0m0.346s 

real 0m0.359s 

user 0m0.352s 

real 0m0.375s 

user 0m0.369s 

real 0m0.370s 

user 0m0.365s 

可見兩者并不明顯。而在 php 5.6 中,同樣的代碼:

real 0m1.022s 

user 0m1.015s 

real 0m1.049s 

user 0m1.041s 

real 0m0.923s 

user 0m0.821s 

real 0m0.838s 

user 0m0.831s 

在 php 5.6 中,stripos

版明顯要快于正規表達式版,由上兩例可見,php7對正規表達式的優化還是相當驚人的。其次也建議,能用普通字元串操作的地方,可以避免使用正規表達式。

因為在其他版本中,這個規則還是适用的。某 zend 大牛官方的分享給出如下資料:

<code>stripos(‘http://’, $website)</code> 速度是<code>preg_match(‘/http:\/\//i’, $website)</code> 的兩倍

<code>ctype_alnum()</code>速度是<code>preg_match(‘/^\s*$/’)</code>的5倍;

<code>“if ($test == (int)$test)”</code> 比 <code>preg_match(‘/^\d*$/’)</code>快5倍

可以相見,正規表達式是相對低效的。

2.6、數組元素定位查找

在數組元素的查找中,有一個關鍵的注意點就是數組值和鍵的查找速度,差異非常大。了解過 php 擴充開發的朋友,應該清楚,數組在底層其實是 hash 表。是以鍵是以快速定位的,而值卻未必。下面來看例子。

首先們構造一個數組:

$a= array();

for($i=0;$i&lt;100000;$i++){

$a[$i] = $i;

在這個數組中,我們測試查找值和查找鍵的效率差别。

第一種方法用 array_ search,第二種用 array_ key_ exists,第三種用 isset 文法結構。 代碼分别如下:

//查找值

foreach($a as $i)

{

array_search($i, $a);

//查找鍵

array_key_exists($i, $a);

//判定鍵是否存在

if(isset($a[$i]));

運作結果如下:

[root@localhostphpperf]# time php7 array.php

real 0m9.026s

user 0m8.965s

sys 0m0.007s

real 0m9.063s

sys 0m0.005s

[root@localhostphpperf]# time php7 array1.php

real 0m0.018s

user 0m0.016s

sys 0m0.001s

real 0m0.021s

user 0m0.015s

sys 0m0.004s

[root@localhostphpperf]# time php7 array2.php

real 0m0.020s

user 0m0.014s

sys 0m0.006s

real 0m0.016s

user 0m0.009s

由上例子可見,鍵值查找的速度比值查找的速度有百倍以上的效率差别。因而如果能用鍵值定位的地方,盡量用鍵值定位,而不是值查找。

2.7、對象與數組

在 php 中,數組就是字典,字典可以存儲屬性和屬性值,而且無論是鍵還是值,都不要求資料類型統一,是以對象資料存儲,既能用對象資料結構的屬性存儲資料,也能使用數組的元素存儲資料。那麼兩者有何差别呢?

使用對象:

classuser

public $uid;

public $username;

public $age;

function getuserinfo()

    {

return "uid:".$this-&gt;uid." username:".$this-&gt;username." age:".$this-&gt;age;

    }

$user = new user();

$user-&gt;uid= $i;

$user-&gt;age = $i%100;

$user-&gt;username="user".$i;

$user-&gt;getuserinfo();

使用數組:

functiongetuserinfo($user)

return "uid:".$user['uid']." username:".$user['username']." age:".$user['age'];

$user = array("uid"=&gt;$i,"age" =&gt;$i%100,"username"=&gt;"user".$i);

getuserinfo($user);

我們分别在 php5.3、php 5.6 和 php 7 中運作這兩段代碼。

[root@localhostphpperf]# time phpobject.php

real 0m2.144s

user 0m2.119s

sys 0m0.009s

real 0m2.106s

user 0m2.089s

sys 0m0.013s

[root@localhostphpperf]# time php object1.php

real 0m1.421s

user 0m1.402s

sys 0m0.016s

real 0m1.431s

user 0m1.410s

sys 0m0.012s

在 php 5.3 中,數組版比對象版快了近30%。

[root@localhostphpperf]# time php56 object.php

real 0m1.323s

user 0m1.319s

sys 0m0.002s

real 0m1.414s

user 0m1.400s

[root@localhostphpperf]# time php56 object1.php

real 0m1.356s

user 0m1.352s

real 0m1.364s

user 0m1.349s

[root@localhostphpperf]# time php7 object.php

real 0m0.642s

user 0m0.638s

sys 0m0.003s

real 0m0.606s

user 0m0.602s

[root@localhostphpperf]# time php7 object1.php

real 0m0.615s

user 0m0.613s

sys 0m0.000s

user 0m0.611s

到了 php 5.6 和 php7 中,兩個版本基本沒有差别,而在 php7 中的速度是 php5.6 中的2倍。在新的版本中,差别已幾乎沒有,那麼為了清楚起見我們當然應該聲明類,執行個體化類來存儲對象資料。

2.8、getter 和 setter

從 java 轉過來學習 php 的朋友,在對象聲明時,可能習慣使用 getter 和 setter,那麼,在 php 中,使用 getter 和 setter 是否會帶來性能上的損失呢?同樣,先上例子。

無 setter版:

有 setter版:

private $username;

function setusername($name)

$this-&gt;username = $name;

$user-&gt;setusername("user".$i);

這裡隻增加了一個 setter。運作結果如下:

real 0m0.607s

real 0m0.598s

user 0m0.596s

[root@localhostphpperf]# time php7 object2.php

real 0m0.673s

user 0m0.669s

real 0m0.668s

user 0m0.664s

從上面可以看到,增加了一個 setter,帶來了近10%的效率損失。可見這個性能損失是相當大的,在 php 中,我們沒有必要再來做 setter 和 getter了。需要引用的屬性,直接使用即可。

2.9、類屬性該聲明還是不聲明

php 本身支援屬性可以在使用時增加,也就是不聲明屬性,可以在運作時添加屬性。那麼問題來了,事先聲明屬性與事後增加屬性,是否會有性能上的差别。這裡也舉一個例子探讨一下。

事先聲明了屬性的代碼就是2.8節中,無 setter 的代碼,不再重複。而無屬性聲明的代碼如下:

兩段代碼,運作結果如下:

real 0m0.608s

user 0m0.604s

user 0m0.605s

[root@localhostphpperf]# time php7 object3.php

real 0m0.733s

user 0m0.728s

real 0m0.727s

user 0m0.720s

從上面的運作可以看到,無屬性聲明的代碼慢了20%。可以推斷出來的就是對于對象的屬性,如果事先知道的話,我們還是事先聲明的好,這一方面是效率問題,另一方面,也有助于提高代碼的可讀性呢。

2.10、圖檔操作 api 的效率差别

先上代碼:

function imagick_resize($filename, $outname)

$thumbnail = new imagick($filename);

$thumbnail-&gt;resizeimage(200, 200, imagick::filter_lanczos, 1);

$thumbnail-&gt;writeimage($outname);

unset($thumbnail);

function imagick_scale($filename, $outname)

$thumbnail-&gt;scaleimage(200, 200);

function convert($func)

$cmd= "find /var/data/ppt |grep jpg";

$start = microtime(true);

exec($cmd, $files);

$index = 0;

foreach($files as $key =&gt;$filename)

$outname= " /tmp/$func"."_"."$key.jpg";

$func($filename, $outname);

$index++;

$end = microtime(true);

echo "$func $index files: " . ($end- $start) . "s\n";

convert("imagick_resize");

convert("imagick_scale");

在上面的代碼中,我們分别使用了 resizeimage 和 scaleimage 來進行圖檔的壓縮,壓縮的是常見的 1-3m 之間的數位相機圖檔,得到如下運作結果:

[root@localhostphpperf]# php55 imagick.php

imagick_ resize 169 files: 5.0612308979034s

imagick_ scale 169 files: 3.1105840206146s

imagick_ resize 169 files: 4.4953861236572s

imagick_ scale 169 files: 3.1514940261841s

imagick_ resize 169 files: 4.5400381088257s

imagick_ scale 169 files: 3.2625908851624s

169張圖檔壓縮,使用 resizeimage 壓縮,速度在4.5s以上,而使用 scaleimage 則在 3.2s

左右,快了将近50%,壓縮的效果,用肉眼看不出明顯差別。當然 resizeimage 的控制能力更強,不過對于批量處理而言,使用

scaleimage 是更好的選擇,尤其對頭像壓縮這種頻繁大量的操作。本節隻是例舉了圖檔壓縮 api 作為例子,也正像 explode 和

preg_ split 一樣,在 php 中,完成同樣一件事情,往往有多種手法。建議采用效率高的做法。

以上就是關于 php 開發的10個方面的對比,這些點涉及到 php 文法、寫法以及 api 的使用。有些政策随着 php 的發展,有的已經不再适用,有些政策則會一直有用。

有童鞋也許會說,在現實的開發應用中,上面的某些觀點和解決政策,有點「然并卵」。為什麼這麼說呢?因為在一個程式的性能瓶頸中,最為核心的瓶頸,

往往并不在 php 語言本身。即使是跟 php

代碼中暴露出來的性能瓶頸,也常在外部資源和程式的不良寫法導緻的瓶頸上。于是為了做好性能分析,我們需要向 php

的上下遊戲延伸,比如延伸到後端的服務上去,比如延伸到前端的優化規則。在這兩塊,都有了相當多的積累和分析,雅虎也據此提出了多達35條前端優化規則,

這些同 php 本身的性能分析構成了一個整體,就是降低使用者的通路延時。

那麼,對于一個程式而言,其他的性能瓶頸可能存在哪裡?我們将深入探讨。是以在本系列的下兩篇,我們将探讨 php 程式的外圍效源的效率問題和前端效率問題,敬請期待。

來源:51cto