看了 PHP 作者寫的 Simple is Hard 以及聽 DK 說明, 才終於明白為啥大家說 PHP framework 很慢, 畢竟 scripting language 寫的 framework 滿天飛, 為啥 PHP 的情況比較不同。
剛從 PHP 換到 Rails / Django 時, 不習慣設好 production server 後就無法一存檔就重讀程式。現在才知道為了這個方便的功能, 付出過高的代價。為了能夠一存檔就執行到新的程式, 有幾種簡單的作法:
- 每次都重讀程式碼並重編譯。代價是非常慢, 又要讀檔又要編譯程式。使用 opcode cache 可減輕這問題, 確保只編譯一次程式。
- 每次都要檢查檔案修改時間, 才知道是否需要重編譯程式。
第二點乍看並不嚴重, benchmark 單頁 PHP 看起來也沒問題, 但使用 framework 時卻不是這麼一回事。即使是寫個 Hello, world 的頁面, framework 仍會載入不少 script, 被載入的 script 會再載入其它 script, 一大串相依性造成的結果, 就是載入一堆檔案。若檔案只會載入一次, 似乎也不是大問題, 但別忘了需要檢查檔案最後修改時間, 結果就是讀個簡單頁面也會有大量的 system call, 而 system call 是很昂貴的。
相關佐證可以參見 Simple is Hard, 難怪投影片裡不斷強調相依性的結構圖還有執行 stat 相關函式的次數。讓我想到 Joel 寫的 《抽象滲漏法則》, 在框架之上, 很難查覺底層的問題。前陣子也才因 NFS server 負擔過重, 讓我以為是我的 vim 出錯, 開關檔變得超慢的。
其它可能的原因還有 PHP 的 OO 設計不良, 或是一些早期設計留下的包袱, 常用 PHP 的人應該比較清楚, 我沒仔細研究。
其它可能的原因還有 PHP 的 OO 設計不良, 或是一些早期設計留下的包袱, 常用 PHP 的人應該比較清楚, 我沒仔細研究。
回一下這篇,現在的 Zend Framework 會把很多事情換成 lazy init 的方式處理,1.11 與 1.5 的速度其實差很多了。(三到四倍的速度差異!)
回覆刪除多謝更新資訊!
回覆刪除Django 也是一堆 lazy initialization/evaluation, 雖說可避免做小事時多做工, 但也提高 trace code 的工。不太喜歡這種寫法, 也可能是不習慣這種風格吧。