40 個 PHP 效能調校的技巧

最近在玩 Smarty & XAJAX
無意間看到了 Reinhold Weber 寫了一些提昇 PHP 效能的討論

網路上雖有中國簡體字版本,但由於文化隔閡過大,翻譯無法達到 “信、達、雅” 的目標
於是又根據原文將它翻譯成台灣正體中文,並加上自己的註解。

  1. 盡量以靜態宣告方法,這將比動態宣告快 4 倍。
  2. echo 比 print 快。(就顯示字串而言)
  3. echo 的參數可用 “,” (逗號) 銜接,這比用 “.” (句號) 串接來得快。
  4. 在 for 迴圈之前定好最大值,而不要在迴圈內計算。
  5. 使用 unset 來釋放記憶體,特別是針對龐大陣列。
  6. 避免使用 __get, __set, __autoload 等物件導向的 magic method。
  7. require_once() 的代價非常高。
  8. 使用 include 與 require 時採用完整路徑,可以節省作業系統解析路徑的時間。
  9. 要查詢腳本何時執行,$_SERVER[’REQUEST_TIME’] 比 time() 來得好。
  10. 以 strncasecmp, strpbrk and stripos 代替正規表示式。
  11. str_replace 比 preg_replace (PCRE 的 replace) 快;strtr 則比 str_replace 又快了 4 倍。
  12. 在某些函數可以接受陣列或單一字元為引數 (譬如:字串替換函數)。
    若您的引數並非很長,可考慮撰寫多個替換敘述;並建議一次傳入一個字元,而不要一口氣傳入整個陣列。
  13. 處理流程控制時,使用 switch-case 比用多個 if-elseif 好。
  14. 使用 “@” 抑制錯誤訊息會非常慢。
  15. 開啟 apache 的 mod_deflate 支援。
  16. 當不使用資料庫時,記得關閉連結。
  17. 陣列中引數加上單引號將比不加快了 7 倍,如 $row[’id’] 與 $row[id]。
  18. 顯示錯誤訊息的代價很高。
  19. 別再迴圈中使用函數,它在每一圈都會被呼叫。如 for ($x=0; $x < count($array); $x) 的 count()。
  20. 於方法中遞增本地變數是最快的,幾乎與函數中呼叫本地變數一樣快。
  21. 遞增全域變數比遞增本地變數曼 2 倍。
  22. 遞增一個物件的屬性 (如 $this->prop++) 比本地變數慢 3 倍。
  23. 遞增一個未定義的本地變數比預先賦值的還慢 9-10 倍。
  24. 宣告了全域變數而沒在函數中使用它,仍然會降低執行速度。因為 PHP 仍會檢查函數內是否存在全域變數。
  25. 方法調用 (method invocation) 與類別中定義的方法數量無關,作者於方法中增加了 10 個方法,前後之效能並無變化。
  26. 衍生類別中的方法比基礎類別中的方法來得快。
  27. 呼叫含有一個參數的空函數所費時間,相當於遞增本地函數的 7-8 倍;
    同樣的作法,對於類別的方法將要 15 倍。
  28. 若字串內無變數需代換,可用單引號代替雙引號。這麼作此將省去變數掃描的時間。
    (如 ‘just a string’ 而非 “just a string”)
  29. 當使用 echo,以逗號代替句號分隔字串。類似第三條所述,但此處將 echo 當成 statement 而非 function。
  30. Apache 對 PHP 的解析比 HTML 慢了 2-10 倍,所以盡可能地使用靜態網頁。
  31. 若無快取機制,PHP 腳本每次都會編譯一次。安裝快取相關機制將省去 25-100% 重新編譯的時間。
  32. 盡可能地使用快取機制。比方說採用 memcached,他是一個高效能的記憶體物件快取系統,藉由降低資料庫負載來加速動態網頁應用。
    OP code 快取也很有用,它可以避免每次重複編譯腳本。
  33. 當處理字串時,檢查字串的長度將會用到 strlen() 函數。此函數幾乎不需任何運算,它把資料結構中已知的字串長度直接傳回,故速度很快。
    (PHP 內部的 zval 資料結構,採用 C 語言 struct 來儲存變數)
    然而由於 strlen() 是函數,函數呼叫仍需耗費相當的運算時間;某些時候可利用 isset() 來改善執行速度。

    例如:
    if (strlen($foo) < 5) { echo "Foo is too short"; }

    if (!isset($foo{5})) { echo "Foo is too short"; }

    呼叫 isset() 比 strlen() 快是因為,isset() 是語言架構而非函數,故無須函數查詢與大小寫轉換。
    這表示決定字串長度幾乎不需要額外成本。

  34. 當遞增或遞減時,後置運算會比前置運算稍慢一點。 ($i++ 比 ++$i 慢)。
    這是 PHP 的特性,故別急著去更改您的 C 或 Java 程式碼,這不會讓它們加速。
    由於後置遞增/遞減會先建立一個暫存變數而後遞增,前置遞增/遞減則直接對原值操作。
    故 ++$i 僅使用 3 個 opcode, 而 $i++ 使用 4 個。
    Zend 的 PHP optimizer 也採用此技巧作最佳化。

    請記得必非所有 optimizer 都會對 opcode 作最佳化,且多數 ISP 並不會對 opcode 作最佳化。

  35. 並非所有事情都要訴諸 OOP,方法與物件的成本很高,消耗大量的記憶體。
  36. 陣列很好用,它讓您不需自行實做每個資料結構。
  37. 不要過度的拆開方法 (method),想清楚哪些程式碼是會被重新使用的。
  38. 當需要時,可以把程式碼拆成方法。
  39. 充分利用 PHP 提供的眾多函數。
  40. 若您的程式碼有很耗時間的功能,盡量以 C 語言撰寫延伸套件 (extension)。
  41. 對您的程式碼進行側寫 (profile),側寫器將告訴您程式碼執行的時間。Xdebug 除錯器即含有側寫器,將告訴您整體表現的瓶頸在哪裡。
  42. Apache 提供的 mod_gzip 能即時壓縮資料,它省去 80% 的傳輸資料量。
  43. 讀讀看 John Lim 所撰寫有關 PHP 最佳化的文章

Leave a Reply

Your email address will not be published.