2009年2月20日 星期五

PHP 效能調校:最佳化你的php程式碼的訣竅

最近工作內容主要就是PHP的效能調校(performance tuning),除了看《php cookbook》,《programming php》裡都有一些相關的主題外,也上網找了一些文章和工具,其中不乏精譬的討論串,但其中整理的很好的個人覺得應屬以下這一篇(http://reinholdweber.com/?p=3),個人也稍稍地加上一點心得補充,所以應該一定會超過原文的四十點。

工具上可以善用xdebug和PEAR::Benchmark、APD等工具來量測效能是否真的有被調到。如果要做壓力測試可以用ab(Apache HTTP server benchmarking tool)和http_load或JMeter。另外,好的apache server組態設定和DB schema和index設計也很重要,總之,要調出好的performance除了程式撰寫上的風格和技巧外,有更多其它週邊相關的技術需求,也需要做不少的實驗來量測數據,一個系統需要調校的是整體,而不是部份的程式碼就可以達到全面性的成果,至於要調到什麼地步,套句老子的話:「知足不辱,知止不怠」,如此而已。

好在我工作上,公司中有一堆大俠相助(Frank, Winson, Chary, hermit, James 先謝囉!),還可以渡過這回的小小難關,希望我淺薄的實戰經驗和小小心得也可以幫到別人。

最佳化你的php程式碼的訣竅
* 如果method可以是static,那就宣告它為static。速度的改善可以是4倍。
* echo 比 print 快多了。
* 使用echo的多個參數用法,不要用string的串接(jiing: 也就是用逗號,不要用句號)
* 在迴圈之前設定你的for迴圈的極大值,不要在迴圈中
* Unset你的變數來釋放記憶體,尤其是大型陣列
* 避免使用magic 函式,像是 __get, __set, __autoload
* require_once()非常昂貴(jiing:能用require就用require,儘可能不要重覆require_once或include_once)
* 在includes和requires中使用完整的路徑,會花費較少的時間在解析OS路徑。
* 當script已經開始時,如果如果你需要找出時間(time),$_SERVER['REQUEST_TIME'] 要比time()好的多。
* 看看你是否能用 strncasecmp, strpbrk 和 stripos 來取代regex(jiing:如果可以用字串相關函數來取代正規表示式,那麼就試試看,有時候多個幾行反而比較快)作法:搜尋所有的(用find指令) preg_exp , preg_match相關指令,並研究如何用str相關的函式來取代
* str_replace比 preg_replace快,不過strtr又比str_replace快四倍
* 如果函式(例如字串取代函式),同時接受array和單一字元作為引數,且你的引數串接不是太長,考慮撰寫一些多餘的置換陳述式,一次傳遞一個字元,不要寫一行程式碼來作為搜尋和取代引數、接受陣列。(jiing:意思是寧可多寫幾行來做這件事)
* 用switch陳述式會比多個if else if 好的多。
* switch中,常跑的case放前面一點也會gain到一點效能。
* 用@來做錯誤抑制非常慢
* 打開apache的 mod_deflate
* 當你完成資料庫相關操作時,關閉你的資料庫連結
* $row['id']要比$row[id] 要快上七倍
* 錯誤訊息非常昂貴
* 在for迴圈中不要使用函式,例如:for ($x=0; $x < count($array); $x)
* count()函式會每次都被呼叫。
* 一般而言for會比foreach快,但當迴圈裡的元素(element)大時,有時foreach會稍快一些(個人的心得是要作實驗)
* 在method中遞增local變數是最快的。幾乎和呼叫函式中的一個local變數一樣快。
* 遞增一個global變數比local變數慢二倍。
* 遞增一個object property(e.g., $this->prop++)會比local 變數慢上三倍。
* 遞增一個未定義的local變數會比一個預先初始化的變數慢上9-10倍
* 只宣告一個global變數,而沒有在函式中使用它也會讓事情變慢(大約與遞增local變數的時間一樣)。php可能會去做檢查來看看全域(global)變數是否存在。
* 方法呼叫似乎與class內所定義的方法數目不相干,因為我新增十多個方法到test class(在test method之前和之後),在效能上並沒有變化。
* 在子class的method執行的比在母class定義的更快。
* 具有一個參數和一個空函式主體的函式呼叫花費大約是7-8個$localvar++運算一樣多的時間。
* 相似的方法呼叫當然約是15個$localvar++運算一樣多的時間。
* 以單引號括住字串會比以雙引號(")括住字串快一丁點,因為php會對"..."查找變數,而不會對'...'。(jiing:少寫含糊不清的雙引號(""),是字串就用單引號)當然囉,只當你在字串中沒有變數時,才可以這麼做。
* 儘量減少型別轉換和判斷
* 當使用echo來列印字串時,使用逗號來分隔字串會比用句號(.)快一點。
* 注意:這只要對echo有用,echo是個函式,它可以採取數個字串作為引數(arguments)。
* 使用php 快取的產品。(jiing:如 xcache, php_apc,也可以考慮架設squid等proxy server,, db能開多大的cache就開上去)
* 快取多多益善。 用 memcached
* 可以用isset()來取代stelne($str)==0
*

Ex.
if (strlen($foo) < 5) { echo "Foo is too short"; }
vs.
if (!isset($foo{5})) { echo "Foo is too short"; }

* 在php裡++$i,比$i++快一點
* 不需要每件事都OOP,通常它是過度負載(overhead),每個method和物件呼叫花費很多的記憶體。(jiing:效能和可維護性、可擴充性的取捨真難啊.......orz)
* 不要實作每個資料結構成class,array也很有用
* 不要過度切分method,思考,哪些程式碼是你真的將會重覆使用的
* 當有需要時,你總是可以稍後再切分一個method的程式碼
* 善用數不盡的內定(predefined)函式(jiing:也有例外,hash('md5','optimize this')比md5('optimize this')快)
* 如果有一堆字串結合後不斷輸出的動作,可以考慮使用ob_start()相關的函式
* 如果你有非花費時間的函式的程式碼裡,那麼考慮寫C的extension。(jiing:PECL裡有相關文件,敝公司中據說有V Ving大神留下來的程式碼可以參考,目前祈禱不要用到這個大絕招)

摘自:http://blog.sina.com.tw/jiing/article.php?pbgid=872&entryid=578626

沒有留言:

wibiya widget