原文:http://rango.swoole.com/archives/440
最近PHP官方終於發佈了傳說中的PHP7,雖然只是alpha版。PHP7號稱是新一代的PHP,官方開發組對Zend引擎底層作了大量修改來優化PHP的性能。能夠說PHP7這個版本的主題就是性能優化。php
在過去PHP一直以開發效率快著稱,而語言自己的性能較差(固然比Python,Ruby仍是要快一些的)。普通的Web網站都是IO密集型的程序,瓶頸在MySQL上,因此體現不出PHP的性能劣勢。但在密集計算方面比C/C++、Java等靜態編譯語言差幾十倍甚至上百倍。另外使用設計很是複雜的開發框架,如Symfony、Laravel等,程序性能也會明顯降低。html
如今隨着PHP愈來愈流行,像Facebook、新浪微博這樣超大型規模的網站都在使用PHP。PHP語言性能問題就愈來愈嚴重了。Facebook有幾十萬臺服務器,若是現有的PHP程序能夠提高一部分性能,將會節約大量的服務器資源。因此就有了HHVM、Hack。Hack爲PHP增長了類型,HHVM是一個從新設計的PHP引擎,實際項目中使用HHVM能夠提近70%的性能。實際項目70%性能提高這是一個什麼概念?騰訊QQ農場最初使用PHP開發,後由於性能問題使用C語言重構,完成後性能提高了100%。程序員
PHP官方也注意到了這個問題,因此就有了PHP7的開發計劃。最新公佈的PHP7-alpha在WordPress項目中測試的表現已經超越了HHVM。將來PHP將會同時具有極高的開發效率和極高的性能,再結合Swoole作異步編程,PHP勢必會更加流行。web
本文簡單介紹一下PHP7作了哪些優化,能夠提高如此多性能。算法
一 zval使用棧內存
在Zend引擎和擴展中,常常要建立一個PHP的變量,底層就是一個zval指針。以前的版本都是經過MAKE_STD_ZVAL動態的從堆上分配一個zval內存。而PHP7能夠直接使用棧內存。PHP代碼中建立的變量也進行了優化,PHP7直接在棧內存上預分配zval。這樣節約了大量內存分配和內存管理的操做。編程
PHP5數組
zval *val; MAKE_STD_ZVAL(val);
PHP7性能優化
zval val;
二 zend_string存儲hash值,array查詢再也不須要重複計算hash
PHP7爲字符串單首創建了新類型叫作zend_string,除了char *指針和長度以外,增長了一個hash字段,用於保存字符串的hash值。PHP中array是核心數據結構,PHP程序中每每都有大量的$array[$key]操做,雖然hashtable查找的時間複雜度是O(1),但$key要轉爲hash值是要通過計算的。不只僅是array操做,實際上PHP底層對於類屬性、類方法、函數,訪問時都要先經過hashtable查找到對應的指針,再執行對應的操做。PHP7以前Zend引擎會有大量的CPU時間用於計算hash值。服務器
實際上PHP程序運行起來以後,大部分狀況下$key的值都是不變的。PHP7乾脆將這個hash值保存起來,下次直接使用,這樣就節省了大量的hash計算操做,PHP的hashtable與C數組的性能一致。websocket
從實際項目進行callgrind性能分析,會發現alloc和hash 2項操做就佔用了至關大比例的CPU時間。PHP7優化以後這2項操做佔用的CPU時間下降了很是多。(注:zend_hash仍然佔12%,由於總體CPU下降了,因此總的耗時下降了很多)
三 hashtable桶內直接存數據
PHP5的hashtable每一個元素都是一個 Bucket *,而PHP7直接存Bucket,減小了內存申請次數,提高了Cache命中率和內存訪問速度。
四 zend_parse_parameters改成宏實現
PHP的C擴展函數與PHP中的變量進行參數輸入時,要使用zend_parse_parameters()函數,這個函數根據一個字符串參數找到對應PHP的zval指針,而後進行賦值。 這個函數實際上有必定的性能消耗。PHP7直接使用宏替換了zend_parse_parameters函數,C擴展中再也不須要使用zend_parse_parameters進行逐個參數的查找,宏展開後自動會實現參數賦值。僅此一項就提高了5%的性能。
五 新增長4種OPCODE
不少PHP程序中會大量使用call_user_function, is_int/string/array, strlen , defined 函數。PHP5 都是以擴展函數的方式提供,PHP7中這4類函數改爲ZendVM的OPCODE指令,執行更快。
六 其餘更多優化
除了上面5個主要優化點以外,PHP7還有其餘更多的細節性能優化。如基礎類型int、float、bool等改成直接進行值拷貝,排序算法改進,PCRE with JIT,execute_data和opline使用全局寄存器等等。PHP7對性能的優化會繼續進行下去。
PHP7-alpha相比PHP5.6性能提高了近3倍。下面是WordPress在PHP7上的表現:
PHP7的新特性
除了性能優化外,PHP7新增長了2項重要的新特性。
變量類型
PHP7版本函數的參數和返回值增長了類型限定。爲何PHP要加入類型,實際上此項特性是爲了PHP7.1版本的JIT特性作準備,增長類型後PHP JIT能夠準確判斷變量類型,生成最佳的機器指令。
function test(int $a, string $b, array $c) : int {
//code
}
錯誤異常
PHP程序出錯後過去Zend引擎會發生致命錯誤並終止程序運行,PHP7可使用try/catch捕獲錯誤。底層使用Exeception代替了Fatal Error。這個特性表示PHP語言正在向一個更加規範的方向發展。應用層與底層在錯誤拋出的方式所有統一爲異常。
try {
non_exists_func();
} catch (EngineException $e) {
echo "Exception: {$e->getMessage()}\n";
}
匿名類
$test = new class("Hello World") {
public function __construct($greeting) {
$this->greeting = $greeting;
}
};
PHP7與JIT
最初PHP7性能優化的方向並非以上所講的,而是JIT。JIT是just in time的縮寫,表示運行時將指令轉爲二進制機器碼。Java語言的JVM引擎底層就是使用JIT將Java字節碼編譯爲二進制機器碼執行。PHP7開發過程當中有一箇中間版本是基於JIT,後來開發組發現使用JIT後,對於實際項目並無有太大的性能提高,因此PHP7最終放棄了JIT方案,PHP7.0-final版本不會攜帶JIT特性。
但若是是密集計算類程序就不一樣了,使用JIT將PHP OpCode編譯爲機器碼,運算的性能會大幅提高。PHP官方開發組在2014年末重啓了JIT的開發工做。
PHP的異步網絡通訊擴展Swoole
PHP在大部分程序員印象中都是用來作Web網站的。PHP沒有像Python的Twisted、Tornado,Java的Netty、Mina,JavaScript的Node.js等框架,沒法實現異步網絡通訊程序。PHP的Swoole擴展就是爲了彌補此項缺陷而誕生的開源項目。Swoole是一個標準的PHP擴展,爲PHP提供了一系列異步IO、事件驅動、並行數據結構功能。
Swoole與Node.js很是類似,不一樣之處是Swoole在並行提供了底層支持。Node.js是一個單進程單線程的程序,在多核服務器上沒法發揮所有CPU核的計算能力。須要程序員自行使用child_process/cluster擴展或者啓動多實例,使程序可以利用到多核優點。而Swoole在底層就支持了多線程/多進程,程序啓動後就會建立好多個IO線程和多個Worker進程。程序員僅需配置線程/進程數量便可。
使用Swoole開發的TCP服務器程序:
$serv = new swoole_server("127.0.0.1", 9501);
$serv->on('connect', function ($serv, $fd){
echo "Client:Connect.\n";
});
$serv->on('receive', function ($serv, $fd, $from_id, $data) {
$serv->send($fd, $data);
});
$serv->on('close', function ($serv, $fd) {
echo "Client: Close.\n";
});
$serv->start();
Swoole一樣也內置了http_server和WebSocket服務器的支持。swoole_http_server與傳統的php-fpm不一樣,它是在PHP內進行事件循環的,基於swoole_http_server徹底能夠開發出相似Java應用服務器同樣,能夠控制完整對象生命週期的程序。swoole_http_server自然支持異步IO,能夠很方便的實現支持大量TCP鏈接的Comet服務。swoole_websocket_server能夠用來實現支持Web實時推送的程序。
使用Swoole的Web服務器程序:
$http = new swoole_http_server("0.0.0.0", 9501);
$http->on('request', function ($request, $response) {
$response->header("Content-Type", "text/html; charset=utf-8");
$response->end("
<h1>Hello Swoole. #".rand(1000, 9999)."</h1>
");
});
$http->start();
PHP的將來
能夠預見PHP語言將來會在性能方面有明顯的提高,愈來愈接近C/C++、Java等靜態編譯語言。再加上Swoole擴展,PHP的使用範圍能夠擴展到移動通訊、雲計算、網絡遊戲、物聯網、車聯網、智能家居等領域。
PHP雖然未必是最好的編程語言,但PHP在向着這個方向在發展。