2013年7月25日 星期四

Facebook 的 hiphop-php (aka hhvm)

什麼是 hiphop-php 呢?


  1. 基本上就是 facebook 自己實作的 php runtime,再加上很多特異功能。
  2. 第一代叫做 HPHPc,這是一個將 php transform 成 c++ 的版本,由於因為對開發者不友善,加上每次只要 php code 有變動,就得整個重新編譯,而編譯一次得花上好幾個鐘頭,所以 facebook 在 2013/02 放棄使用這個版本。(第一代其實還包括 HPHPi,HPHPd 等衍生的特殊版本)
  3. 第二代叫做 HHVM,將原本的編譯改成 vm 模式,加上 JIT 支援,讓 php 執行起來就跟在飛一樣快。
  4. wiki

那 hhvm 有什麼特異功能呢?

  1. 基本上他就是一個 base on libevent 的 http server,加上 php runtime。
  2. 但是你想得到的一些基本 http server 該有的他都有,例如 virtual host, gzip, keepalive, rewrite, acl 等等。
  3. 此外,還可以扮演 proxy/rpc/static 。
  4. 再來它可以開一個 admin server,bind 在特定的 port 上,提供一些 server status,以及一些管理上的相關 api。
  5. 非常快,在我的 i7-3770k + 32G ram 的機器上,跑一隻簡單的 php,出現了接近 10000 reqs/sec 的數據,當然這會跟著你的 php 程式複雜程度而減少。
  6. 容易 deploy,如果 hhvm 是 static link 的話,deploy 只需要將 binary scp 到所有機器上就完成了,根本不需要管相關的 dependencies library。hhvm 在跑起來的時候,其實需要額外的 php data file,但是他用了一個很變態的方法存放這些 php data file,把它藏在 hhvm binary file 的 .text section 裡面。
  7. fb 自己實作了不少特殊功能的 function,例如用 non-blocking 的方式連接 mysql。
  8. 還很多,我目前還在看當中。


那目前有什麼做不到的?

  1. 由於還在緊密開發,所以他官方宣稱只支援到 php 5.3,並且不是所有的 php extension,都可以在 hhvm 裡面使用。
  2. 例如資料庫的部分,目前僅支援 mysql/sql,有些人想要 mongodb driver,目前還在殷切盼望中。
  3. 文件缺乏,雖然 source code 是你最要好的朋友,但是不是所有人都能看 c/c++ 去瞭解整個系統的。
  4. i386/powerpc/sparc/ia64/arm 等平台都不支援,目前只支援 amd64(x86_64),arm 的話,據說不支援啦,但是某些地方有看到這個 keyword,這讓我想到之前聽到的 facebook 考慮用 arm 來當 web 的八卦。
理論上只要你用 mysql 當 database,沒用奇怪的 extension,那麼 hhvm 應該是可以直接拿來用,連 nginx/apache 等 web server 都可以不用安裝了。但是如果有用到額外的 php extension,那要就是等別人寫出對應的 hhvm extension,不然就不建議使用。(當然也可以自己下去寫看看)

2013年7月7日 星期日

我也來點勵志的好了.

很多的同事,一起工作久了,就會問‘你哪個大學畢業的?研究所呢?’,其實,我根本沒讀大學呀,最高學歷只有高中,還讀了三家,從省立高職日校,到省立高職夜校,最後只好認命地在私立高職的補校讀了三年,再多考一次資格考,才把高中同等學歷補齊。現在大家一直在講証照,一直講技職體系出了問題,我的經歷應該可以給技職體系的學生們一些激勵吧。那憑什麼給大家激勵?高職畢業,一張證照也沒有,在外商工作過,每天準時上下班,日子也過得舒服,這些應該當理由就夠了吧。

