從PHP5.0到PHP7.1的性能全評測

本文是最初是來自國外的這篇:PHP Performance Evolution 2016, 感謝高可用架構公衆號翻譯成了中文版, 此處是轉載的高可用架構翻譯後的文章從PHP 5到PHP 7性能全評測(含未發佈的JIT版PHP 8對比), 稍微調整了格式而成。
php

導讀:PHP 是 Web 開發最經常使用的語言,每一個大版本的更新都帶來很多新特性和性能提高。特別是 PHP 7.0 的發佈,帶來 PHP 性能飛躍。本文做者對各個 PHP 版本進行了 CPU 性能基準測試,而且帶來了PHP下個大版本的消息。本文中文版由高可用架構志願者翻譯。html

自 1994 年 Rasmus Lerdorf 建立 PHP 以來, PHP 語言經歷了許多改進,其中性能是開發人員在評估新版本時考慮的主要標準之一。mysql

閱讀這篇文章,能夠了解從 PHP 5 到 7(包括 7.1)的性能提高,同時也將瞭解到即將加入到 PHP 8 的試驗性的 JIT 分支版本的性能。git

簡介

本文將根據時間做出更新,增長更多信息和基準測試結果,包括還沒有發佈的新版本,以便更好地瞭解多年來 PHP 性能演變。若是您有更正或建議改進,請在文後留言。github

自 1994 年 Rasmus Lerdorf 建立 PHP 以來, PHP 語言經歷了激烈的演進。雖然初版是一個簡單的一人開發的 CGI 程序,Rasmus Lerdorf、Andi Gutmans 和 Zeev Suraski 加入了該語言的第三個版本的開發,並根本性從新設計。從那以後, PHP 開發組也建立並發展起來。sql

隨着項目的發展,因爲 PHP 3 自然的可擴展性, PHP 在覈心和附加擴展開發的功能獲得了蓬勃發展,如網絡通訊,解析,緩存和數據庫支持。shell

語言自己也在發展,帶來了一系列的改進。這包括支持面向對象的結構,例如類,接口, traits,閉包等。數據庫

對於許多開發人員來講,僅有新功能是不夠的。隨着語言愈來愈受歡迎, PHP 社區對於提供更好性能,可擴展性和更少內存使用的需求愈來愈強烈。緩存

PHP 開發團隊近 20 年來一直致力於解決這些需求,雖然 PHP 3 的引入大大提升了性能,但直到 Andi Gutmans 和 Zeev Suraski 引入 Zend Engine 併發布 PHP 4, PHP 的性能纔開始變得正式起來。性能優化

2000 年推出的新的內存編譯器和執行器模型大大提升了 PHP 的性能(提升了 5 倍甚至 10 倍),並首次被正式的 Web 應用程序和站點所使用。咱們能夠說,今天 PHP 的成果遠遠超出了任何人在 PHP 項目誕生時的指望。

PHP 的蓬勃發展增長了改善性能的慾望。幸運的是, Zend Engine 中設計的模型爲持續優化性能提供了良好的基礎。

雖然 PHP 5.0 沒有帶來實質性的性能提高,而且在某些狀況下甚至比 PHP4 更慢,一個由 Dmitry Stogov 領導的團隊在社區的大力幫助下已經在後續版本中不斷優化語言,在 PHP 5.6 發佈的時候,在大多數狀況下,性能提高在 1.5x 和 3x 之間。

2015 年 12 月, PHP 7.0 取得了重大突破。 2016 年 12 月,7.1 版本也帶來了一系列加強功能。

PHP 8 性能展望

這是一個前途光明的版本,目前正在開發當中,由 Zend 的 Dmitry Stogov 主導。雖然它是基於 PHP 7.1 版本基礎,但實際版本號還沒有定義,因此本文稱這個版本爲「試驗 JIT」分支下。

關鍵功能 JIT(Just-In-Time)編譯,是一種將代碼轉換爲另外一種字節碼(好比運行它的機器 CPU 的本地代碼)的技術。 JIT 可使程序運行更快。

本文涵蓋了幾個基準測試的結果,從 PHP 5 的第一個版本到 PHP 的試驗性 JIT 分支版本,PHP 5 以前的版本性能本文不做介紹。

在寫這篇文章的時候,咱們很難肯定 PHP 8 以前是否會有另外一個主要版本,好比 PHP 7.2。可是能夠假設在 PHP 8 發佈時,它已經包括當前試驗版 JIT 分支的強大功能。

PHP 性能評估

本文只運行純 CPU 任務腳本的基準測試(不須要I / O操做的任務例如訪問文件,網絡或數據庫鏈接)。

