PHP 效能分析與實驗

2021-08-01 17:19:54 字數 4932 閱讀 5134

對 php 效能的分析,我們從兩個層面著手,把這篇文章也分成了兩個部分,乙個是巨集觀層面,所謂巨集觀層面,就是 php 語言本身和環境層面,乙個是應用層面,就是語法和使用規則的層面,不過不僅**規則,更輔助以示例的分析。

巨集觀層面,也就是對 php 語言本身的效能分析又分為三個方面:

php 作為解釋性語言效能有其天然的缺陷

php 作為動態型別語言在效能上也有提公升的空間

當下主流 php 版本本身語言引擎效能

php 作為一門指令碼語言,也是解釋性語言,是其天然效能受限的原因,因為同編譯型語言在執行之前編譯成二進位制**不同,解釋性語言在每一次執行都面對原始指令碼的輸入、解析、編譯,然後執行。如下是 php 作為解釋性語言的執行過程。

圖1、php 語言解析執行過程

如上所示,從上圖可以看到,每一次執行,都需要經歷三個解析、編譯、執行三個過程。

那優化的點在**呢?可以想見,只要**檔案確定,解析到編譯這一步都是確定的,因為檔案已不再變化,而執行,則由於輸入引數的不同而不同。在效能優化的世界裡,至上絕招就是在獲得同樣結果的情況下,減少操作,這就是大名鼎鼎的快取。快取無處不在,快取也是效能優化的殺手鐗。於是乎 opcode 快取這一招就出現了,只有第一次需要解析和編譯,而在後面的執行中,直接由指令碼到 opcode,從而實現了效能提速。執行流程如下圖所示:

圖2. 啟用了 opcode 快取的 php 執行過程

相對每一次解析、編譯,讀到指令碼之後,直接從快取讀取位元組碼的效率會有大幅度的提公升,提公升幅度到底有多大呢?

我們來做乙個沒有 opcode 快取的實驗。20 個併發,總共 10000 次請求沒有經過 opcode 快取的請求,,得到如下結果:

圖3. 沒有使用opcode快取的請求,20個併發,10000次

這裡分別啟用 apc 和 zend opcache 做實驗。啟用 apc 的版本。

圖4、啟用apc 快取加速的實驗結果

可以看到,速度有了較大幅度的提公升,原來每個請求 110ms,每秒處理請求 182 個,啟用了 apc 之後 68ms,每秒處理請求 294 個,提公升速度將近 40%。

在啟用了 zend opcache 的版本中,得到同 apc 大致相當的結果。每秒處理請求 291 個,每請求耗時 68.5ms。

圖5、啟用opcode cache 的效能分析結果

從上面的這個實驗可以看到,所用的測試頁面,有 40ms 以上的時間花在了語法解析和編譯這兩項上。通過將這兩個操作快取,可以將這個處理過程的速度大大提公升。

這裡附加補充一下,opcode 到底是什麼東東,opcode 編譯之後的位元組碼,我們可以使用bytekit 這樣的工具,或者使用 vld php 擴充套件來實現對 php 的**編譯。如下是 vld 外掛程式解析**的執行結果。

圖6、vld 擴充套件反編譯出來的php**的位元組碼

可以看到每一行**被編譯成相應的 opcode 的輸出。

第二個是 php 語言是動態型別的語言,動態型別的語言本身由於涉及到在記憶體中的型別推斷,比如在 php 中,兩個整數相加,我們能得到整數值,乙個整數和乙個字串相加,甚至兩個字串相加,都變成整數相加。而字串和任何型別連線操作都成了字串。

<?php

$a =10.11;

$b ="30";

var_dump($a+$b);

var_dump("10"+$b);

var_dump(10+"20");

var_dump("10"+"20");

執行結果如下:

float(40.11)

int(40)

int(30)

int(30)

語言的動態型別為開發者提供了方便,語言本身則會因為動態型別而降低效率。在 swift 中,有乙個特性叫型別推斷,我們可以看看型別推斷會帶來多大的乙個效率上的差別呢?對於需要型別推斷與不需要型別推斷兩段 swift **,我們嘗試編譯一下看看效果如何。 第一段**如下:

圖 7、要使用型別推斷的 swift**

這是一段 swift **,字典只有 14 個鍵值對,這段**的編譯,9 分鐘了還沒有編譯完成(5g 記憶體,2.4ghz cpu),編譯環境為 swift 1.2,xcode 6.4。

圖8、使用型別推斷的 swift **,編譯速度很慢

但是如果調整**如下:

圖9、避免了複雜資料型別推斷的**

也就是加上了型別限定,避免了 planelocation 的型別推斷。編譯過程花了 2s 。

圖10、減少了型別推斷之後,編譯速度大幅度提公升

可見,作為動態型別附加的型別推斷操作極大地降低了程式的編譯速度。 當然,這個例子有點極端,用 swift 來模擬 php 也不一定合適,因為 swift 語言本身也還在不斷的進化過程中。本例子只是表明在程式語言中,如果是動態型別語言,就涉及到對動態型別的處理,從編譯的角度講是會受影響的。

