PHP 性能分析10則

下面咱們根據小程序來驗證一些常見的性能差異。php

 

2.一、使用 echo 仍是 print前端

 

在有的建議規則中,會建議使用 echo ,而不使用 print。說 print 是函數,而 echo 是語法結構。實際上並非如此,print 也是語法結構,相似的語法結構,還有多個,好比 list、isset、require 等。不過對於 PHP 7 如下 PHP 版本而言,二者確實有性能上的差異。以下兩份代碼:程序員

 

for($i=0; $i<1000000; $i++)
{
echo("Hello,World!");
}

for($i=0; $i<1000000; $i++)
{
print ("Hello,World!");
}

在 PHP 5.3 中運行速度分別以下(各2次):web

 

[root@localhostphpperf]# time php echo1.php > /dev/null正則表達式

 

real 0m0.233s 算法

user 0m0.153s 小程序

sys 0m0.080s 後端

[root@localhostphpperf]# time php echo1.php > /dev/null數組

 

real 0m0.234s 瀏覽器

user 0m0.159s

sys 0m0.073s

 

[root@localhostphpperf]# time phpecho.php> /dev/null

 

real 0m0.203s

user 0m0.130s

sys 0m0.072s

[root@localhostphpperf]# time phpecho.php> /dev/null

 

real 0m0.203s

user 0m0.128s

sys 0m0.075s

 

在 PHP5.3 版中效率差距10%以上。而在 PHP5.4 以上的版本中,區別不大,以下是 PHP7 中的運行效率。

 

[root@localhostphpperf]# time php7 echo.php> /dev/null

 

real 0m0.151s

user 0m0.088s

sys 0m0.062s

[root@localhostphpperf]# time php7 echo.php> /dev/null

 

real 0m0.145s

user 0m0.084s

sys 0m0.061s

 

[root@localhostphpperf]# time php7 echo1.php > /dev/null

 

real 0m0.140s

user 0m0.075s

sys 0m0.064s

[root@localhostphpperf]# time php7 echo1.php > /dev/null

 

real 0m0.146s

user 0m0.077s

sys 0m0.069s

 

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

 

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<100000; $i++) {
require "str.php";
}