為什麼只讀高職,為什麼不讀高中,然後大學一路讀上去?這大概得從國中時代講起,國中的時候,幾乎都沒在認真讀書,大概上輩子有燒好香,所幸成績都還不錯。我們班算是前段班,同學們幾乎都是腦袋聰明,很會玩,就是不願意認真讀書。因為這原因,所以國三的時候,我就在想,是一定要讀高中嗎?不能走不一樣的路嗎?因此決定不參加高中聯考,只參加高職及五專聯考。最後上了省立永靖高工製圖科,讀了兩三個月就沒去上課,自動退學。隔年因為朋友的原因,報名了省立北斗家商的夜校廣告科,一樣讀了兩三個月自動退學。最後是某朋友勸我,就算怎樣不喜歡讀書,起碼得把高中同等學歷補齊,以後才不會後悔。因為這個理由,所以報名了彰化的私立正德工商夜校的資訊科 [1]。為什麼會是資訊科?班上男生的說法都一樣,大家都以為資訊科女生多,結果女生就被蜂擁而至的男生們排擠掉了,反而資料處理科的女生才多。:(

資訊科並沒有教太多資訊相關的東西,真的要講的話 ,隱約記得就是大易輸入法跟 8051 組合語言。輸入法對於寫程式來講,沒有太大意義,學校教輸入法,我猜大概是要大家多一樣才藝,然後比賽誰速度快。8051 組合語言,教的時候大部份的人都聽不懂,考試的時候好像也沒考,真的要實做,只要有一兩個會寫,互相 cover 就通過測驗了。

講完了學歷,再來講電腦相關的經歷。最開始接觸電腦,是在國中畢業之後,白天讀永靖高工,晚上我自己跑去一家電腦公司當工讀生,在那邊認識了電腦上的啓蒙師傅。也因為他,所以開始學著架設 dial-up BBS [2]。當時的作業系統都是 MS-DOS,沒有多工, 一台 pc 同時間只能讓一個用戶撥接上來用。那時對於所謂的作業系統,我一點概念都沒有,頂多就是玩耍著 QEMM,讓可用記憶體增加一點,玩耍著 pctools,修改遊戲存檔,讓玩遊戲練功的時間縮短,不過也因此學習到什麼是 16 進位。

當作業系統是 MS-DOS 的電腦,進入了 BBS 模式,就是等著讓別人撥接進來用,自己只能在旁邊乾瞪眼,因此問了師傅,有沒有什麼方法,可以讓很多人同時使用一台電腦?他說有種系統叫做 unix,但是他並沒真的用過,也不熟。而他自己架設的 BBS 上,是透過一個叫 desqview/X [3] 的程式,讓一台電腦,做到簡單的多工,來達到架設多線 dial-up BBS 的目的。

用 desqview/X 沒多久,OS 世代進入了 windows95,據說不需要 desqview/X 這類的程式,也可以做到多工。我突然想起 unix 這個 keyword,很想知道那到底是什麼樣的系統。當時台灣有些先進在推廣 linux,因此買了書,使用光碟安裝起自己的第一個 linux 系統,那時比較有名也比較友善的,應該是 Slackware 吧,RedHat 還沒出現在市場上。

由於對於 linux 系統不熟,所以遇到了一些小問題的解決辦法,就是重新安裝整個系統。也因為這樣的反覆安裝,開始對於整個環境比較熟悉。既然系統比較熟悉一點了,那就學一下寫程式吧!PHP 的前身 php/fi 剛出現,因為語法跟別的語言比較起來相對簡單,就花了一些時間好好地學了一下 php/fi,然後忘了什麼原因,同時間也學著寫 perl

linux 用了一年多,多數時間都在摸索,剛好交大的 jdli 在推廣 FreeBSD,想說反正都一樣是 unix like 的系統,多學一點也無所謂,就順勢換到了 FreeBSD,沒想到一用成主顧,自此在自己的機器上,就很少安裝 linux 了(測試或者案例需要不算)。

FreeBSD 當時的社群,非常壯大,每個人都可以直接參與,可以送自己想改的東西回去。由於年紀還輕,熱血過剩,所以想了很多想做的東西,例如 FreeBSD 在中文使用上的修正什麼的,但是 submit 幾次 patch 都沒有人理會,就自作主張的用英文寫了封信,給 jkh@ [4],問他有沒有方法,可以讓中文相關的事情有人來關注?他就問我有沒有興趣接下這個任務?我回答說‘可是我並不是那麼懂 c 呀’,他回我說‘沒問題的’,因此,在 1997 年的 11 月,正式變成了 FreeBSD.org 的一員。[5]

成為 FreeBSD 的成員之後,除了持續在 BBS 上的 tw.bbs.386bsd 回答問題之外,偶爾也會寫一些跟中文相關的文章。有討論自然少不了吵架,也因為吵架,催生了 FreeBSD FAQ 繁體中文版。當時號召了好幾個 bsd 板上認識的網友,大家在空閒時間裡,將 FreeBSD FAQ 翻譯成繁體中文。因為我是 committer,也就順理成章的將這些成果變成官方的 FAQ 繁體中文版,並且直接在 FreeBSD 官方網站上就可以看到。[6]

1999 年當兵,2000 年底退伍,退伍之後工作。這時,社群的一些人,開始改用 IRC 作為聊天以及交換資訊的途徑。最早大家用 BitchX 這個 client,同時有另外一個叫做 irssi 的 client 好像也很不錯。經過測試之後,發現 irssi 無法正確的刪除中文字。某天晚上,熬夜研究了一下這部分的 code,並參考了 clive@ 當時對於 bitchx 的一些修正,終於將這問題解決。在 irc 讓大家測試沒問題之後,寫信通知 irssi 的作者,他馬上就發佈了一個包含這個修正的版本。[7]


[未完, 下次再講工作做過哪些好玩的事情吧!]

Ref:
  1. 正德工商已經改名為正德高中,是一所綜合高中,據說升學率不算低,算是彰化不錯的私立學校了。不過真實狀況到底是怎樣,我並不清楚。
  2. 不是 ptt 那種 internet bbs,而是透過 modem 撥接的 bbs,當時比較代表性的,有superbbs/ra/rbbs/powerboard 等。
  3. 後來才知道 X 代表的就是 XWindow,在 DOS 上實作出 X server 真的是很厲害。
  4. FreeBSD 的 founder,後來去蘋果當 unix 部門的主管,最近轉職到 iXSystem 當 CTO。
  5. 那時候好像還在正德讀第三年。
  6. 後來 FAQ 格式改版過很多次,由於大家工作的工作,上大學的上大學,加上我個人當兵的原因,沒有餘力跟上最新的變化。
  7. irssi 後續的中文修正,是由 mhsin 接手,他同時也是修正 windows 上常用的終端程式 putty 中文相關問題的長輩。

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)


