緣起
關於PHP很多人的直觀感覺是PHP是一種靈活的腳本語言庫類豐富使用簡單安全非常適合WEB開發但性能低下PHP的性能是否真 的就如同大家的感覺一樣的差呢?本文就是圍繞這麼一個話題來進行探討的從源碼應用場景基准性能對比分析等幾個方面深入分析PHP之性能問題並通 過真實的數據來說話
從原理分析PHP性能
從原理分析PHP的性能主要從以下幾個方面內存管理變量函數運行機制來進行分析
內存管理
類似Nginx的內存管理方式PHP在內部也是基於內存池並且引入內存池的生命周期概念在內存池方面PHP對PHP腳本和擴展的所有內 存相關操作都進行了托管對大內存和小內存的管理采用了不同的實現方式和優化具體可以參考以下文檔在內存分配和回收的生命周期內PHP采用一次初始化申請+動態擴容+內存標識回收機制並且在每次請求結束後直 接對內存池進行重新mask
變量
總所周知PHP是一種弱變量類型的語言所以在PHP內部所有的PHP變量都對應成一種類型Zval其中具體定義如下
圖一PHP變量
在變量方面PHP做了大量的優化工作比如說Reference counting和copy on writer機制這樣能夠保證內存使用上的優化並且減少內存拷貝次數(請參考)在數組方面PHP內部采用高效的hashtable來實現
函數
在PHP內部所有的PHP函數都回轉化成內部的一個函數指針比如說擴展中函數
ZEND_FUNCTION ( my_function );//類似function my_function(){}
在內部展開後就會是一個函數
void zif_my_function ( INTERNAL_FUNCTION_PARAMETERS );
void zif_my_function(
int ht
zval * return_value
zval * this_ptr
int return_value_used
zend_executor_globals * executor_globals
);
從這個角度來看PHP函數在內部也是對應一個函數指針
運行機制
在話說PHP性能的時候很多人都會說“C/C++是編譯型JAVA是半編譯型PHP是解釋型”也就是說PHP是先動態解析再代碼運行的所以從這個角度來看PHP性能必然很差
的確從PHP腳本運行來輸出的確是一個動態解析再代碼運行的過程具體來說PHP腳本的運行機制如下圖所示
圖二 PHP運行機制
PHP的運行階段也分成三個階段
Parse語法分析階段
Compile編譯產出opcode中間碼
Execute運行動態運行進行輸出
所以說在PHP內部本身也是存在編譯的過程並且據此產生了大量的opcode cache工具比如說apceaccxcache等等這些opcode cache在生產環境基本上在標配基於opcode cache能到做到“PHP腳本編譯一次多次運行”的效果從這點上PHP就和JAVA的半編譯機制非常類似
所以從運行機制上來看PHP的運行模式和JAVA是非常類似的都是先產生中間碼然後運行在不同虛擬機上
動態運行
從上面的幾個分析來看PHP在內存管理變量函數運行機制等幾個方面都做了大量的工作所以從原理來看PHP不應該存在性能問題性能至少也應該和Java比較接近
這個時候就不得不談PHP動態語言的特性所帶來的性能問題了由於PHP是動態運行時所以所有的變量函數對象調用作用域實現等等都是在 執行階段中才確定的這個從根本上決定了PHP性能中很難改變的一些東西在C/C++等能夠在靜態編譯階段確定的變量函數在PHP中需要在動態運行 中確定也就決定了PHP中間碼不能直接運行而需要運行在Zend Engine上
說到PHP變量的具體實現又不得不說一個東西了HashtableHashtable可以說在PHP靈魂之一在PHP內部廣泛用到包含變量符號棧函數符號棧等等都是基於hashtable的
以PHP變量為例來說明下PHP的動態運行特點比如說代碼
<?php
$var = “hello blogxiuwzcom”;
?>
該代碼的執行結果就是在變量符號棧(是一個hashtable)中新增一個項
當要使用到該變量時候就去變量符合棧中去查找(也就是變量調用對出了一個hash查找的過程)
同樣對於函數調用也基本上類似有一個函數符號棧(hashtable)
其實關於動態運行的變量查找特點在PHP的運行機制中也能看出一些PHP代碼通過解釋編譯後的流程下圖
圖 PHP運行實例
從上圖可以看出PHP代碼在compile之後產出的了類符號表函數符號表和OPCODE在真正執行的時候zend Engine會根據op code去對應的符號表中進行查找處理
從某種程度上在這種問題的上很難找到解決方案因為這是由於PHP語言的動態特性所決定的但是在國內外也有不少的人在尋找解決方案因為 通過這樣能夠從根本上完全的優化PHP典型的列子有facebook的hiphop
結論
從上面分析來看在基礎的內存管理變量函數運行機制方面PHP本身並不會存在明顯的性能差異但由於PHP的動態運行特性決定了 PHP和其他的編譯型語言相比所有的變量查找函數運行等等都會多一些hash查找的CPU開銷和額外的內存開銷至於這種開銷具體有多大可以通過後 續的基准性能和對比分析得出
因此也可以大體看出PHP不太適合的一些場景大量計算性任務大數據量的運算內存要求很嚴格的應用場景如果要實現這些功能也建議通過擴展的方式實現然後再提供鉤子函數給PHP調用這樣可以減低內部計算的變量函數等系列開銷
基准性能
對於PHP基准性能目前缺少標准的數據大多數同學都存在感性的認識有人認為QPS就是PHP的極限了此外對於框架的性能和框架對性能的影響很沒有響應的權威數字
本章節的目的是給出一個基准的參考性能指標通過數據給大家一個直觀的了解
具體的基准性能有以下幾個方面
裸PHP性能完成基本的功能
裸框架的性能只做最簡單的路由分發只走通核心功能
標准模塊的基准性能所謂標准模塊的基准性能是指一個具有完整服務模塊功能的基准性能
環境說明
測試環境
Uname
aPnux db
forum
test
db
baidu
com
_
#
SMP Wed Aug
:
:
CST
x
_
x
_
x
_
GNU/Pnux
Red Hat Enterprise Pnux AS release (Nahant Update )
Intel(R) Xeon(R) CPU E @ GHz
軟件相關
Nginx
nginx version: nginx/
built by gcc
(Red Hat
)
Php(采用phpfpm)
PHP (cP) (built: Mar ::)
Copyright (c) The PHP Group
Zend Engine v Copyright (c) Zend Technologies
with eAccelerator v Copyright (c) eAccelerator by eAccelerator
bingo
PHP框架
其他說明
目標機器的部署方式nginx>phpfpm>php腳本
測試壓力機器和目標機器獨立部署
裸PHP性能
最簡單的PHP腳本
<?php
require_once ‘/actions/indexActionphp’;
$objAction = new indexAction();
$objAction>init();
$objAction>execute();
?>
Acitons/indexActionphp裡面的代碼如下
<?php
class indexAction
{
pubPc function execute()
{
echo ‘hello world!’;
}
}
?>
通過壓力工具測試結果如下
裸PHP框架性能
為了和的對比基於bingo框架實現了類似的功能代碼如下
<?php
require_once ‘Bingo/Controller/Frontphp’;
$objFrontController = Bingo_Controller_Front::getInstance(array(
‘actionDir’ => ‘/actions’
));
$objFrontController>dispatch();
壓力測試結果如下
從該測試結果可以看出框架雖然有一定的消耗但對整體的性能來說影響是非常小的
標准PHP模塊的基准性能
所謂標准PHP模塊是指一個PHP模塊所必須要具體的基本功能
路由分發
自動加載
LOG初始化&Notice日志打印所以的UI請求都一條標准的日志
錯誤處理
時間校正
自動計算每個階段耗時開銷
編碼識別&編碼轉化
標准配置文件的解析和調用
采用bingo的代碼自動生成工具產生標准的測試PHP模塊test
測試結果如下
結論
從測試數據的結論來看PHP本身的性能還是可以的基准性能完全能夠達到幾千甚至上W的QPS至於為什麼在大多數的PHP模塊中表現不佳 其實這個時候更應該去找出系統的瓶頸點而是簡單的說OKPHP不行那我們換C來搞吧(下一個章節會通過一些例子來對比采用C來處理不見得有特 別的優勢)
通過基准數據可以得出以下幾個具體的結論
PHP本身性能也很不錯簡單功能下能夠達到QPS極限也能過W
PHP框架本身對性能影響非常有限尤其是在有一定業務邏輯和數據交互的情況下幾乎可以忽略
一個標准的PHP模塊基准性能能夠達到QPS( cpu idle)
對比分析
很多時候大家發現PHP模塊性能不行的時候就來一句“ok我們采用C重寫吧”在公司內采用C/C++來寫業務邏輯模塊的現象到處都有在前幾年甚至幾乎全部都是采用C來寫那時候大家寫的真是一個痛苦調試難敏捷不要談
From:http://tw.wingwit.com/Article/program/PHP/201311/21287.html