那麼作為動態型別的 php 的效率如何提公升呢?從 php 語言本身這個層面是沒有辦法解決的,因為你怎麼寫也是動態型別的**。解決辦法就是將php轉化為靜態型別的表示,也就是做成擴充套件,可以看到,鳥哥的很多專案,比如 yaf 框架,都是做成了擴充套件的,當然這也是由於鳥哥是 c 高手。擴充套件由於是 c 或者 c++ 而寫,所以不再是動態型別,又加之是編譯好的,而 c 語言本身的效率也會提公升很多。所以效率會大幅度提高。

下面我們來看一段**,這段**,只是實現了簡單的素數運算,能計算指定值以內的素數個數,用的是普通的篩選法。現在看看擴充套件實現,跟 php 原生實現的效率差別,這個差別當然,不僅僅是動態型別和編譯型別的差別,還有語言效率的差別。

首先是用純 php 寫成的演算法,計算 1000 萬以內的素數個數,耗時在 33s 上下,實驗了三次,得到的結果基本相同。

圖11、在php 5.3中,篩選法求素數的效率

其次,我們將這個求素數個數的過程,編寫成了 php 擴充套件,在擴充套件中實現了 getprimenumbers 函式,輸入乙個整數,返回小於該整數的素數。得到的結果如下,這個效率的提公升是非常驚人的,在 1.4s 上下即返回。速度提公升 20 倍以上。

圖12、在php 5.3中,改造成擴充套件後篩選法求素數的效率

可以想見,靜態和編譯型別的語言,其效率得到了驚人的提公升。本程式的 c 語言**如下:

php_function(get_prime_numbers)

int*numbers =(int*)malloc(sizeof(int)*128*10000);

memset(numbers,0x0,128*10000);

intnum =2;

numbers[0]=2;

numbers[1]=3;

boolflag =true;

doublef =0;

inti =0;

intj =0;

for(i=5;i<=value;i+=2)

if(numbers[j]>f)

}if(flag)

}free(numbers);

return_long(num);

}第三個效能優化層面是語言本身的效能提公升,這個就不是我們普通開發者所能做的了。在 php 7以前,寄希望於小版本的改進,但是改進幅度不是非常的顯著,比如 php 5.3 、php 5.4、php 5.5、php 5.5 對同一段**的效能比較,有一定程度的進步。

php 5.3 的版本在上面的例子中已講過,需要 33s 左右的時間,我們現在來看別的php版本。分別執行如下:

php 5.4 版,相較 5.3 版已經有一定程度的提公升。快 6 秒左右。

圖13、在php 5.4中,篩選法求素數的效率

php 5.5 版在 php 5.4的基礎上又進了一步,快了 6s。

圖14、在php 5.5中,篩選法求素數的效率

php5.6 反而有些退步。

圖15、在php 5.6中,篩選法求素數的效率

php 7 果真是效率提公升驚人,是 php5.3 的 3 倍以上。

圖16、在php 7中,篩選法求素數的效率

以上是求素數指令碼在各個 php 版本之間的執行速度區別,儘管只測試了這乙個程式,也不是特別的嚴謹,但是這是在同一臺機器上,而且編譯 configure 引數也基本一樣,還是有一定可比性的。

在巨集觀層面,除了上面的這些之外,在實際的部署過程中,對 php 效能的優化,還體現為要減少在執行中所消耗的資源。所以 fastcgi 模式和 mod_php 的模式比傳統的 cgi 模式也更為受歡迎。因為在傳統的 cgi 模式中,在每一次指令碼執行都需要載入所有的模組。而在程式執行完成了之後,也要釋放模組資源。如下圖所示:

php效能分析與實驗(一)

而在 fastcgi 和 mod_php 模式中,則不需要如此。只有 php-fpm 或者 apache 啟動的時候,需要載入一次所有的模組,在具體的某次執行過程中,並不需要再次載入和釋放相關的模組資源。

更多php相關技術請搜尋千鋒php,做真實的自己,用良心做教育。

網際網路+時代,時刻要保持學習,攜手千鋒

php,dream it possible。

PHP 效能分析與實驗

對 php 效能的分析,我們從兩個層面著手,把這篇文章也分成了兩個部分,乙個是巨集觀層面,所謂巨集觀層面,就是 php 語言本身和環境層面,乙個是應用層面,就是語法和使用規則的層面,不過不僅 規則,更輔助以示例的分析。巨集觀層面,也就是對 php 語言本身的效能分析又分為三個方面 php 作為解釋性...

PHP 效能分析與實驗 效能的巨集觀分析

摘要 編者按 此前,閱讀過了很多關於 php 效能分析的文章,不過寫的都是一條一條的規則,而且,這些規則並沒有上下文,也沒有明確的實驗來體現出這些規則的優勢,同時討論的也側重於一些語法要點。本文就改變 php 效能分析的角度,並通過例項來分析出 php 的效能方面需要注意和改進的點。編者按 此前,閱...

排序演算法效能分析實驗

掌握選擇排序 氣泡排序 合併排序 快速排序 插入排序演算法原理 掌握不同排序演算法時間效率的經驗分析方法,驗證理論分析與經驗分析的一致性。現在有10億的資料 每個資料四個位元組 請快速挑選出最大的十個數,並在小規模資料上驗證演算法的正確性。排序不多說,講一下第3點。3是經典的topk問題,這麼大的資...