2013年5月10日 星期五

go and elf rpath

某社後來都強制得對 binary 加上 rpath, 為了系統安全的原因.

那怎樣在 go 裡面檢查 binary file 的 rpath ?

用 debug/elf 呀.

--
package main

import (
        "fmt"
        "debug/elf"
)

func main() {
        f, err := elf.Open("/bin/ls")
        if err != nil {
                panic(err)
        }

        rpath, err := f.DynString(elf.DT_RPATH);
        fmt.Printf("%s\n", rpath)
}

2013年1月29日 星期二

pecl-solr

pecl-solr 是一個 php 的 extension, 用 curl & libxml2 處理從 solr 吐回來的資料,
資料的格式可能是 xml, json 或是 serialize 過的 php array string.


這幾天在看 freebsd 的 pr, 發現寫 pecl-solr 的這群人非常的天才,
即使他們全部都有 @php.net 的帳號.


首先, 用 curl 去 solr 拉 search result, 如果格式是 xml 的話,
再用 libxml2 處理 search result, 轉成 php array.
(以上都是在 extension 裡面呼叫, 所以都是用到 c 的 function)


但是 build 出來的 solr.so 並沒有 link 到 libcurl & libxml2.
往上追到 Makefile, 發現根本沒有 "-lcurl -lxml2" 等 keyword,

Makefile 是透過 configure 產出的,
那是不是 configure 的時候出了什麼問題?

也不是, config.log 怎樣看都是對的,
有抓到 curl & libxml2 的 library & header file, 判斷都是對的.

configure 是透過 phpize 讀取 config.m4 產生的.
那 config.m4 有沒有問題?

config.m4 在 curl 判斷上, 將 -lcurl 的定義放進了 CURL_SHARED_LIBADD

最後會用 PHP_SUBST 來把要 link 的 library 放進 Makefile 裡面,
但是他的 config.m4 裡面是這樣的.

PHP_SUBST(SOAP_SHARED_LIBADD)

你明明是 solr extension, 為什麼要用 SOAP 這名字?
並且為什麼上面寫的是 CURL_SHARED_LIBADD,

第一, 名字寫錯了, 第二, 就算寫錯, 兩個寫的一樣也是兜的起來,

這樣怎麼可能會去 link 到對的 library?


然後又發現 pecl-solr 有一些 unit test, 跑了一下,
錯誤訊息裡面說我沒有裝 php curl extension.

code 怎樣看都是直接 call libcurl 的 c function, 為什麼要我裝 php curl extension?

再繼續看 php_solr.c 發現他在裡面 require 了 php curl & libxml2 extension.


真相大白, 為什麼在 runtime 的時候都不會有問題?

因為很白痴的要求一定要裝 php curl & libxml2, 所以不會發生 undefined symbol 的問題,

但是這不是脫褲子放屁嗎?


php-curl 大家用得比較多, 但是 php-libxml2 在我所知道的人裡面, 很少人直接 call 這個,
pecl-solr 只是直接 call c function, 為什麼要 require php-libxml2 extension?

2013年1月9日 星期三

工程師想要什麼?

我們組最近有個需求, 想要監控 mysql 各個 table 的 row size,

直接讓某台 cron 機器, 開 mysql connection 到各台 mysql 讀 INFORMATION_SCHEMA 是最直覺的,

也有同事說, 讓 mysql server 自己讀自己的 INFORMATION_SCHEMA,
讓別人透過 http 來讀 json 就好, 但是這樣可能就得跑一下 apache 之類的.

後來我研究了一下, 公司內部本來就有一個 package, 可以透過 http 提供 mysql 各項的 metrics,

原本想要稍微修改這個 package, 讓我們想要提供的資料可以套用進去,

更深入研究之後, 發現這個 package 的 desc 寫著 mysql status framework,

Framework 唷, 真厲害, 而他真的就是一套用 perl 寫的簡單 framework,

用了 AnyEvent::HTTPD 做前端, 用了內部的 dbm 當 storage,
只要繼承他的 perl module, 寫一些 callback function,
就可以把怎樣搜集資料, 以及 http handler 都搞定,

立馬就寫了一下, 大概不到一百行就搞定我們要的功能,
也不會有 performance impact,

因為往 mysql 的 query 都不是即時的,
是透過 collector 每多久查一下, 寫到 storage,
前端的 AnyEvent::HTTPD 收到 request, 往 storage 查, 馬上就吐出去.


簡單, 好用, 又讓我覺得很爽.

工程師想要的是什麼? 就是這種偶然遇到的小 hack 得到的喜悅呀.