下面我們根據小程序來驗證一些常見的性能差別。
2.1、使用 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次):
[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.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.3、單引號還是雙引號?
單引號,還是雙引號,是一個問題。一般的建議是能使用單引號的地方,就不要使用雙引號,因為字符串中的單引號,不會引起解析,從而效率更高。那來看一下實際的差別。
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.4、錯誤應該打開還是關閉?
在 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.5、正則表達式和普通字符串操作
在字符串操作中,有一條常見的規則,即是能使用普通字符串操作方法替代的,就不要使用正則表達式來處理,用 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.6、數組元素定位查找
在數組元素的查找中,有一個關鍵的注意點就是數組值和鍵的查找速度,差異非常大。了解過 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.7、對象與數組
在 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.3、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.8、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.9、類屬性該聲明還是不聲明
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.10、圖片操作 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 程序員打下基礎,對于實際生產中程序的效率提升,往往幫助也不是特別顯著,因為大家也看到,在文章的實例中,很多操作往往是百萬次才能看出明顯的性能差別。在現實的頁面中,每一個請求很快執行完成,對這些基礎代碼的調用,往往不會有這么多次調用。不過了解這些,總是好的。
那么,對于一個程序而言,其他的性能瓶頸可能存在哪里?我們將深入探討。所以在本系列的下兩篇,我們將探討 PHP 程序的外圍效源的效率問題和前端效率問題,敬請期待。
- 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私房菜