這篇文章帶給大家的內容是關於PHP7更新及效能優化的介紹(圖文),有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。
PHP7革新與性能優化
有幸參與2015年的PHP技術高峰會(PHPCON),聽了鳥哥(惠新宸)的關於PHP7的新特性和效能優化的分享,一切都令人感到興奮。鳥哥是國內最權威的PHP專家,他的分享有很多非常有價值的東西,我透過整理分享的PPT和收集相關資料,整理為這篇解讀性質的技術文章,希望能給做PHP開發的同學一些幫助。
PHP已經走過了20年的歷史,直到今天,PHP7都發布了RC版,據說,PHP7正式版應該會在2015年11月份左右發布。 PHP7對於上一個系列的PHP5.*,可以說是一個大規模的革新,尤其是在性能方面實現跨越式的大幅提升。
PHP是一種在全球廣泛使用的Web開發語言,PHP7的革新也當然會為這些Web服務帶來更深刻的改變。這裡引用鳥哥PPT中的一個圖表(82%的Web網站有使用PHP作為開發語言):
(註:一個web網站可以會使用多種語言作為它的開發語言)
(註:本文含有不少從鳥哥PPT裡的截圖,圖片版權歸鳥哥所有)
我們先看看兩張激動人心的性能測試結果圖:
Benchmark比較(圖片來自於PPT):
PHP7的效能測試結果,效能壓測結果,耗時從2.991下降到1.186,大幅度下降60%。
WordPress的QPS壓測(圖片來自於PPT):
而在WordPress專案中,PHP7對比PHP5.6,QPS提升2.77倍。
看完令人興奮的效能測試結果對比,我們就進入正題哈。 PHP7的新增特性很多,不過,我們會更聚焦在那些主要的變化。
一、新增功能與改變
1. 標量類型與回傳類型宣告(Scalar Type Declarations & Scalar Type Declarations)
PHP語言一個非常重要的特點是“弱類型”,它讓PHP的程式變得非常容易編寫,新手接觸PHP能夠快速上手,不過,它也伴隨著一些爭議。支持變數類型的定義,可以說是革新性質的變化,PHP開始以可選的方式支援類型定義。除此之外,還引入了一個開關指令declare(strict_type=1);,當這個指令一旦開啟,將會強制當前文件下的程序遵循嚴格的函數傳參類型和返回類型。
例如一個add函數加上類型定義,可以寫成這樣:
如果配合強制類型開關指令,則可以變成這樣:
如果不開啟strict_type,PHP將會嘗試幫你轉換成要求的類型,而開啟之後,會改變PHP就不再做類型轉換,類型不匹配就會拋出錯誤。對於喜歡「強型別」語言的同學來說,這是一大福音。
更詳細的介紹:
PHP7標量類型宣告RFC[翻譯]
2. 更多的Error變成可擷取的Exception
PHP7實作了一個全域的throwable接口,原來的Exception和部分Error都實作了這個介面(interface), 以介面的方式定義了異常的繼承結構。於是,PHP7中更多的Error變為可捕獲的Exception返回給開發者,如果不進行捕獲則為Error,如果捕獲就變為一個可在程序內處理的Exception。這些可被捕獲的Error通常都是不會對程式造成致命傷害的Error,例如函數不存。 PHP7進一步方便開發者處理,讓開發者對程式的掌控能力更強。因為在預設情況下,Error會直接導致程式中斷,而PHP7則提供捕捉並且處理的能力,讓程式繼續執行下去,為程式設計師提供更靈活的選擇。
例如,執行一個我們不確定是否存在的函數,PHP5相容的做法是在函數被呼叫之前追加的判斷function_exist,而PHP7則支援捕獲Exception的處理方式。
如下圖的範例(截圖來自PPT內):
3. AST(Abstract Syntax Tree,抽象語法樹)
AST在PHP編譯過程中作為一個中間件的角色,替換原來直接從解釋器吐出opcode的方式,讓解釋器(parser)和編譯器(compliler)解耦,可以減少一些Hack程式碼,同時,讓實作更容易理解和可維護。
PHP5:
PHP7:
更多AST資訊:
https://wiki.php.net/rfc/abstract_syntax_tree
4. Native TLS(Native Thread local storage,原生執行緒本地儲存)
PHP在多執行緒模式下(例如, Web伺服器Apache的woker和event模式,就是多執行緒),需要解決「線程安全」(TS,Thread Safe)的問題,因為執行緒是共享進程的記憶體空間的,所以每個執行緒本身需要透過某種方式,建構私有的空間來保存自己的私有數據,避免和其他執行緒相互污染。而PHP5採用的方式,就是維護一個全域大數組,為每個執行緒分配一份獨立的儲存空間,而執行緒透過各自擁有的key值來存取這個全域資料組。
而這個獨特的key值在PHP5中需要傳遞給每一個需要用到全域變數的函數,PHP7認為這種傳遞的方式並不友好,並且存在一些問題。因而,嘗試採用一個全域的執行緒特定變數來保存這個key值。
相關的Native TLS問題:
https://wiki.php.net/rfc/native-tls
5. 其他新功能
# PHP7新功能和變化不少,我們這裡並不全部展開來細說哈。
(1) Int64支持,統一不同平台下的整數長度,字串和檔案上傳都支援大於2GB。
(2) 統一變數語法(Uniform variable syntax)。
(3) foreach表現行為一致(Consistently foreach behaviors)
(4) 新的操作符 , ??
#(5) Unicode字元格式支援(\u{xxxxx})
(6) 匿名類別支援(Anonymous Class)
… …
二、跨越式的效能突破:全速前進
1. JIT與效能
Just In Time(即時編譯)是一種軟體最佳化技術,指在運作時才會去編譯字節為機器碼。從直覺出發,我們都很容易認為,機器碼是電腦能夠直接辨識和執行的,比起Zend讀取opcode逐條執行效率會更高。其中,HHVM(HipHop Virtual Machine,HHVM是一個Facebook開源的PHP虛擬機)就採用JIT,讓他們的PHP性能測試提升了一個數量級,放出一個令人震驚的測試結果,也讓我們直觀地認為JIT是一項點石成金的強大技術。
而實際上,在2013年的時候,鳥哥和Dmitry(PHP語言核心開發者之一)就曾經在PHP5.5的版本上做過一個JIT的嘗試(並沒有發布)。 PHP5.5的原來的執行流程,是將PHP程式碼透過詞法和語法分析,編譯成opcode字節碼(格式和組譯有點像),然後,Zend引擎讀取這些opcode指令,逐條解析執行。
而他們在opcode環節後引入了類型推論(TypeInf),然後透過JIT產生ByteCodes,然後再執行。
於是,在benchmark(測試程式)中得到令人興奮的結果,實現JIT後效能比PHP5.5提升了8倍。然而,當他們把這個優化放入到實際的專案WordPress(一個開源部落格專案)中,卻幾乎看不見效能的提升,得到了一個令人費解的測試結果。
於是,他們使用Linux下的profile類型工具,對程式執行進行CPU耗時佔用分析。
執行100次WordPress的CPU消耗的分佈(截圖來自PPT):
註解:
21%CPU時間花費在記憶體管理。
12%CPU時間花費在hash table操作,主要是PHP陣列的增刪改查。
30%CPU時間花費在內建函數,例如strlen。
25%CPU時間花費在VM(Zend引擎)。
經過分析後,得到了兩個結論:
(1)JIT產生的ByteCodes如果太大,會造成CPU快取命中率下降(CPU Cache Miss)
在PHP5.5的程式碼裡,因為並沒有明顯類型定義,只能靠類型推斷。盡可能將可以推斷出來的變數類型,定義出來,然後,結合類型推斷,將非該類型的分支程式碼去掉,產生直接可執行的機器碼。然而,類型推斷不能推斷出全部類型,在WordPress中,能夠推斷出來的類型資訊只有不到30%,能夠減少的分支代碼有限。導致JIT以後,直接產生機器碼,產生的ByteCodes太大,最終造成CPU快取命中大幅下降(CPU Cache Miss)。
CPU快取命中是指,CPU在讀取並執行指令的過程中,如果需要的資料在CPU一級快取(L1)中讀取不到,就必須往下繼續尋找,一直到二級快取(L2)和三級快取(L3),最終會嘗試到記憶體區域裡尋找所需的指令數據,而記憶體和CPU快取之間的讀取耗時差距可以達到100倍級別。所以,ByteCodes如果過大,執行指令數量過多,導致多層快取無法容納如此之多的數據,部分指令將不得不被存放到記憶體區域。
CPU的各級快取的大小也是有限的,下圖是Intel i7 920的設定資訊:
因此,CPU快取命中率下降會帶來嚴重的耗時增加,另一方面,JIT帶來的效能提升,也被它所抵消掉了。
透過JIT,可以降低VM的開銷,同時,透過指令優化,可以間接降低記憶體管理的開發,因為可以減少記憶體分配的次數。然而,對於真實的WordPress專案來說,CPU耗時只有25%在VM上,主要的問題和瓶頸實際上並不在VM上。因此,JIT的最佳化計劃,最後沒有被列入該版本的PHP7特性。不過,它很可能會在更後面的版本中實現,這一點也非常值得我們期待哈。
(2)JIT效能的提升效果取決於專案的實際瓶頸
JIT在benchmark中有大幅的提升,是因為程式碼量比較少,最終產生的ByteCodes也比較小,同時主要的開銷是在VM中。而應用在WordPress實際專案中並沒有明顯的效能提升,原因WordPress的程式碼量要比benchmark大得多,雖然JIT降低了VM的開銷,但是因為ByteCodes太大而又造成CPU快取命中下降和額外的內存開銷,最終變成沒有提升。
不同類型的專案會有不同的CPU開銷比例,也會得到不同的結果,脫離實際專案的效能測試,並不具有很好的代表性。
2. Zval的改變
PHP的各種類型的變量,其實,真正儲存的載體就是Zval,它特徵是海納百川,有容乃大。從本質上看,它是C語言實現的一個結構體(struct)。對於寫PHP的同學,可以將它粗略地理解為是一個類似array數組的東西。
PHP5的Zval,記憶體佔據24個位元組(截圖來自PPT):
PHP7的Zval,記憶體佔據16個位元組(截圖來自PPT):
Zval從24個位元組下降到16個位元組,為什麼會下降呢,這裡需要補一點點的C語言基礎,輔助不熟悉C的同學理解。 struct和union(聯合體)有點不同,Struct的每一個成員變數要各自佔據一塊獨立的記憶體空間,而union裡的成員變數是共用一塊記憶體空間(也就是說修改其中一個成員變量,公有空間就被修改了,其他成員變數的記錄也就沒有了)。因此,雖然成員變數看起來多了不少,但是實際佔據的記憶體空間卻下降了。
除此之外,還有被明顯改變的特性,部分簡單型別不再使用引用。
Zval結構圖(源自PPT):
圖中Zval的由2個64bits(1位元組=8bit,bit是「位元」)組成,如果變數類型是long、bealoon這些長度不超過64bit的,則直接儲存到value中,就沒有下面的引用了。當變數類型是array、objec、string等超過64bit的,value儲存的就是一個指針,指向真實的儲存結構位址。
對於簡單的變數類型來說,Zval的儲存變得非常簡單且有效率。
不需要引用的類型:NULL、Boolean、Long、Double
需要引用的類型:String、Array、Object、Resource、Reference
3. 內部類型zend_string
Zend_string是實際儲存字串的結構體,實際的內容會儲存在val(char,字元型)中,而val是一個char數組,長度為1(方便成員變數佔位)。
結構體最後一個成員變數採用char數組,而不是使用char*,這裡有一個小最佳化技巧,可以降低CPU的cache miss。
如果使用char數組,當malloc申請上述結構體內存,是申請在同一片區域的,通常是長度是sizeof(_zend_string) 實際char存儲空間。但是,如果使用char*,那個這個位置儲存的只是一個指針,真實的儲存又在另外一片獨立的記憶體區域內。
使用char[1]和char*的記憶體分配比較:
#從邏輯實作的角度來看,兩者其實也沒有太大差別,效果很類似。而實際上,當這些記憶體區塊被載入到CPU的中,就顯得非常不一樣。前者因為是連續分配在一起的同一塊內存,在CPU讀取時,通常都可以一同獲得(因為會在同一級緩存中)。而後者,因為是兩塊內存的數據,CPU讀取第一塊內存的時候,很可能第二塊內存數據不在同一級緩存中,使CPU不得不往L2(二級緩存)以下尋找,甚至到記憶體區域查到想要的第二塊記憶體資料。這裡就會造成CPU Cache Miss,兩者的耗時最高可以相差100倍。
另外,在字串複製的時候,採用引用賦值,zend_string可以避免的記憶體拷貝。
6. PHP數組的變化(HashTable和Zend Array)
在編寫PHP程式過程中,使用最頻繁的類型莫過於數組,PHP5的數組採用HashTable實作。如果用比較粗略的概括方式來說,它算是一個支援雙向鍊錶的HashTable,不僅支援透過陣列的key來做hash映射存取元素,也能透過foreach以存取雙向鍊錶的方式遍歷數組元素。
PHP5的HashTable(截圖來自於PPT):
這個圖看起來很複雜,各種指標跳來跳去,當我們透過key值存取一個元素內容的時候,有時需要3次的指標跳躍才能找對需要的內容。而最重要的一點,就在於這些數組元素存儲,都是分散在各個不同的記憶體區域的。同理可得,在CPU讀取的時候,因為它們就很可能不在同一級快取中,會導致CPU不得不到下級快取甚至記憶體區域查找,也就是造成CPU快取命中下降,進而增加更多的耗時。
PHP7的Zend Array(截圖來自PPT):
新版本的陣列結構,非常簡潔,讓人眼睛一亮。最大的特點是,整塊的陣列元素和hash映射表全部連接在一起,被分配在同一塊記憶體內。如果是遍歷一個整數的簡單型別數組,效率會非常快,因為,數組元素(Bucket)本身是連續分配在同一塊記憶體裡,而且,數組元素的zval會把整數元素儲存在內部,也不再有指針外鏈,全部資料都儲存在目前記憶體區域內。當然,最重要的是,它能夠避免CPU Cache Miss(CPU快取命中率下降)。
Zend Array的變更:
(1) 陣列的value預設為zval。
(2) HashTable的大小從72降到56位元組,減少22%。
(3) Buckets的大小從72下降到32字節,減少50%。
(4) 數組元素的Buckets的記憶體空間是一同分配的。
(5) 陣列元素的key(Bucket.key)指向zend_string。
(6) 陣列元素的value被嵌入到Bucket中。
(7) 降低CPU Cache Miss。
7. 函數呼叫機制(Function Calling Convention)
PHP7改善了函數的呼叫機制,透過最佳化參數傳遞的環節,減少了一些指令,提高執行效率。
PHP5的函數呼叫機制(截圖來自於PPT):
圖中,在vm堆疊中的指令send_val和recv參數的指令是相同,PHP7透過減少這兩條重複,來達到對函數呼叫機制的底層最佳化。
PHP7的函數呼叫機制(截圖來自於PPT):
8. 透過巨集定義和內聯函數(inline ),讓編譯器提前完成部分工作
C語言的巨集定義會被在預處理階段(編譯階段)執行,提前將部分工作完成,無需在程式執行時分配內存,能夠實現類似函數的功能,卻沒有函數呼叫的壓棧、彈棧開銷,效率會比較高。內聯函數也類似,在預處理階段,將程式中的函數替換為函數體,真實運行的程式執行到這裡,就不會產生函數呼叫的開銷。
PHP7在這方面做了不少的最佳化,將不少需要在執行階段要執行的工作,放到了編譯階段。例如參數類型的判斷(Parameters Parsing),因為這裡涉及的都是固定的字元常數,因此,可以放到編譯階段來完成,進而提升後續的執行效率。
例如下圖中處理傳遞參數類型的方式,從左邊的寫法,最佳化為右邊巨集的寫法。
三、小結
鳥哥的PPT裡放過一組對比數據,就是WordPress在PHP5.6執行100次會產生70億次的CPU指令執行數目,而在PHP7中只需要25億次,減少64.2%,這是一個令人震撼的數據。
在鳥哥的整個分享中,給我最深刻的一個觀點是:要注意細節,很多細小的優化,一點點持續地積累,積少成多,最終匯聚為驚豔的成果。為山九仞,豈一日之功,我想大概也是這個道理。
毫無疑問,PHP7在效能方面實現跨越式的提升,如果能夠將這些成果應用在PHP的Web系統中,也許我們只需要更少的機器,就可以支撐起更高請求量的服務。 PHP7正式版的發布,令人充滿無限憧憬。
以上是PHP7更新及效能最佳化的介紹(圖文)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

