PHP 的效能測試

有篇有名的文章叫做:40 Tips for optimizing your php code ,不過原文已經消失了,這邊有轉貼版 ,至於翻譯則是在論壇上面隨便找的(看得懂就好,懶得去找原本是誰翻的,我猜是對岸網友)。

所以我做了一點實驗…

測試環境:
CPU: Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz
Memory: 8007 MB (用 Free -m 看)
Kernel: Linux 2.6.26-2-amd64 (debian)
FS: reiserfs
OS: Debian x86_64
PHP: PHP 5.2.6-1+lenny8 with Suhosin-Patch 0.9.6.2 (cli)

載入模組 (只有預先關閉 APC)
[PHP Modules] bcmath, bz2, calendar, ctype, curl, date, dba, dom, exif, filter, ftp, gd, gettext, hash, iconv, imagick, json, libxml, mbstring, mcrypt, mhash, mime_magic, mysql, mysqli, ncurses, openssl, pcntl, pcre, PDO, pdo_mysql, posix, Reflection, session, shmop, SimpleXML, soap, sockets, SPL, standard, suhosin, sysvmsg, sysvsem, sysvshm, tokenizer, wddx, xml, xmlreader, xmlwriter, zip,
zlib
[Zend Modules] Suhosin

測試方法有兩種,在 command line 下面用 time php5 test.php 或是用 php-benchmark ,我用後者,方便寫測試程式,有興趣的人應該也可以用前者驗證,挑幾條來玩玩看:

原文 If a method can be static, declare it static. Speed improvement is by a factor of 4.
翻譯 如果一個方法可靜態化,就對它做靜態聲明。速率可提升至4倍
結論 比較快是一定的…但究竟快了幾倍,那也得看方法內是做什麼事情而定。

原文 echo is faster than print.
翻譯 echo 比 print 快
結論 的確如此

原文 Use echo’s multiple parameters instead of string concatenation.
翻譯 使用echo的多重參數(譯註:指用逗號而不是句點)代替字元串連接
結論 的確如此

原文 When echoing strings it’s faster to separate them by comma instead of dot. Note: This only works with echo, which is a function that can take several strings as arguments.
翻譯 輸出多個字元串時,用逗號代替句點來分隔字元串,速度更快。註意:只有echo能這麼做,它是一種可以把多個字元串當作參數的“函數”(譯註:PHP手冊中說echo是語言結構,不是真正的函數,故把函數加上了雙引號)
結論 的確如此

原文 It’s better to use switch statements than multi if, else if, statements.
翻譯 使用選擇分支語句(譯註:即switch case)好於使用多個if,else if語句
結論 我測的結果是如果要轉換型別,Switch 會比較慢,但兩者還是沒有顯著差異。

原文 Error suppression with @ is very slow.
翻譯 用@屏蔽錯誤消息的做法非常慢
結論 這邊有個問題,就是要顯示錯誤訊息反而花更多成本,但用 @ 屏蔽的確在邏輯上會多做一些事情

原文 Error messages are expensive
翻譯 錯誤訊息代價昂貴
結論 請對照上一條原則.. XD

原文 $row[’id’] is 7 times faster than $row[id]
翻譯 $row[‘id’]的效率是$row[id]的7倍
結論 效率會比較高,但不明顯,我覺得更重要的是 $row[’id’] 可讀性好多了!

原文 Do not use functions inside of for loop.
翻譯 盡量不要在for循環中使用函數,比如for ($x=0; $x < count($array); $x) 每循環一次都會調用count()函數 結論 非常正確

原文 Just declaring a global variable without using it in a function also slows things down (by about the same amount as incrementing a local var). PHP probably does a check to see if the global exists.
翻譯 僅定義一個局部變量而沒在函數中調用它,同樣會減慢速度(其程度相當於遞增一個局部變量)。PHP大概會檢查看是否存在全局變量
結論 在 function 內有差,在 Class 內沒有顯著差別。

原文 Method invocation appears to be independent of the number of methods defined in the class because I added 10 more methods to the test class (before and after the test method) with no change in performance.
翻譯 方法調用看來與類中定義的方法的數量無關,因為我(在測試方法之前和之後都)添加了10個方法,但性能上沒有變化
結論 差距並不顯著

原文 When working with strings and you need to check that the string is either of a certain length you’d understandably would want to use the strlen() function. This function is pretty quick since it’s operation does not perform any calculation but merely return the already known length of a string available in the zval structure (internal C struct used to store variables in PHP). However because strlen() is a function it is still somewhat slow because the function call requires several operations such as lowercase & hashtable lookup followed by the execution of said function. In some instance you can improve the speed of your code by using an isset() trick. Ex. if (strlen($foo) < 5) { echo "Foo is too short"; } vs. if (!isset($foo{5})) { echo "Foo is too short"; } Calling isset() happens to be faster then strlen() because unlike strlen(), isset() is a language construct and not a function meaning that it's execution does not require function lookups and lowercase. This means you have virtually no overhead on top of the actual code that determines the string's length. 翻譯 當操作字元串並需要檢驗其長度是否滿足某種要求時,你想當然地會使用strlen()函數。此函數執行起來相當快,因為它不做任何計算,只返回在zval 結構(C的內置數據結構,用於存儲PHP變量)中存儲的已知字元串長度。但是,由於strlen()是函數,多多少少會有些慢,因為函數調用會經過諸多步驟,如字母小寫化(譯註:指函數名小寫化,PHP不區分函數名大小寫),會跟隨被調用的函數一起執行。在某些情況下,你可以使用isset() 技巧加速執行你的程式。 舉例如下: if (strlen($foo) < 5) { echo "Foo is too short"; } 與 if (!isset($foo{5})) { echo "Foo is too short"; } 結論 是的,跑 1000 次的情況下效能差了 10% 。

如果有興趣深入探討,可以看更多 The PHP Benchmark 的測試,最後附上我寫的測試程式碼:

測試環境