使用的基準測試腳本以下所示:

  1. bench.php 可在PHP源代碼的 php-src/Zend 目錄
  2. micro_bench.php 也能夠在 PHP 源代碼發佈的 php-src/Zend 目錄中找到
  3. mandelbrot.php https://gist.githubusercontent.com/dstogov/12323ad13d3240aee8f1/raw/37fed3beb7e666b70e199bcf361af541b3c30d2d/b.php

基準腳本僅使用每一個PHP主要版本的最新小版本運行。所以,測試的版本以下:

  1. 5.0.5
  2. 5.1.6
  3. 5.2.17
  4. 5.3.29
  5. 5.4.45
  6. 5.5.38
  7. 5.6.28
  8. 7.0.13
  9. 7.1.0
  10. PHP-JIT(JIT實驗分支)

固然,我想肯定,咱們在相同的基準上運行全部小版本,例如在 5.3.0 到 5.3.29 之間。結果是有說服力的:性能方面的主要加強不是由小版本帶來的,而是主要版本號的變化,例如從 PHP 5.4 到 PHP 5.5,或從PHP 5.6 到 PHP 7。

小版本沒有顯示任何明顯的性能改進。這意味着相同的腳本應該以相同的速度運行,不管您使用 PHP 5.4.0 仍是 PHP 5.4.45。

您能夠查看基準進程部分,詳細說明主機系統的設置,各個基準的運行方式以及如何解釋時序結果。

純 CPU 基準測試結果

這部分給出了每一個 PHP 版本的基準測試結果。

每一個基準列顯示 3 個值:

  • 時間: 執行時間,以秒和毫秒爲單位
  • %rel, gain:相對於之前的版本收益的執行時間。 在下面的表格中,例如,%rel。 bench.php 和版本 5.3.29 的收益是 31.89%,意味着該腳本比 5.2.17 版本運行快 31.89%。
  • %abs, gain:與 PHP 5.0 相比腳本運行的收益。 若是你看看bench.php 和試驗性的 JIT 分支的這個列的交集,你會注意到,對於這個特定的測試基準,PHP 8 比 PHP 5.0 快 41 倍以上。

純CPU基準測試的結果以下所示:

 

CPU基準測試CPU基準測試

(1)測試不能在 5.3 以前的版本上運行,由於它使用了還沒有實現的對象功能。
(2)此列中的結果有點偏頗,由於基準須要至少 PHP 5.3 運行。把它們當成純粹說明,由於他們不能與 PHP 5.0 性能進行比較。
(3)這是一個 mandelbrot.php 腳本的修改版本,它運行得太快,在 7.1.0 和試驗 JIT 分支沒法準確的統計時間,咱們在腳本中運行計算 100 次而不是 1 次。

 

純CPU測試曲線圖

固然,這些都是純 CPU 的基準測試。它們不涵蓋 PHP 性能的全部方面,它們可能不表明真實狀況。可是結果足夠顯著,足以說明幾個方面的問題:

  • PHP 5.1 將 PHP 5.0的 性能提升了一倍多
  • 5.2 和 5.3 帶來了他們本身的一些性能加強,但他們沒有像5.1版本那樣引人注目。
  • 5.4 版本是一個大的性能改進。(這裏有我曾經分享過的PHP5.4的性能優化的演講PPTPHP-5.4 Performance
  • opcache 擴展插件與 5.5 和 5.6 版捆綁在一塊兒。當相同的腳本在 Web 服務器連續運行時,因爲更快的代碼加載會帶來性能加強。可是,opcache 不會真正顯示其在CLI模式下執行腳本的優點。
  • PHP 7.0 是性能方面的一個重大突破。 Zend Engine 已經徹底從新設計,咱們能夠在這裏看到這項工做的結果。(這裏有我曾經分享過的PHP7的性能優化的演講的PPTThe secret of PHP7’s Performance 
  • PHP 7.1 在 opcache 擴展中引入了 opcode 優化。這再次解釋了上述表格中當與 7.0 相比時,性能的增益。(這裏有我曾經分享過的PHP7.1的性能優化的演講PPTPHP 7.1’s New Features and Performance
  • 試驗 JIT 分支是另外一個重大突破,JIT 能夠對現有代碼提供很大的性能改進,但在某些狀況下,你可能會注意到速度提升只有幾個百分點,在最壞的狀況下,它甚至可能會變慢,由於編譯不會生成更快的代碼。請記住,此功能目前正在開發中。

本節介紹了 3 個純 CPU 基準測試腳本的結果。在運行一般執行的以數據庫或文件訪問典型場景的 PHP 應用程序時,它不會給出一樣的數字,但我認爲他們可以表明您對代碼的某些部分指望的性能改進。

PHP 每一個版本的性能提高

PHP 5 相比 PHP 4 帶來了明顯的改進。 Zend Engine 是 PHP 解釋器的核心,它已經徹底從新設計( Zend Engine 2),爲未來的加強功能奠基了基礎。本文很少介紹 PHP 4 和 PHP 5 之間的差別,只簡要概述的 PHP 5.0 以後發生了什麼。

如下部分列出了在後續 PHP 版本中的改進。請注意,這裏僅列出影響 PHP 核心的修改。有關更完整的描述,請查看 PHP 5 和 PHP 7 的change log。

PHP 5.1

  • Compiled variables
  • Specialized executor
  • Real-path cache
  • Faster switch() statement handling
  • Faster array functions
  • Faster variable fetches
  • Faster magic method invocations

PHP 5.2

  • New memory manager
  • Optimized array/HashTable copying
  • Optimized require_once() and include_once() statements
  • Small optimization on specific internal functions
  • Improved compilation of HEREDOCS and compilation of interpolated strings

PHP 5.3

  • Segmented VM stack
  • Stackless VM
  • Compile-time constants substitution
  • Lazy symbol table initialization
  • Real-path cache improvement
  • Improved PHP runtime speed and memory usage
  • Faster language parsing
  • Improved PHP binary size and code startup

PHP 5.4

  • Delayed HashTable allocation
  • Constant tables
  • Run-Time binding caches
  • Interned Strings
  • Improved the output layer
  • Improved ternary operator performance when using arrays

PHP 5.5

  • Improved VM calling convention
  • OPcache integration
  • Other misc. optimizations to the Zend Engine

PHP 5.6

  • Optimized empty string handling, minimizing the need to allocate new empty values

PHP 7.0

下面大部分列出的改進都與 Zend Engine 相關:

  • Core data structures re-factoring
  • Better VM calling convention
  • New parameters parsing API
  • Yet another new memory manager
  • Many improvements in VM executor
  • Significantly reduced memory usage
  • Improved __call() and __callStatic() functions
  • Improved string concatenation
  • Improved character searching in strings

PHP 7.1

  • New SSA based optimization framework (embedded into opcache)
  • Global optimization of PHP bytecode based on type inference
  • Highly specialized VM opcode handlers

PHP 8 / 下一代試驗性 JIT 分支版

  • Just-In-Time compiling

性能如何衡量

基準化比單純運行 Unix 時間命令來測量腳本的執行有所區別。 這就是爲何我經歷瞭如下步驟:

配置系統

首先我設置了一個具備如下特性的專用系統:

  • 一個帶有1個2.4GHz虛擬內核,2GB RAM和兩個SSD驅動器的VPS,一個用於存儲操做系統數據,另外一個用於存儲各類PHPyuan dai ma,二進制文件和報告輸出
  • Debian Wheezy操做系統,版本3.2.82-1
  • Gnu C編譯器版本4.9.2-10(Debian Jessie發行版)
  • 雖然系統捆綁了Gnu C編譯器版本4.7.2,但須要升級到更新的版本。 試驗性 JIT 分支必須用Gnu C> = 4.8編譯。

編譯源代碼

在構建完整發行版以前,使用如下選項運行配置腳本:

  1. --prefix=/usr/local/php
  2. --disable-debug
  3. --disable-phpdbg
  4. --enable-mysqlnd
  5. --enable-bcmath
  6. --with-bz2=/usr
  7. --enable-calendar
  8. --with-curl
  9. --enable-exif
  10. --enable-fpm
  11. --with-freetype-dir
  12. --enable-ftp
  13. --with-gd
  14. --enable-gd-jis-conv
  15. --enable-gd-native-ttf
  16. --with-gettext=/usr
  17. --with-gmp
  18. --with-iconv
  19. --enable-intl
  20. --with-jpeg-dir
  21. --enable-mbstring
  22. --with-mcrypt
  23. --with-openssl
  24. --enable-pcntl
  25. --with-pdo-mysql=mysqlnd
  26. --with-png-dir
  27. --with-recode=/usr
  28. --enable-shmop
  29. --enable-soap
  30. --enable-sockets
  31. --enable-sysvmsg
  32. --enable-sysvsem
  33. --enable-sysvshm
  34. --enable-wddx
  35. --with-xmlrpc
  36. --with-xsl
  37. --with-zlib=/usr
  38. --enable-zip
  39. --with-mysqli=mysqlnd

注意,在編譯舊版時,上面的一些選項須要被禁用或被其餘替代,而且並非全部的擴展均可用或能夠被編譯。

運行基準測試

每一個基準測試都使用 PHP CLI 專用腳本運行,該腳本遵循如下步驟:

使用 microtime()函數從內部獲取腳本執行時間。 在此修改後,基準腳本將以下所示:

  1. <?php
  2.     $__start__ = microtime( true );
  3.     /***
  4.         original benchmark script code here
  5.     ***/
  6.     fprintf( STDERR, microtime( true ) - $__start__);
  7.  ?>

執行 2 次運行,以確保 PHP 可執行文件和基準測試腳本內容都在操做系統緩存中
運行腳本 5 次,並提取最小,最大和平均運行時間,如腳本報告。 本文僅顯示平均運行時間,稱之爲「腳本運行時間」。

php.ini 文件以下所示:

  1. engine = On
  2. short_open_tag = Off
  3. realpath_cache_size = 2M
  4. max_execution_time = 86400
  5. memory_limit = 1024M
  6. error_reporting = 0
  7. display_errors = 0
  8. display_startup_errors = 0
  9. log_errors = 0
  10. default_charset = "UTF-8"
  11.  
  12. [opcache]
  13. zend_extension=opcache.so
  14. opcache.enable=1
  15. opcache.enable_cli=1
  16. opcache.optimization_level=-1
  17. opcache.fast_shutdown=1
  18. opcache.validate_timestamps=1
  19. opcache.revalidate_freq=60
  20. opcache.use_cwd=1
  21. opcache.max_accelerated_files=100000
  22. opcache.max_wasted_percentage=5
  23. opcache.memory_consumption=128
  24. opcache.consistency_checks=0
  25. opcache.huge_code_pages=1
  26.  
  27. // PHP 8/Next only
  28. opcache.jit=35
  29. opcache.jit_buffer_size=32M

分析運行結果

使用 Unix time 命令來計時,輸出以下所示:

  1. $ time php bench.php
  2. real: 0m1.96s
  3. user: 0m1.912s
  4. sys: 0m0.044s

第一個值,real : 是命令開始到終止之間的時間(當你回到 shell 提示符)。

第二個值,user :說明在用戶模式中花費的時間(在咱們的例子中,這是在 php 可執行文件中花費的時間)。

最後一個值 sys :說明在操做系統(內核)調用中花費的時間。這個值應該是最小的,可是若是你的代碼訪問緩慢的設備結果會比較大。重負載的操做系統也可能影響此處報告的值。

在空閒系統上一般,數量(user + sys)應該很是接近 real。這是在上面的例子中的狀況:user + sys = 1.956s,real 是 1.960s。 0.004s 的差別不屬於當前進程:它僅僅意味着操做系統執行任務所花費的額外時間,例如調度。

同一個腳本在一個負載很重的系統上執行,並行編譯 3 個不一樣的 PHP 版本:

  1. $ time php bench.php
  2. real: 0m7.812s
  3. user: 0m2.02s
  4. sys: 0m0.101s

在這裏我清楚地看到,系統自己的重負載對使用的時間(也許在系統時間)有重大影響。

這就是爲何我在這個基準中保留一個額外的值,操做系統開銷,這是調用的時間和(用戶+系統)時間之間的差。

在純 CPU 基準測試活動期間,我確保在 99% 以上的時間,這個值嚴格小於 100 毫秒,即便運行須要幾十秒鐘完成的腳本。

特別鳴謝

特別鳴謝 Dmitry Stogov 和全部 PHP 核心開發者們。

本文是和 Dmitry Stogov 合做完成的 , 他幫助審閱了文章內容 , 來保證這個文章的正確性。

Dmitry Stogov 曾經是 Truck MMCache 的開發者,在 PHP4 時代就能夠用做共享內存中緩存 PHP Opcode,從那時候起,Dmitry Stogov 就加入了 Zend,一直到如今。

Dmitry 是 PHP NG 的主要開發者 , 也就是咱們後來知道的 PHP7, 和 Dmitry 一塊兒合做的有 Xinchen Hui(就是我 :)),Nikita Popov,正是他們在一塊兒開發了 PHP7 以及後來的版本包括 PHP JIT。

在 PHP7 以前 , PHP5 時代的 Andi Gumans, Zeev Suraski 以及 Stas Malishev 等也作了不少的工做來提高 PHP5 的性能,限於篇幅,本文就不詳細介紹。

結論

本文的目的是給你一個不一樣版本PHP性能的概述,從 5.0 開始,到當前正在開發的最新版本,使用一組已知的基準腳本。

它還爲您提供了由每一個連續 PHP 版本解決的性能改進方面的列表。

本文將隨着新的 PHP 版本的公佈而更新,而且未來會添加新的基準測試結果。 我也但願添加一些真實世界的 PHP 應用程序,如 WordPress 的基準測試結果。

若是您有任何問題或發現不許確,請隨時發表評論。 同時,與其餘對 PHP 性能感興趣的開發人員分享這篇文章。

相關文章
相關標籤/搜索