PHP在電子商務、內容管理系統和API開發中廣泛應用。 1)電子商務:用於購物車功能和支付處理。 2)內容管理系統:用於動態內容生成和用戶管理。 3)API開發:用於RESTfulAPI開發和API安全性。通過性能優化和最佳實踐,PHP應用的效率和可維護性得以提升。

PHP可以輕鬆創建互動網頁內容。 1)通過嵌入HTML動態生成內容,根據用戶輸入或數據庫數據實時展示。 2)處理表單提交並生成動態輸出,確保使用htmlspecialchars防XSS。 3)結合MySQL創建用戶註冊系統,使用password_hash和預處理語句增強安全性。掌握這些技巧將提升Web開發效率。

PHP和Python各有優勢,選擇依據項目需求。 1.PHP適合web開發,尤其快速開發和維護網站。 2.Python適用於數據科學、機器學習和人工智能,語法簡潔,適合初學者。

PHP仍然具有活力,其在現代編程領域中依然佔據重要地位。 1)PHP的簡單易學和強大社區支持使其在Web開發中廣泛應用;2)其靈活性和穩定性使其在處理Web表單、數據庫操作和文件處理等方面表現出色;3)PHP不斷進化和優化,適用於初學者和經驗豐富的開發者。

PHP在現代Web開發中仍然重要,尤其在內容管理和電子商務平台。 1)PHP擁有豐富的生態系統和強大框架支持,如Laravel和Symfony。 2)性能優化可通過OPcache和Nginx實現。 3)PHP8.0引入JIT編譯器,提升性能。 4)雲原生應用通過Docker和Kubernetes部署,提高靈活性和可擴展性。

PHP適合web開發,特別是在快速開發和處理動態內容方面表現出色,但不擅長數據科學和企業級應用。與Python相比,PHP在web開發中更具優勢,但在數據科學領域不如Python;與Java相比,PHP在企業級應用中表現較差,但在web開發中更靈活;與JavaScript相比,PHP在後端開發中更簡潔,但在前端開發中不如JavaScript。

PHP和Python各有優勢,適合不同場景。 1.PHP適用於web開發,提供內置web服務器和豐富函數庫。 2.Python適合數據科學和機器學習,語法簡潔且有強大標準庫。選擇時應根據項目需求決定。

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

Dreamweaver CS6
視覺化網頁開發工具

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。