marker                                     time index            ex time         perct   
---------------------------------------------------------------------------------------
Start                                      1271166102.63234900   -                0.00%
---------------------------------------------------------------------------------------
StaticMethodStart                          1271166102.63238600   0.000037         0.03%
---------------------------------------------------------------------------------------
StaticMethodStop                           1271166102.63430300   0.001917         1.46%
---------------------------------------------------------------------------------------
MethodStart                                1271166102.63431500   0.000012         0.01%
---------------------------------------------------------------------------------------
MethodStop                                 1271166102.63781100   0.003496         2.66%
---------------------------------------------------------------------------------------
PrintStart                                 1271166102.63782000   0.000009         0.01%
---------------------------------------------------------------------------------------
PrintStop                                  1271166102.65499800   0.017178        13.09%
---------------------------------------------------------------------------------------
EchoStart                                  1271166102.65501200   0.000014         0.01%
---------------------------------------------------------------------------------------
EchoStop                                   1271166102.66703400   0.012022         9.16%
---------------------------------------------------------------------------------------
Start: Echo + ,                            1271166102.66704600   0.000012         0.01%
---------------------------------------------------------------------------------------
End: Echo + ,                              1271166102.68043400   0.013388        10.20%
---------------------------------------------------------------------------------------
Start: Echo + .                            1271166102.68044700   0.000013         0.01%
---------------------------------------------------------------------------------------
End: Echo + .                              1271166102.69948600   0.019039        14.50%
---------------------------------------------------------------------------------------
Start: Switch                              1271166102.69951200   0.000026         0.02%
---------------------------------------------------------------------------------------
End: Switch                                1271166102.70379900   0.004287         3.27%
---------------------------------------------------------------------------------------
Start: if                                  1271166102.70381000   0.000011         0.01%
---------------------------------------------------------------------------------------
End: if                                    1271166102.70807200   0.004262         3.25%
---------------------------------------------------------------------------------------
suppress start                             1271166102.70808100   0.000009         0.01%
---------------------------------------------------------------------------------------
suppress end                               1271166102.71372200   0.005641         4.30%
---------------------------------------------------------------------------------------
no suppress start                          1271166102.71373400   0.000012         0.01%
---------------------------------------------------------------------------------------
no suppress end                            1271166102.72371200   0.009978         7.60%
---------------------------------------------------------------------------------------
start string convert                       1271166102.72373200   0.000020         0.02%
---------------------------------------------------------------------------------------
end string convert                         1271166102.72392700   0.000195         0.15%
---------------------------------------------------------------------------------------
start no string convert                    1271166102.72393900   0.000012         0.01%
---------------------------------------------------------------------------------------
end no string convert                      1271166102.72407900   0.000140         0.11%
---------------------------------------------------------------------------------------
start no functions inside of for loop      1271166102.72462000   0.000541         0.41%
---------------------------------------------------------------------------------------
end no functions inside of for loop        1271166102.73778500   0.013165        10.03%
---------------------------------------------------------------------------------------
start using functions inside of for loop   1271166102.73779700   0.000012         0.01%
---------------------------------------------------------------------------------------
end using functions inside of for loop     1271166102.74494600   0.007149         5.45%
---------------------------------------------------------------------------------------
start strlen                               1271166102.74495800   0.000012         0.01%
---------------------------------------------------------------------------------------
end strlen                                 1271166102.74547700   0.000519         0.40%
---------------------------------------------------------------------------------------
start isset                                1271166102.74548900   0.000012         0.01%
---------------------------------------------------------------------------------------
end isset                                  1271166102.74571200   0.000223         0.17%
---------------------------------------------------------------------------------------
start declare many methods                 1271166102.74572100   0.000009         0.01%
---------------------------------------------------------------------------------------
end  declare many methods                  1271166102.74899700   0.003276         2.50%
---------------------------------------------------------------------------------------
start declare one method                   1271166102.74900600   0.000009         0.01%
---------------------------------------------------------------------------------------
end declare one method                     1271166102.75232400   0.003318         2.53%
---------------------------------------------------------------------------------------
start define redundancy variable           1271166102.75233400   0.000010         0.01%
---------------------------------------------------------------------------------------
end define redundancy variable             1271166102.75980700   0.007473         5.69%
---------------------------------------------------------------------------------------
start no variable                          1271166102.75981600   0.000009         0.01%
---------------------------------------------------------------------------------------
end no variable                            1271166102.76346600   0.003650         2.78%
---------------------------------------------------------------------------------------
Stop                                       1271166102.76362100   0.000155         0.12%
---------------------------------------------------------------------------------------
total                                      -                     0.131272       100.00%
Posted in Programming
8 comments on “PHP 的效能測試
  1. Joe Horn says:

    “It’s better to use switch statements than multi if, else if, statements” 應該是為了 debugging 與 maintenance … 🙄

  2. steve says:

    翻譯 錯誤訊息代價昂貴結論
    請對照上一條原則.. XD ((這有誤 有去聽過PHP大師那場 才會懂他所謂的昂貴
    請看這頁 http://talks.php.net/show/ntu/25

    • roga says:

      i see, 「錯誤訊息代價昂貴」是對的,避免錯誤或警告可以節省運算時間。這邊「請對照上一條原則.. XD」的意思是是因為上一條原則指出「用@屏蔽錯誤消息的做法非常慢」,可是實際上不用 @ 會更慢,所以兩者有點自打嘴巴的嫌疑 😛

      雖然我不太清楚 @ 的實做方式 (是否因為加了 @ 而繞過 php_verror() 等 function , 或許晚點可以用 Callgrind 驗證看看).. 😀

  3. Anonymous says:

    我想環境跟設定影響應該滿大的,我自己是用 Fedora 12 + PHP 5.3.1 跑的結果大部分跟那篇文章描述的一樣… 😳

    • roga says:

      您這麼一說,我補跑了另一個測試環境,果然和您說的一樣,兩個環境有些數據是相反的,我得要研究一下是什麼造成的…

  4. saiue says:

    超級贊的
    我也對40 Tips for optimizing your php code 有很大的疑問
    看到實際抄做範例
    只能說贊

  5. cly says:

    echo語句 “不要求返回任何數值” 為最主要的差異 …
    所以運行速度要略微快於print

    PHP代碼中可以把print作為一個普通函數來使用

    $rs = print “Hello World”;
    $rs的值將為1

1 Pings/Trackbacks for "PHP 的效能測試"

Leave a Reply

Your email address will not be published.