require_once.php
for($i=0; $i<100000; $i++) {
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

[root@localhostphpperf]# time php7 require.php

 

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

[root@localhostphpperf]# time php7 require_once.php

 

real 0m0.057s

user 0m0.052s

sys 0m0.004s

 

從上能夠看到,若是存在大量的重複加載的話,require_ once 明顯優於 require,由於重複的文件再也不有 IO 操做。即便不是大量重複的加載,也建議使用 require_ once,由於在一個程序中,通常不會存在數以千百計的文件包含,100次內存比較的速度差距,一個文件包含就至關了。

 

2.三、單引號仍是雙引號?

 

單引號,仍是雙引號,是一個問題。通常的建議是能使用單引號的地方,就不要使用雙引號,由於字符串中的單引號,不會引發解析,從而效率更高。那來看一下實際的差異。

 

classUser
{
private $uid;
private $username;
private $age;

function  __construct($uid, $username,$age){
$this->uid= $uid;
$this->username = $username;
$this->age = $age;
    }
function getUserInfo()
    {
return "UID:".$this->uid." UserName:".$this->username." Age:".$this->age;
    }
function getUserInfoSingle()
    {
return 'UID:'.$this->uid.' UserName:'.$this->username.' Age'.$this->age;
    }

function getUserInfoOnce()
    {
return "UID:{$this->uid}UserName:{$this->username} Age:{$this->age}";
    }

function getUserInfoSingle2()
    {
return 'UID:{$this->uid} UserName:{$this->username} Age:{$this->age}';
    }
}

for($i=0; $i<1000000;$i++) {
$user = new User($i, "name".$i, $i%100);
$user->getUserInfoSingle();
}

在上面的 User 類中,有四個不一樣的方法,完成同樣的功能,就是拼接信息返回,看看這四個不一樣的方法的區別。

 

第一個、getUserInfo ,使用雙引號和屬性相拼接

 

[root@localhostphpperf]# time php7 string.php

 

real 0m0.670s

user 0m0.665s

sys 0m0.002s

[root@localhostphpperf]# time php7 string.php

 

real 0m0.692s

user 0m0.689s

sys 0m0.002s

[root@localhostphpperf]# time php7 string.php

 

real 0m0.683s

user 0m0.672s

sys 0m0.004s

 

第二個、getUserInfoSingle ,使用單引號和屬性相拼接

 

[root@localhostphpperf]# time php7 string.php

 

real 0m0.686s

user 0m0.683s

sys 0m0.001s

[root@localhostphpperf]# time php7 string.php

 

real 0m0.671s

user 0m0.666s

sys 0m0.003s

[root@localhostphpperf]# time php7 string.php

 

real 0m0.669s

user 0m0.666s

sys 0m0.002s

 

可見在拼接中,單雙引號並沒有明顯差異。

 

第三個、getUserInfoOnce,再也不使用句號.鏈接,而是直接引入在字符串中解析。

 

[root@localhostphpperf]# time php7 string.php

 

real 0m0.564s

user 0m0.556s

sys 0m0.006s

[root@localhostphpperf]# time php7 string.php

 

real 0m0.592s

user 0m0.587s

sys 0m0.004s

[root@localhostphpperf]# time php7 string.php

 

real 0m0.563s

user 0m0.559s

sys 0m0.003s

 

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

 

第四個、getUserInfoSingle2 雖然沒有達到咱們真正想要的效果,功能是不正確的,可是在字符串中,再也不須要解析變量和獲取變量值,因此效率確實有大幅度提高。

 

[root@localhostphpperf]# time php7 string.php

 

real 0m0.379s

user 0m0.375s

sys 0m0.003s

[root@localhostphpperf]# time php7 string.php

 

real 0m0.399s

user 0m0.394s

sys 0m0.003s

[root@localhostphpperf]# time php7 string.php

 

real 0m0.377s

user 0m0.371s

sys 0m0.004s

 

效率確實有了大的提高,快了50%。

 

那麼這個快,是因爲不須要變量引用解析帶來的,仍是隻要加入$自然的呢?咱們再試着寫了一個方法。

 

functiongetUserInfoSingle3()
{
return "UID:{\$this->uid} UserName:{\$this->username} Age:{\$this->age}";
}

獲得以下運行時間:

 

[root@localhostphpperf]# time php7 string.php

 

real 0m0.385s

user 0m0.381s

sys 0m0.002s

[root@localhostphpperf]# time php7 string.php

 

real 0m0.382s

user 0m0.380s

sys 0m0.002s

[root@localhostphpperf]# time php7 string.php

 

real 0m0.386s

user 0m0.380s

sys 0m0.004s

 

發現轉義後的字符串,效率跟單引號是一致的,從這裏也能夠看見,單引號仍是雙引號包含,若是不存在須要解析的變量,幾乎沒有差異。若是有須要解析的變量,你也不能光用單引號,要麼使用單引號和連綴,要麼使用內部插值,因此在這條規則上,不用太過糾結。

 

2.四、錯誤應該打開仍是關閉?

 

在 PHP 中,有多種錯誤消息,錯誤消息的開啓是否會帶來性能上的影響呢?從直覺以爲,因爲錯誤消息,自己會涉及到 IO 輸出,不管是輸出到終端或者 error_log,都是如此,因此確定會影響性能。咱們來看看這個影響有多大。

 

error_reporting(E_ERROR);
for($i=0; $i<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

[root@localhostphpperf]# time php7 string2.php

 

real 0m0.487s

user 0m0.484s

sys 0m0.002s

[root@localhostphpperf]# time php7 string2.php

 

real 0m0.476s

user 0m0.471s

sys 0m0.003s

 

再執行 E_ ALL 版,有大量的錯誤日誌輸出,咱們把輸出重定向到/dev/null

 

[root@localhostphpperf]# time php7 string2.php > /dev/null

 

real 0m0.928s

user 0m0.873s

sys 0m0.051s

[root@localhostphpperf]# time php7 string2.php > /dev/null

 

real 0m0.984s

user 0m0.917s

sys 0m0.064s

[root@localhostphpperf]# time php7 string2.php > /dev/null

 

real 0m0.945s

user 0m0.887s

sys 0m0.056s

 

可見慢了將近一倍。

 

如上可見,即便輸出沒有正式寫入文件,錯誤級別打開的影響也是巨大的。在線上咱們應該將錯誤級別調到 E_ ERROR 這個級別,同時將錯誤寫入 error_ log,既減小了沒必要要的錯誤信息輸出,又避免泄漏路徑等信息,形成安全隱患。

 

2.五、正則表達式和普通字符串操做

 

在字符串操做中,有一條常見的規則,便是能使用普通字符串操做方法替代的,就不要使用正則表達式來處理,用 C 語言操做 PCRE 作過正則表達式處理的童鞋應該清楚,須要先 compile,再 exec,也就是說是一個相對複雜的過程。如今就比較一下二者的差異。

 

對於簡單的分隔,咱們可使用 explode 來實現,也可使用正則表達式,好比下面的例子:

 

ini_set("precision", 16);
function microtime_ex()
{
list($usec, $sec) = explode(" ", microtime());
return $sec+$usec;
}

for($i=0; $i<1000000; $i++) {
microtime_ex();
}

耗時在0.93-1S之間。

 

[root@localhostphpperf]# time php7 pregstring.php

 

real 0m0.941s

user 0m0.931s

sys 0m0.007s

[root@localhostphpperf]# time php7 pregstring.php

 

real 0m0.986s

user 0m0.980s

sys 0m0.004s

[root@localhostphpperf]# time php7 pregstring.php

 

real 0m1.004s

user 0m0.998s

sys 0m0.003s

 

咱們再將分隔語句替換成:

 

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

獲得以下數據,慢了近10-20%。

 

[root@localhostphpperf]# time php7 pregstring1.php

 

real 0m1.195s

user 0m1.182s

sys 0m0.004s

[root@localhostphpperf]# time php7 pregstring1.php

 

real 0m1.222s

user 0m1.217s

sys 0m0.003s

[root@localhostphpperf]# time php7 pregstring1.php

 

real 0m1.101s

user 0m1.091s

sys 0m0.005s

 

再將語句替換成:

 

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

即匹配一到多個空格,並無太多的影響。除了分隔外,查找咱們也來看一個例子。

 

第一段代碼:

 

$str= "China has a Large population";
for($i=0; $i<1000000; $i++) {
if(preg_match("#l#i", $str))
    {
    }
}

第二段代碼:

 

$str= "China has a large population";
for($i=0; $i<1000000; $i++) {
if(stripos($str, "l")!==false)
    {
    }
}

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

 

在 PHP 7 下運行效果以下:

 

[root@localhostphpperf]# time php7 pregstring2.php

 

real 0m0.172s

user 0m0.167s

sys 0m0.003s

[root@localhostphpperf]# time php7 pregstring2.php

 

real 0m0.199s

user 0m0.196s

sys 0m0.002s

[root@localhostphpperf]# time php7 pregstring3.php

 

real 0m0.185s

user 0m0.182s

sys 0m0.003s

[root@localhostphpperf]# time php7 pregstring3.php

 

real 0m0.184s

user 0m0.181s

sys 0m0.003s

 

二者區別不大。再看看在 PHP5.6 中的表現。

 

[root@localhostphpperf]# time php56 pregstring2.php

 

real 0m0.470s

user 0m0.456s

sys 0m0.004s

[root@localhostphpperf]# time php56 pregstring2.php

 

real 0m0.506s

user 0m0.500s

sys 0m0.005s

[root@localhostphpperf]# time php56 pregstring3.php

 

real 0m0.348s

user 0m0.342s

sys 0m0.004s

[root@localhostphpperf]# time php56 pregstring3.php

 

real 0m0.376s

user 0m0.364s

sys 0m0.003s

 

可見在 PHP 5.6 中表現仍是很是明顯的,使用正則表達式慢了20%。PHP7 難道是對已使用過的正則表達式作了緩存?咱們調整一下代碼以下:

 

$str= "China has a Large population";

for($i=0; $i<1000000; $i++) {
$pattern = "#".chr(ord('a')+$i%26)."#i";
if($ret = preg_match($pattern, $str)!==false)
    {
    }
}

這是一個動態編譯的 pattern。

 

$str= "China has a large population";

for($i=0; $i<1000000; $i++) {
$pattern = "".chr(ord('a')+$i%26)."";
if($ret = stripos($str, $pattern)!==false)
    {
    }
}

在 PHP7 中,獲得了以下結果:

 

[root@localhostphpperf]# time php7 pregstring2.php

 

real 0m0.351s

user 0m0.346s

sys 0m0.004s

[root@localhostphpperf]# time php7 pregstring2.php

 

real 0m0.359s

user 0m0.352s

sys 0m0.004s

[root@localhostphpperf]# time php7 pregstring3.php

 

real 0m0.375s

user 0m0.369s

sys 0m0.003s

[root@localhostphpperf]# time php7 pregstring3.php

 

real 0m0.370s

user 0m0.365s

sys 0m0.005s

 

可見二者並不明顯。而在 PHP 5.6 中,一樣的代碼:

 

[root@localhostphpperf]# time php56 pregstring2.php

 

real 0m1.022s

user 0m1.015s

sys 0m0.005s

[root@localhostphpperf]# time php56 pregstring2.php

 

real 0m1.049s

user 0m1.041s

sys 0m0.005s

[root@localhostphpperf]# time php56 pregstring3.php

 

real 0m0.923s

user 0m0.821s

sys 0m0.002s

[root@localhostphpperf]# time php56 pregstring3.php

 

real 0m0.838s

user 0m0.831s

sys 0m0.004s

 

在 PHP 5.6 中,stripos 版明顯要快於正則表達式版,由上兩例可見,PHP7對正則表達式的優化仍是至關驚人的。其次也建議,能用普通字符串操做的地方,能夠避免使用正則表達式。由於在其餘版本中,這個規則仍是適用的。某 zend 大牛官方的分享給出以下數據:

 

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

 

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

 

「if ($test == (int)$test)」preg_match(‘/^\d*$/’)快5倍

 

能夠相見,正則表達式是相對低效的。

 

2.六、數組元素定位查找

 

在數組元素的查找中,有一個關鍵的注意點就是數組值和鍵的查找速度,差別很是大。瞭解過 PHP 擴展開發的朋友,應該清楚,數組在底層實際上是 Hash 表。因此鍵是以快速定位的,而值卻未必。下面來看例子。

 

首先們構造一個數組:

 

$a= array();
for($i=0;$i<100000;$i++){
$a[$i] = $i;
}

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

 

第一種方法用 array_ search,第二種用 array_ key_ exists,第三種用 isset 語法結構。 代碼分別以下:

 

//查找值
foreach($a as $i)
{
array_search($i, $a);
}
//查找鍵
foreach($a as $i)
{
array_key_exists($i, $a);
}
//斷定鍵是否存在
foreach($a as $i)
{
if(isset($a[$i]));
}

運行結果以下:

 

[root@localhostphpperf]# time php7 array.php

 

real 0m9.026s

user 0m8.965s

sys 0m0.007s

[root@localhostphpperf]# time php7 array.php

 

real 0m9.063s

user 0m8.965s

sys 0m0.005s

[root@localhostphpperf]# time php7 array1.php

 

real 0m0.018s

user 0m0.016s

sys 0m0.001s

[root@localhostphpperf]# time php7 array1.php

 

real 0m0.021s

user 0m0.015s

sys 0m0.004s

[root@localhostphpperf]# time php7 array2.php

 

real 0m0.020s

user 0m0.014s

sys 0m0.006s

[root@localhostphpperf]# time php7 array2.php

 

real 0m0.016s

user 0m0.009s

sys 0m0.006s

 

由上例子可見,鍵值查找的速度比值查找的速度有百倍以上的效率差異。於是若是能用鍵值定位的地方,儘可能用鍵值定位,而不是值查找。

 

2.七、對象與數組

 

在 PHP 中,數組就是字典,字典能夠存儲屬性和屬性值,並且不管是鍵仍是值,都不要求數據類型統一,因此對象數據存儲,既能用對象數據結構的屬性存儲數據,也能使用數組的元素存儲數據。那麼二者有何差異呢?

 

使用對象:

 

classUser
{
public $uid;
public $username;
public $age;
function getUserInfo()
    {
return "UID:".$this->uid." UserName:".$this->username." Age:".$this->age;
    }
}

for($i=0; $i<1000000;$i++) {
$user = new User();
$user->uid= $i;
$user->age = $i%100;
$user->username="User".$i;
$user->getUserInfo();
}

使用數組:

 

functiongetUserInfo($user)
{
return "UID:".$user['uid']." UserName:".$user['username']." Age:".$user['age'];
}

for($i=0; $i<1000000;$i++) {
$user = array("uid"=>$i,"age" =>$i%100,"username"=>"User".$i);
getUserInfo($user);
}

咱們分別在 PHP5.三、PHP 5.6 和 PHP 7 中運行這兩段代碼。

 

[root@localhostphpperf]# time phpobject.php

 

real 0m2.144s

user 0m2.119s

sys 0m0.009s

[root@localhostphpperf]# time phpobject.php

 

real 0m2.106s

user 0m2.089s

sys 0m0.013s

[root@localhostphpperf]# time php object1.php

 

real 0m1.421s

user 0m1.402s

sys 0m0.016s

[root@localhostphpperf]# time php object1.php

 

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

[root@localhostphpperf]# time php56 object.php

 

real 0m1.414s

user 0m1.400s

sys 0m0.006s

[root@localhostphpperf]# time php56 object1.php

 

real 0m1.356s

user 0m1.352s

sys 0m0.002s

[root@localhostphpperf]# time php56 object1.php

 

real 0m1.364s

user 0m1.349s

sys 0m0.006s

[root@localhostphpperf]# time php7 object.php

 

real 0m0.642s

user 0m0.638s

sys 0m0.003s

[root@localhostphpperf]# time php7 object.php

 

real 0m0.606s

user 0m0.602s

sys 0m0.003s

[root@localhostphpperf]# time php7 object1.php

 

real 0m0.615s

user 0m0.613s

sys 0m0.000s

[root@localhostphpperf]# time php7 object1.php

 

real 0m0.615s

user 0m0.611s

sys 0m0.003s

 

到了 PHP 5.6 和 PHP7 中,兩個版本基本沒有差異,而在 PHP7 中的速度是 PHP5.6 中的2倍。在新的版本中,差異已幾乎沒有,那麼爲了清楚起見咱們固然應該聲明類,實例化類來存儲對象數據。

 

2.八、getter 和 setter

 

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

 

無 setter版:

classUser
{
public $uid;
public $username;
public $age;
function getUserInfo()
    {
return "UID:".$this->uid." UserName:".$this->username." Age:".$this->age;
    }
}

for($i=0; $i<1000000;$i++) {
$user = new User();
$user->uid= $i;
$user->age = $i%100;
$user->username="User".$i;
$user->getUserInfo();
}

有 setter版:

 

classUser
{
public $uid;
private $username;
public $age;
function setUserName($name)
    {
$this->username = $name;
    }
function getUserInfo()
    {
return "UID:".$this->uid." UserName:".$this->username." Age:".$this->age;
    }
}

for($i=0; $i<1000000;$i++) {
$user = new User();
$user->uid= $i;
$user->age = $i%100;
$user->setUserName("User".$i);
$user->getUserInfo();
}

這裏只增長了一個 setter。運行結果以下:

 

[root@localhostphpperf]# time php7 object.php

 

real 0m0.607s

user 0m0.602s

sys 0m0.004s

[root@localhostphpperf]# time php7 object.php

 

real 0m0.598s

user 0m0.596s

sys 0m0.000s

[root@localhostphpperf]# time php7 object2.php

 

real 0m0.673s

user 0m0.669s

sys 0m0.003s

[root@localhostphpperf]# time php7 object2.php

 

real 0m0.668s

user 0m0.664s

sys 0m0.004s

 

從上面能夠看到,增長了一個 setter,帶來了近10%的效率損失。可見這個性能損失是至關大的,在 PHP 中,咱們沒有必要再來作 setter 和 getter了。須要引用的屬性,直接使用便可。

 

2.九、類屬性該聲明仍是不聲明

 

PHP 自己支持屬性能夠在使用時增長,也就是不聲明屬性,能夠在運行時添加屬性。那麼問題來了,事先聲明屬性與過後增長屬性,是否會有性能上的差異。這裏也舉一個例子探討一下。

 

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

classUser
{ 
function getUserInfo()
    {
return "UID:".$this->uid." UserName:".$this->username." Age:".$this->age;
    }
}

for($i=0; $i<1000000;$i++) {
$user = new User();
$user->uid= $i;
$user->age = $i%100;
$user->username="User".$i;
$user->getUserInfo();
}

兩段代碼,運行結果以下:

 

[root@localhostphpperf]# time php7 object.php

 

real 0m0.608s

user 0m0.604s

sys 0m0.003s

[root@localhostphpperf]# time php7 object.php

 

real 0m0.615s

user 0m0.605s

sys 0m0.003s

[root@localhostphpperf]# time php7 object3.php

 

real 0m0.733s

user 0m0.728s

sys 0m0.004s

[root@localhostphpperf]# time php7 object3.php

 

real 0m0.727s

user 0m0.720s

sys 0m0.004s

 

從上面的運行能夠看到,無屬性聲明的代碼慢了20%。能夠推斷出來的就是對於對象的屬性,若是事先知道的話,咱們仍是事先聲明的好,這一方面是效率問題,另外一方面,也有助於提升代碼的可讀性呢。

 

2.十、圖片操做 API 的效率差異

 

在圖片處理操做中,一個很是常見的操做是將圖片縮放成小圖。縮放成小圖的辦法有多種,有使用 API 的,有使用命令行的。在 PHP 中,有 imagick 和 gmagick 兩個擴展可供操做,而命令行則通常使用 convert 命令來處理。咱們這裏來討論使用 imagick 擴展中的 API 處理圖片的效率差異。

 

先上代碼:

 

function imagick_resize($filename, $outname)
{
$thumbnail = new Imagick($filename);
$thumbnail->resizeImage(200, 200, imagick::FILTER_LANCZOS, 1);
$thumbnail->writeImage($outname);
unset($thumbnail);
}

function imagick_scale($filename, $outname)
{
$thumbnail = new Imagick($filename);
$thumbnail->scaleImage(200, 200);
$thumbnail->writeImage($outname);
unset($thumbnail);
}


function convert($func)
{
$cmd= "find /var/data/ppt |grep jpg";
$start = microtime(true);
exec($cmd, $files);
$index = 0;
foreach($files as $key =>$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

 

[root@localhostphpperf]# php55 imagick.php

 

imagick_ resize 169 files: 4.4953861236572s

imagick_ scale 169 files: 3.1514940261841s

 

[root@localhostphpperf]# php55 imagick.php

 

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 開發自己,寫出更好的 PHP 程序,爲你成爲一個資深的 PHP 程序員打下基礎,對於實際生產中程序的效率提高,每每幫助也不是特別顯著,由於你們也看到,在文章的實例中,不少操做每每是百萬次才能看出明顯的性能差異。在現實的頁面中,每個請求很快執行完成,對這些基礎代碼的調用,每每不會有這麼屢次調用。不過了解這些,老是好的。

相關文章
相關標籤/搜索