2013年6月18日 星期二

perl performance tuning

不知道是不是因為 aws 實在太好用,還是用的機器數量, 決定服務在大老闆心中的份量, 因此很多人覺得, performance 是不需要稍微調教的, 只要加機器, 就可以得到更多的效能, 或者會拿出大神高德納講的那句話出來當證明,

"過早的最佳化, 是萬惡的根源"

但是在我看來, 很多人並不是晚點才做最佳化, 而是根本不做最佳化.


最近花了點時間, 看了社內某個用 mod_perl 寫出來的產品.在 4g ram 的 vmware 裡面, 只跑得出 9reqs/sec 的數據, 問問原本維護的人, 他們說這很合理呀, 他們要求即使是測試環境, 最少也給 16g ram, 才有可能跑得起來, 4g 他們覺得連跑都跑不動.


花了點時間用 Devel::NYTProf 做一下 profiling, 發現每一次的 request, db access 都在最後面, 大概只佔該次 request 的 1/5 ~ 1/4 左右而已, 而前面看起來很忙, 但是都不知道在忙什麼.


仔細研究 code 之後, 發現了下面幾個問題:


  1. 每次 request 會先檢查 session 是否 valid, 但是檢查的 function 連續被 call 了兩次.
  2. 檢查 session 是否 valid 的 function, 檢查的瞬間, 會從資料庫撈出某資料, 這資料會先用 fork 某程式來取得更仔細的資料, 而這個會 fork 的 function, 從開始到結束會被 call 四次.
2. 的 code 如下.

my $encode = encode_base64($plain_text);
my $r = `perl decode_base64.pl "$encode" | xxxxx`;


$plain_text 是從資料庫出來的資料, 先用 encode_base64 編碼, 再帶進去 fork perl 出來的 decode_base64.pl 解成 plain_text, pipe 給 xxxxx.

decode_base64.pl 是一隻 perl script, 只做一件事情, 從 @ARGV 收參數, 然後 decode_base64 之後, print 到 stdout.


這, 是喝到多醉, 才會寫出這種 code? 因為它其實可以簡化成

my $r = `echo "$plain_text" | xxxxx`;

少 fork perl 這種大傢伙, 會減少很多時間的, 另外, 這個 function 會 fork 幾次? 答案應該是 24 次, (shell + perl + xxxxx) * 4 * 2 = 24, 每一個 request 先 fork 24 隻程式出來, 其中包括八次是 perl, 是能得到多少效能?

那, 可以完全避免掉 fork 嗎? 答案是可以的, xxxxx 這是社內其他人寫的東西, 機器上看起來只有 binary, 那拿的到 source code 嗎? 沒有理由不行呀, 拿到 source code 之後, 改寫成 perl XS, 理論上就可以完全避免掉 fork 了.


將 1. 2. 的部分避免掉之後, 再做一次 benchmark, 得到了 45reqs/sec 的數據, 這已經是 500% 的 improve, 而這只是最基本的避免 fork 而已, 是不是還有其它招式可以加速? 當然有, 只是要見招拆招.



附註:

  1. http://cdn.oreillystatic.com/en/assets/1/event/45/Understanding and Optimizing your Code with Devel__NYTProf Presentation.pdf
  2. 這是 perl DBI 作者的簡報, 他同時也是 Devel::NYTProf 的作者.
  3. 簡報裡面提到不少 tuning 的概念, 都非常的棒.
  4. 最後已經在計較 subroutine 傳參數進去, 是否要 unpack 的效率了.
  5. 有的時候, 如果可以找到相同功能的 perl module, 但是是用 xs 改寫過的, 測試沒問題就儘量用, 速度上差很多 (前提是該 module 沒 bug)


沒有留言: