程序員日誌 · 2014-12-26 18:33php
該調研是2013年10月份作的,目標是尋找更好的PHP引擎,來代替百度各產品線正在使用的PHP 5.2。html
機器環境:前端
cpu: Intel(R) Xeon(R) CPU E5-2620 0 @ 2.00GHz, 12核。git
內存:64G程序員
引擎:github
php 5.2.17 (當時百度所用版本)web
php 5.5.4 (當時最新版本)json
hhvm 2.3-dev (當時最新版本)ubuntu
爲了公平起見,三個引擎都採用-O2的編譯優化選項。後端
加速器:
php5.2.17:採用eaccelerator
php5.5.4:eacc不可用,採用自帶的zend opcache
hhvm:內置opcode緩存,開啓jit
擴展:
因爲目前的百度擴展只有不多移植到了hhvm,因此爲了公平起見,其它百度擴展也在php5.2和php5.5中禁用,而採用純php的方式實現,實踐證實採用純php實現性能並未有太大損失。
下面用了不一樣的場景,來比較PHP引擎的性能。
用php官方提供的測試腳本:
bench.php : https://github.com/php/php-src/blob/master/Zend/bench.php
micro_bench.php : https://github.com/php/php-src/blob/master/Zend/micro_bench.php
第三方提供的測試腳本bench_third.php : http://www.php-benchmark-script.com/
測試結果以下:
引擎 | bench.php 耗時 | micro_bench.php 耗時 | bench_third.php 耗時 |
---|---|---|---|
php5.2 | 6.692s | 41.890s | 9.226s |
php5.5 | 3.609s | 14.972s | 5.893s |
hhvm | 0.579s | 5.832s | 2.869s |
分析:php歷次新版本對性能都有優化,所以php5.5在純cpu計算上比php5.2有明顯優點。而hhvm採用jit機制,將php轉化爲本地代碼執行,因此性能上比執行opcode的php5.5還要快上不少。
結論:對於純cpu型計算,hhvm>>php5.5>>php5.2。
簡單的http服務即由一個簡單的php邏輯接口層加一個後端C模塊(或通用存儲)構成的服務,這裏用了百度內部一個實際的簡單服務爲例。
簡單作了一下性能測試,client併發10,client與service同機部署,service與後端C模塊不一樣機部署。結果以下:
引擎 | 平均響應時間 | Cpu idle | Qps |
---|---|---|---|
php5.2 | 3.6ms | 50% | 1900 |
php5.5 | 2.9ms | 60% | 2500 |
hhvm | 2.0ms | 60% | 3900 |
分析:對於簡單http服務,自身也有一部分的邏輯計算,除此以外,hhvm是webserver與php引擎一體化,不須要 webserver->php這一層的開銷,所以在響應時間和qps上有更明顯的優點。因爲C模塊後端自己響應時間較短(1ms),因此不一樣引擎的 總體響應時間的區別就比較明顯。
結論:簡單http服務,在qps上hhvm>>php5.5>php5.2,在響應時間上,除掉後端服務耗時,也是hhvm>>php5.5>php5.2。
這裏用了貼吧的最新版ui,串行訪問後端。
採用20個併發,測試結果以下:
引擎 | 總體耗時 | 除訪問後端以外的耗時 | idle | qps |
---|---|---|---|---|
php5.2 | 520ms | 127ms | 50% | 40 |
php5.5 | 500ms | 107ms | 45% | 45 |
hhvm | 470ms | 72ms | 65% | 50 |
分析:ui大部分時間消耗在後端服務訪問,這方面引擎不起做用。而另外一方面,除後端服務外的耗時也很高,這部分對cpu消耗較大,致使qps較低。
顯然,這個效果並非很符合咱們的預期,爲了達到理想的qps,咱們須要想方法進一步下降除後端以外的耗時。
首先咱們在PHP5.2下運行xhprof,找出了一些較耗時的函數,並進行了優化:
去掉沒必要要的編碼轉換,減小15ms
去掉沒必要要的warning日誌,耗時減少4.3ms
改進xss轉碼函數實現,耗時減小1.8ms
經過上面幾個優化,HHVM下ui的耗時降到了52ms,其中模板耗時34ms。可是這樣仍不使人滿意,如何進一步優化呢?
既然上面的xhprof是在PHP5.2下執行的,那咱們嘗試直接在HHVM下運行xhprof會如何?結果發現,HHVM下xhprof的結果與PHP5.2下有很大不一樣,發現了一些新的優化點,進行了優化:
有好幾個函數會在每一個請求中重複計算一樣的信息,加上apc cache以後,耗時減少16ms
用戶自定義的errorHandler,很耗時去掉後耗時減少7ms
去掉一些debug日誌,耗時減少1ms
經過上面的優化,咱們將HHVM下ui的耗時降到了26ms。那麼,一樣的優化,做用在PHP5.2或PHP5.5下又如何呢?
引擎 | 優化前 | 業務優化後 |
---|---|---|
php5.2 | 127ms | 103ms |
php5.5 | 107ms | 87ms |
hhvm | 72ms | 26ms |
使人失望。。。究其緣由,是由於在php下耗時在各函數的分佈至關分散,即便優化個別函數,也難以有大輻提高。
而在hhvm下,因爲引擎自己性能較高,所以一些原來實現得不合理而比較耗時的函數就突顯出來了,所以針對這些函數進行優化,能夠取得更加明顯的效果。
結論:對於產品前端UI,引擎能夠在必定程度上優化響應時間和qps,hhvm>php5.5>php5.2,如再結合一些針對hhvm的優化,能夠大幅提高hhvm下的qps。
語言方面的兼容性來自兩個方面:
hhvm是基於php5.4的語法標準,所以,hhvm和php5.5與咱們目前用的php5.2都存在語法差別,php5.5差別更大一些。
目前所遇到的差別個數以下:
引擎 | 影響處理結果 | 不影響結果,打warning |
---|---|---|
php5.5 | 1個 | 1個 |
hhvm | 1個 | 1個 |
全部差別可見這裏:
http://www.php.net/manual/en/migration53.php
http://www.php.net/manual/en/migration54.php
http://www.php.net/manual/en/migration55.php
因爲是兩套不一樣實現,所以hhvm與zend引擎也有差別。
目前遇到的差別數以下:
影響處理結果 | 不影響結果,打warning |
---|---|
2個 | 0個 |
hhvm文檔也提到了一些差別:https://github.com/facebook/hhvm/blob/master/hphp/doc/inconsistencies
結論:整體上,hhvm與php5.5在語言方面與php5.2都存在一些差別,而且對處理結果形成影響。不過,大部分的差別都會致使執行時報fatal或warning,咱們只需根據相應提示修改代碼便可,改動成本不大。
將原百度用到的擴展在php5.5下編譯,大部分能編譯經過,少數編譯失敗:
升級到最新版本可解決,5個
修改代碼解決,2個
hhvm內置了不少經常使用的第三方擴展,所以對於第三方擴展,基本無須遷移。主要須要考慮的是百度擴展的遷移。
hhvm編寫擴展有兩種方式:
1、靜態編譯
編寫擴展的idl文件(json格式)
根據idl自動生成代碼框架
填充代碼框架,實現本身的邏輯
從新編譯hhvm
缺點:
現有百度擴展須要從新改造,與獲取輸入參數、返回結果相關及其它調用到php.h中的api的地方都須要重寫。這是主要的成本。
hhvm的api目前還不太穩定,2.1.0下編寫的擴展,在2.2.0下就編譯不經過了。。
優勢:
新擴展編寫,比在zend下編寫更加方便,hhvm提供相似於boost variant的api,處理輸入輸出很方便
2、動態加載(HNI:hhvm native interface)
編寫函數聲明.php(語法與php很像)
編寫函數實現.cpp
hphpize && cmake . && make,只須單獨編譯擴展
動態加載更爲方便,所以推薦使用這種方式編寫擴展。
3、ZendCompatLayer
hhvm正在實現一個ZendCompatLayer,即兼容zend擴展的一個層,實現zend引擎相關api,以方便現有擴展遷移到hhvm。具體見:https://github.com/facebook/hhvm/blob/master/hphp/doc/php.extension.compat.layer
按照其描述,zend擴展能夠不作太多修改,便可遷移到hhvm。
嘗試開啓ZendCompatLayer功能,並編譯包中自帶的yaml擴展,確認ZendCompatLayer已經可用
嘗試使用ZendCompatLayer編譯兩個百度擴展,發現不少問題,好比連接時缺乏各類函數、加載不了ini、調用不了minit/rinit等函 數、gcc版本不兼容、原擴展不支持多線程環境等等,通過各類修改hhvm和擴展代碼,終於跑通,但成本很高,並且穩定性很難保障。
測試了一下ZendCompatLayer的性能,用一個包含string, int和嵌套數組的數組,反覆調用一個百度擴展作打包解包,結果以下:
耗時 | 實現方式 |
---|---|
199 ms | HNI |
769 ms | HNI+ZendCompat |
348 ms | php5.2 |
結論:ZendCompatLayer目前還不完善,不少api未實現,另外性能還較差,對性能要求高的擴展仍是建議用hhvm原生方式實現,而長尾擴展能夠用ZendCompatLayer,減小遷移與維護成本。
結論:php5.5對擴展保持較好的兼容性,遷移成本不高。hhvm目前擴展遷移成本較高,不過若是有ZendCompatLayer,也能大大減少遷移成本,能夠等ZendCompatLayer完善以後,再作進一步調研。
php5.5對操做系統基本沒有什麼要求。
hhvm對系統要求較高,須要在ubuntu/centos下編譯、運行,百度大部分機器不符合條件。。
不過,通過研究後發現,能夠經過打包so方式,讓hhvm能在百度機器運行,上面的測試都是在百度機器上進行的,沒有發現什麼問題。
根據一樣的原理,經過打包依賴so和頭文件,也能讓hhvm在百度機器上編譯,已經成功編譯。
內核方面,hhvm要求至少2.6.32系統,百度大部分系統都符合要求。
結論:系統兼容性上,php5.5和hhvm都沒有什麼問題。
php5.5對php.ini保持兼容,但php-fpm.conf則變成ini格式,原來的php-fpm.conf須要修改,而且php-fpm也被編譯成一個單獨的可執行文件,而再也不和php-cgi共用。
hhvm的配置是一種新的hdf格式,並且其配置項都不相同,不必定每一個php.ini配置都能找到對應的配置項,不過常見的都有。新版hhvm也支持指定php.ini文件了,不過仍然不能支持全部配置項。
hhvm是webserver與php一體化,webserver的常見功能,如rewrite、簡單防攻擊、proxy、日誌、靜態文件服務也都 有。所以webserver的相關配置也要成hhvm的配置格式。不過如今HHVM也支持fastcgi模式了,因此這個已經不成問題。
結論:配置兼容性上php5.5更好一些。不過配置文件也並很少,人工改形成本也不是很高,所以這方面問題並不大。
php從5.3到5.5增長了不少新功能,具體能夠見:
http://www.php.net/manual/en/migration53.php
http://www.php.net/manual/en/migration54.php
http://www.php.net/manual/en/migration55.php
整體上可歸納爲語法更靈活,功能更豐富。
hhvm的語法基於5.4,庫的支持也不如php5.5豐富。但hhvm提供了幾個功能,可能對性能優化會有很大幫助:
StartupDocument:能夠在啓動時執行該文件,在此文件中作一些全局初始化工做,並將結果變量經過apc傳遞給worker線程。這樣咱們將一些重複的初始化工做放到該文件,避免每一個請求都執行一樣的工做。
ThreadDocuments:能夠用後臺線程執行一個文件,作一些好比信息收集、定時更新之類的操做,避免在每一個請求中執行相應過程,影響在線請求的響應時間。
Pagelet Server/Xbox Server:能夠啓動一個子Server,子Server維護一個線程池,主Server能夠將一些計算任務分發給子Server異步執行,從而實現並 行化。我嘗試了一下這個功能,利用xbox server很方便地就實現了並行調用後端服務的功能,確實很方便,並且這個機制是通用的,不只後端調用能夠並行,本地計算也能夠並行,能夠考慮將模板並 行渲染?呵呵。
多線程架構:這其實不算一個功能,而是hhvm多線程架構給咱們帶來的自然便利。由於在同一個進程內,內存共享會很方便,訪問速度更快,而像一些好比鏈接池的功能,也變得能夠實現。
上述功能的介紹詳見:
https://github.com/facebook/hhvm/blob/master/hphp/doc/server.documents
https://github.com/facebook/hhvm/blob/master/hphp/doc/threading
php5.5的開發者支持很好,文檔全,api很是穩定。
相比之下,hhvm的開發者支持目前還比較弱,文檔不多,不少地方要本身摸索,api還不太穩定,不過hhvm的社區很活躍,github有不少人在提issue和patch,hhvm官方團隊也很給力,迭代速度很快,每8周發佈一個穩定版本。
整體上,使用php5.5,性能會有必定提高,遷移成本較小。
使用hhvm,性能會有很大提高,但遷移成本會較大,主要是擴展方面的遷移成本。
UPDATE
看這裏的結論很明顯,百度大部分產品線使用了HHVM,由於咱們相信資源的節約對於 咱們這麼大規模的應用收益很大。
讀到這裏,你可能會有疑問思考你到底要不要升級到HHVM?你想知道PHP7對比怎麼樣?
這裏是你須要遷移HHVM的緣由:
你如今服務器成本比較大,想要節約成本,或者流量持續增加資源不足。
你須要比較透明的提高性能
你沒有什麼自有擴展,或者有時間遷移擴展(擴展遷移到HHVM的成本很低,後面會作相關的介紹)
你想使用Hack語言
或者大家想嘗試新的技術
一樣,這也有不建議遷移HHVM的緣由:
你的服務器及流量不多
你的業務很穩定,沒有性能上的要求
大家是土豪,機器隨便堆,預算一大把
這還有一些答疑:
Q: 有人提到運維成本的問題,HHVM真的不容易運維麼?
A: HHVM的多線程模型是相對PHP-FPM不穩定,可是有很是多的進程守護解決方案能夠很好的解決, 這個不是個問題,以百度如今的運維來看比較穩定。
Q:PHP7的性能不是已經不錯了麼?爲何還要遷移到HHVM?
A:首先PHP7還處於開發狀態,首先遷移大版本的風險仍是有的,至少如今考慮是不靠譜的,若是願意等, 能夠等到PHP7出來之後來進行對比。看看哪一個更適合你,目前這個階段HHVM是比PHP更優的選擇。
作大家的測試(Do Your Own Benchmarking)
若是大家決定遷移了,那麼開始前也建議作一次大家業務相關的評測,以確保收益在預期範圍內。