2014年9月25日 星期四

HHVM 及 Hack 的相容性

最近筆者開始嘗試 HHVM 應用於 Production 環境,一些心得如下:


1. Linux

現有的 Linux Distribution 幾乎都有 pre-built 套件,可直接安裝,就算要自行編譯也沒有太大的問題。 Facebook 的 HHVM Team 主要都還是 Focus 在 Linux Server 上的相容性。在 Linux 平台上編譯,可參考官方的 WIKI 文件


2. Mac OS X

Mac OS X 上 HHVM 的相容性比以往提昇許多,過去要自行 patch 許多套件編譯,現在只需要 patch jemalloc 即可。且幾乎所有相依套件皆可用 Homebrew 或 Macports 安裝。

目前 HHVM 的版本 HHVM 3.2 確定可於 Mac OS X 上編譯,只不過需透過 GCC 4.8 而非 Mac OS X 內建的 clang,相依的 Boost 套件也需由 GCC 4.8 編譯才行。

至於 master branch 目前 third-party 的 folly package 由於使用了 Linux 上的 Futex,因此目前唯一可在 Mac OS X 上編譯的版本就只剩 3.2。 不過也有人開始了 Mac OS X 平台上的編譯修正,可參見 @mcuelenaere 的 HHVM osx-fixes 分支,以及 @bsampathfolly 修正

至於 3.2 版本 Mac OS X 上的編譯步驟,可參考此處


3. Hack Programming Language

至於 Facebook 的 Hack programming language 幾乎與 PHP 相容,原本的 PHP source code 只需要修改標頭,將 <?php 改為 <?hh 即可當做 Hack 執行,因此由 PHP 換用到 Hack 幾乎是無痛轉移。

至於使用 Hack 有何好處?一來是提供型別檢查,方便開發時及早發現問題,二來依照型別資訊可大幅提昇 VM 效能,三來可以提供準確的自動補齊並與編輯器或 IDE 做整合。

Hack 的剖析工具是用 OCaml 寫的,可直接和 IDE 整合,如 Vim plugin 有 vim-hack (https://github.com/hhvm/vim-hack) 可使用。Emacs 則有 hack mode 可用 (https://github.com/facebook/hhvm/tree/master/hphp/hack/editor-plugins/emacs)。

待續


2014年8月27日 星期三

談 Git 的 Graft Points 及 git-replace

git merge --squash [branch] 可以將分支的修改全數合併至同一個 commit 但不會保留合併資訊,不過若是該分支持續開發,勢必會再次合併至主幹,這時如果重新使用 git merge --squash ,則已經合併過的 commit 也會因為重新被合併而造成衝突。

這時,要讓 squashed commit 同時也有 merged commit 的性質,可以利用 .git/info/grafts 這個檔案或利用 git-replace 來手動定義合併資訊製作造假的合併資訊!

好用的地方在哪個? 假設你有兩個 perforce branch,分支之間不斷大量修改合併,但使用 git-p4 clone 後,你希望這兩個 branch 的合併基底 (merge base) 重新計算,減少合併衝突,就可以利用這個 graft point 來定義合併節點。

甚至是 git-svn 在處理樹狀目錄的合併資訊,也可以利用 graft points 來定義合併節點減少合併衝突。

當儲存倉越來越大時,你也可以將某個 commit 之前的 history 全數壓縮到另外一個儲存倉並把古老的 commit history 全數壓起來 (squash) 來節省空間...要查看細節時,再用 git-replace 將 commit history 接回來即可。

但 .git/info/grafts 只存在 local 端,並不會被 push 出去,因此若要能修改整個 commit tree 可利用 git replace --graft […] 直接將 commit 換掉 parent。

參見:

git-replace: http://git-scm.com/docs/git-replace.html

Graft Point: https://git.wiki.kernel.org/index.php/GraftPoint

#ThePowerfulGit

2014年7月4日 星期五

CLIFramework - Command-line application framework for PHP

還在苦惱用 PHP 寫 Command-line application 要支援 zsh completion 得自己寫困難的 zsh completion 嗎?

CLIFramework 提供一整套框架讓你用 PHP 快速架構一支 command-line application,還可自動產生 zsh completion script 讓你 Key in 無負擔 XD

所有的 option completion, argument completion 皆可以用純 PHP 撰寫

請看圖 XD



專案網址: https://github.com/c9s/CLIFramework

2014年6月26日 星期四

我為何仍繼續使用 Macports 而不用 Homebrew

很多人因為 Macports 在裝一些套件時,無法避免的另外編譯另外一套 Perl 裝至系統而跳槽使用 Homebrew。但身為一個前 Perl Developer,直接使用 System Perl as a dependency 其實並不太安全。

其一:  Mac OS 本身在升級時,會自動將 Perl 版本升級,如果你 link 至舊版的 Perl,升級之後你的 Homebrew 套件所有相依 System Perl 幾乎都要重新編譯才行。

但用 Macports 裝,則是將套件完全獨立一個空間出來,把套件跟 System library 的接觸面減到最小,就算是升級 Mac OS ,只要不是 32 位元轉 64 位元,原本編譯的套件幾乎都可以繼續使用。

其二:  Macports 現在提供 pre-built 好的套件安裝檔,安裝 package 幾乎不再需要重新編譯,Installer 自動下載解開就裝完了,完全不浪費 CPU。但目前 Homebrew 裝套件應該都是要重新編譯的。

其三:  安裝或升級套件,Macports 會自動幫你掃描是不是有 Broken library,若有問題也會自動重裝 library。(目前看起來 Homebrew 沒這個功能 ?)

其四: Homebrew 雖然 Formula 眾多,但安裝非官方的 Formula 有時容易引起套件衝突,舉例來說 PHP 編譯有各種 configure options,各家使用的 default configure options 又不一樣,安裝 extension 的時候還會彼此影響到... 有些 Formula 還必須先查看是否有 enable 某功能,不然裝了也是白裝。

其五: Homebrew 編譯套件不像 Macports 提供客製化 variants,你用某個 Formula 裝就是固定只能裝他預先指定好的 features,要客製化只能自己複製出來改,自己複製出來改就累了,你還得自己 maintain forumla 才行。 (更新: 據說 Homebrew 後來也支援了 option,不過升級套件要自己記住當初裝的 option 才行)


2014年6月14日 星期六

一些關於 API 設計的碎碎念

平常可能寫 API 寫久了沒什麼感覺,但最近對於 API 設計感想特別多...碎碎念罷了。

1. 各種參數的 naming 如果沒有 consistent,在不同的 context 下會很容易造成混淆,譬如說同一個參數名稱在 A context 是某種 Type,B context 卻是另外一種 Type。

這樣的命名混淆,在一整個 process flow 裡面,一旦遇到不同的呼叫使用相同名稱卻在不同 context 有不同意義的參數,就會成為杯具。

再怎麼樣想不到適合的名字也不該用混淆的名字

2. 命名歸類的一致性: 譬如說某某 method 是 A 相關,可以 A 作為 prefix,那就不要突然 A 突然 B 突然又 C,否則誰知道是在幹嘛(丟鍵盤)

3. 函式命名沒有這麼難,就算不知道怎麼命名也至少用動詞加受詞 (V+OBJ) 吧,然後 OBJ 的名稱一定要一致,不要一下 memberInfo 一下 memberData 的。

4. 此外 method name 本身的命名應該要讓人能讀出 read-only / write 等資訊,而不是一個看起來明明是 read-only 的 API 本身卻包含了 write 的行為。

5. 參數類型、可選不可選、臨界值、長度、不同值所代表的隱含行為在文件上寫清楚也是相當重要的,否則呼叫方若誤解了參數所帶來的隱含操作,則會造成系統不穩定。

6. 另外參數名稱也請不要用 1234567 當 suffix 呀,若是為了安全性,你好歹也用英文縮寫。

7. 最後,至少寫個 persistent layer 的 test 吧,curl 腳本也行,不要一下噴 HTML 一下 XML 的,明明都送 Accept: text/xml 啦!

2014年6月11日 星期三

Perl URL Router Benchmarks

To improve the overhead of URL routing, I developed a tiny URL routing library in C - R3, which is designed for high performance and low memory usage.

The implementation uses the trie data structure. Trie is an efficient information retrieval data structure, which uses a prefix tree to search inserted strings by their common prefix, thus it's pretty fast for matching routes.

The tricky part of R3 is that we need to support regular expression patterns in our routing definition to dispatch path dynamically, thus R3 implemented a variant of Trie by mixing some concepts of DFA, not just simply inserting plain string paths into the prefix tree.

There are several comparison types in R3: plain string, opcode or regular expression pattern. Each node have its own comparison type to dispatch the path to their own children.

For those simple regular expression patterns, R3 compiles the regular expressions into opcodes to enhance the comparison speed and it's twice faster than using PCRE library to match the path with the patterns.

And since there are a lot of things to do before the matching operation, the whole tree structure needs to be compiled before dispatching paths.

Here is the continuous benchmark result of R3 itself, The C version R3 can dispatch over 11 million plain string paths per second.

A talented Perl hacker - Cindy Wang developed a CPAN module based on R3 library - Router::R3, which has become the fastest routing module on CPAN. It's so fast that you just can't deny.

The Perl version R3 (Router::R3) can dispatch nearly 1 million paths per second while Journey (The Rails router) dispatches 9.9 thousand plain string paths per second.

We also used the routing path generator from the rails' pull request stevegraham/rails/pull/1 to benchmark the Perl URL routers on CPAN, including HTTP::Router, Router::Simple, Router::Boom and Router::R3

There are over 3 hundred generated routing paths in the benchmark script. we tested the "plain string matching" with the route in the middle of the list, and also the first route matching, regular expression matching.

Here comes the dispatching speed result:

Benchmarking 'plain string matching' by path '/corge/quux/bar'
===============================================================
                Rate  HTTP::Router Router::Simple  Router::Boom    Router::R3
HTTP::Router      203/s            --           -89%         -100%         -100%
Router::Simple   1782/s          779%             --          -99%         -100%
Router::Boom   168658/s        83094%          9365%            --          -82%
Router::R3     954407/s       470684%         53461%          466%            --
Benchmarking 'regexp string matching' by path '/post/2012/03'
===============================================================
                Rate  HTTP::Router Router::Simple  Router::Boom    Router::R3
HTTP::Router     1076/s            --           -88%          -99%         -100%
Router::Simple   9309/s          765%             --          -91%          -97%
Router::Boom   104387/s         9602%          1021%            --          -66%
Router::R3     306925/s        28426%          3197%          194%            --
Benchmarking 'first charactar matching' by path '/'
===============================================================
                    Rate  HTTP::Router Router::Simple Router::Boom    Router::R3
HTTP::Router      3839/s            --           -87%         -98%         -100%
Router::Simple   30545/s          696%             --         -83%          -98%
Router::Boom    180555/s         4603%           491%           --          -88%
Router::R3     1535999/s        39910%          4929%         751%            --

2014年6月10日 星期二

評 Microsoft Sculpt 人體工學鍵盤 (Microsoft Sculpt Ergonomic Keyboard)

Microsoft Sculpt Keyboard 在造型上及人體工學上的設計實在無懈可擊。 但有幾個缺點造成相當多的不便:

1. Dongle 只能貼在滑鼠上,所以如果要帶鍵盤外出,就得連滑鼠一起帶。不然就是得把 Dongle 放口袋或其他地方,加上 Dongle 太小了... 非常容易弄不見。

根據 HLB 的敘述,Dongle 弄不見之後竟然沒有辦法維修或另外購買!!! 就算你願意花錢買 Dongle 也不行!

2. 鍵盤帶著外出,由於完全沒有電源開關,所以只要壓到鍵盤,就會造成電量損失,放上一整天隔天就會沒電! 所以帶著外出時,還要先把電池全部卸下........

2014年5月21日 星期三

R3 - 極致效能的 URL Router Library

其實幾年前就有想用 DFA 做 URL router 的想法,只是為了有趣,所以當時用 PHP 寫了一個簡單的 RE parser,但是用 PHP 寫成的 parser 實在太醜了,而且在動態語言裡面這樣實作也不會快到哪裡去,於是後來就作罷。

當時也另外看了 Journey 的原始碼,發現 Rails/Journey 在處理 Regular Expression 的路徑比對也只是把一整個 Regular Expression 路徑放到另外一個 array 裡面去循序比對罷了,並不會塞到 DFA 裡面.... 看到這邊其實就想丟鍵盤了,如果你只有 字串拿來做 DFA,那倒不如就直接用 Ruby 內建的 Hash Table 查表算了。

後來一直沒有動機繼續把這個想法做完,一方面,用 C parsing regular expression 實在太累人,且就算實作了,考量到開發時間有限,也不可能完整支援所有的 RE features,所以就折衷先寫了一個非常 Tiny 的 library - Pux 來解決 PHP + Nginx/Apache routing overhead 的問題。

再後來,有次跟 Cindy 討論發現其實可以用 prefix/suffix tree 來做比對。而 prefix tree 很適合這個問題。但 RE 還是沒有辦法很完美的整合進來。

睡了一晚,想到 RE 其實可以拆成個別的 node 做 partial match 即可,這樣就可以用很簡單的資料結構的解決掉問題,不用自己做 Regular Expression 的引擎,只要把部分字串交給 PCRE 接手即可,而且還可以得到非常不錯的效能。

後來用了一些零零碎碎的時間總算把雛形寫完了,接著就拿 stevegraham/rails 的 PR 裡的測試資料簡單評測了一下。發現新引擎處理比對的速度,每秒約可以處理一千兩百萬到一千三百萬個路徑 (12M~13M) 比對 ( http://c9s.github.io/r3/bench.html ) ,而 Journey 約處理九千個路徑左右 (約一千多倍的差異)。

現在,只要是 3P 語言都可以透過跟 R3 binding 得到相當不錯的效能!歡迎大家來實作 R3  binding:

專案網址: http://github.com/c9s/r3


目前 Cindy 寫的 Perl 版本的 R3 CPAN module 已經放上 CPAN :https://metacpan.org/pod/Router::R3

當 Rails/Journey 還在 9326 i/s 繞,Perl 版的 R3 跑起來就有 1,662,383 i/s。

當然你可能會說,Router 有需要這麼快嗎?Well 其實這個專案基本上只是為了實驗加興趣而實作的,雖是實驗,但所有的 Feature 都透過 C 的 Check Unit Testing 做測試,使用 autotools 做 feature testing, project building。

R3 後續在 Hacker News 以及 Twitter 上引起討論。 如:



日本的 mattn san 也做了一份 Perl Router 的 Benchmark,與號稱最快的 Perl Router Module 比較:


2014年5月15日 星期四

Judy array 在 2003 年時似乎是當時最快的 associative array 實作.. 當時好像掀起了一番熱潮.. 查了一下現在好像也有其他 hash table / associative array 比 Judy array 快。

有翻到 benchmark project 測 PHP 內建的 Array, SplFixedArray, Judy Array,結果 Array 居然表現跟 Judy Array 不相上下?不知道是不是因為 Language runtime 轉型 overhead 的關係?

2014年5月10日 星期六

動態語言裡設計函式介面越要彈性簡單好用,就需要實作 Runtime 的 type checking,執行時期會增加更多的計算成本。

程式語言如 C++, Haskell 可以透過 Parametric polymorphism 多型的方式提高介面易用度,編譯時期就可以確定要呼叫哪個符合類型的函式。

很不幸的,動態語言如 PHP, JavaScript, Ruby 不支援多型,就只能用 instanceof, typeof, is_a? 等方式來檢查型別,這時候 trade off 就來了,你希望函式要 "Less is more" 還是要 "說清楚吃雞豬牛或鴨" ?

前者好用但計算成本高,後者麻煩但計算成本低。這就是 jQuery 為什麼慢,而有些人堅持用 document.getElementById 的原因。

2014年5月8日 星期四

利用通勤等零碎時間讀張榮發回憶錄,內心澎湃不已。張總裁從蘇澳貧困的家中出生,歷經語言隔閡,18歲喪父,扛起扶養弟妹的責任,拼命苦讀考取船副,之後創立船公司,準確預測國際趨勢,並成為成為國際中首度使用貨櫃輪的船公司,在 GPS 還在美軍試驗時,就率先採用 GPS 作為貨櫃輪的導航系統;搭飛機的時候還用皮尺帳量機內座位間距、尺寸等..一直做到七八十歲才退休!長榮國際企業的成功真的不是偶然,誰有資格說企業家成功都是靠爸?

發現所謂有決心、超強意志、有智慧的企業家,是絕不提”夢想”兩字,他們只專注當下,對未來沒有妄想,克服眼前的困境,紮實的走好每一步路。

自我期許之

2014年5月7日 星期三

重構 PHP 引擎並大幅改善效能 - PHP Mailing List


譯文 Hackpad: https://hackpad.com/-PHP--HRY9UYvrArM

原文網址: http://news.php.net/php.internals/73888

認識我的人們都知道我在 Zend 主要的責任及熱情都放在 PHP 效能這個主題上。 事實上,從 PHP 5.0 版本開始,我們已經在綜合性效能評比 (Benchmark) 上改善了六倍的速度,並且讓一般使用 PHP 撰寫的應用程式達到了兩倍速度改善。

我們不停的改善 PHP 引擎以及 OPCache (PHP 5.5 版的核心套件,用於快取)。 然而,當時對於 PHP 5.5 版本的釋出,我們並無顯著的發展,除了別的事情之外,我們也試驗了一些記憶體管理, JIT 技術等其他可能的辦法。

我花了相當多的時間試驗 JIT 的想法,並證實這是可行的 (以 LLVM 為基礎的 JIT 編譯器,然後嵌入 OPCache)。 由 bench.php 得到的結果相當驚人 -- (由原本的 2.175 秒改善為 0.219 秒 -- PHP 5.5 近十倍速度的改善),但在現實的應用程式中,我們只得到了一些加速。 但這讓我們更深入的探索 PHP 引擎執行時期 (Runtime) 的特性以及那些真正的瓶頸,來取得巨大的進展。

很明顯的 VM 幾乎已經高度優化了,但是卻內部所使用的資料結構卻需要使用大量的記憶體調用 (Allocation)、釋放 (Deallocation)、參照計算 (Reference Counting)。

一般來說,現實生活中的 PHP 應用程式花了約 20% 的 CPU 實在在記憶體管理 (memory management)、10% 做雜湊表 (Hash Table) 操作、30% 呼叫內部函式 (Internal functions) 且只有 30% 花在虛擬機器 (VM) 上。

當然,我們嘗試了將 VM code 用 JIT-only 的方式運行,在大多數的狀況下,他還是花了同樣多的記憶體配置。因此,我們決定著重在最主要的效能瓶頸上。 主要的想法是修改內部的資料類型 (data types) 並最小化堆積 (Heap) 的用量。

這是一個相當困難的決定,因為我們必須重構相當大量的程式碼,而且我們完全不知道重構之後是否會造成其他問題或衝擊。

現在我可榮幸的向您們展示這四個月以來的工作成果。這次的 PHP 引擎重構大量的改善了效能、記憶體用量,並且為未來的效能改善及 JIT 建立了穩固的基礎。我會避免技術細節(更多細節將會在 http://wiki.php.net/phpng <http://wiki.php.net/phpng>* 發佈),簡單的說 - 我們改變了整棟建築物的地基並且盡量保持建築本身不改變。現在新的引擎已經達成了 *10-30% 左右的加速,且不只是在效能評比 (Benchmark) 中,在現實應用程式也達成了相同的加速!

以下是我們目前所達成的效能改善:
  • Wordpress 3.6 – 20.0% gain (253 vs 211 req/sec)
  • Drupal 6.1 – 11.7% gain (1770 vs 1585 req/sec
  • Qdig – 15.3% gain (555 vs 482 req/sec)
  • ZF test app – 30.5% gain (217 vs 166 req/sec)
在某些應用程式中,我們相較於其他 PHP 實作得到了更好的結果。 若有其他人能夠測試他們的應用程式,並且與目前使用的 PHP 版本的效能比較,整理更多的效能差異結果,那就更好了。

這次的重構還未完成,因為我們一開始只是測試這樣的修改是否能得到改善。 目前並不是所有的延伸套件 (Extension) 都得到支持,有一些單元測試是失敗的,此外,我們對於其他改善也還有更多想法。

但我們覺得,現有的結果已經足以證實改善的方向,且可以開放出來讓大家檢視、回饋或協助,如我們現在有了正確的方向,我們也想儘快讓社群參與。

因為,要支持所有的延伸套件 (Extension) 以及持續的做出更多改善,還有許多工作需要完成,這些也需要社群的協助。
請嘗試這個重構過的 PHP 引擎並且提供我們意見或結果,像是: 效能, 記憶體用量或任何的想法。 你可以在 php.net 上的 *phpng* 這個開發分支上找到這個新版的 PHP。

建置步驟則在: http://wiki.php.net/phpng

如同我們上面提到的,有一些 Extension 還未被支援,因此並不是所有的東西都可以運作。

我想要感謝 Xhinchin 以及 Nikita ,他們完成了這次的成果許多重要的部份。

謝謝。 Dmitry。


-- 林佑安譯於 2014 五月

      2014年4月25日 星期五

      Mac OS X 之 SSD 保養 - 適用 Macbook Air, Macbook Pro 等

      SSD 擁有相當優異的高效能讀寫效率,但大家都知道 SSD 以 2012 年以前的技術來說,若經常使用,壽命通常大約落在 1-2 年,不如機械式硬碟長。筆者一顆去年買的 Kingston 128GB,差不多 1 年又兩個月就突然宣告掛點,也因此,了解如何保養您的 SSD 是非常重要的一件事情。


      Mac OS X 在背後偷偷幫你做的事情:


      1. Spotlight 索引


      就是你右上角點放大鏡時,會出現的東西,我相信有很多人幾乎都不用這個功能 (我認識的人不是沒在用就是不知道有這個功能),但這個功能預設是開啟的。



      雖然你沒用到,但這程式有時候會不停的在背景大量讀取檔案,有時需要電腦做一些較重的工作時,整個系統會變得很吃力。

      如果在終端機內輸入 fs_usage ,你會發現有時硬碟大量的讀寫都是來自 mdworker,而這個 mdworker 就是 Spotlight 索引的背景程式。 他會怎麼跑? 你有多少檔案,他就跑多少檔案,不管那些檔案你到底有沒有用到,他都會收納起來放在這個搜尋索引內。

      有幾種調校方式:


      * 關掉 Spotlight: 如果你完全不使用 Spotlight,則可以考慮把 Spotlight 完全關閉,而 Spotlight 基本上是沒辦法從圖形介面去關閉的,所以你必須先打開你的"終端機",輸入一些指令:

      $ sudo mdutil -i off /

      這樣可以關閉掉整個本機磁碟的索引。要關掉所有磁碟的索引?

      $ sudo mdutil -i off -a


      * 關掉平常不搜尋的檔案類型: 點開放大鏡,最下方有一個 Spotlight 設定,點開之後,把不必要的類型都關掉:




      * 忽略有零碎小檔的目錄:

      2. 關閉 Time Machine 的本機備份模式


      Time Machine 的備份方式是這樣的,首先他每個小時會產生一個備份點 (Checkpoint),每 24 個小時,留下一個備份點,過一個月,就留下當月每天的備份點,每個月都留下每週的備份點。當硬碟空間不夠的時候,Time Machine 就會開始問你要不要清除一些太舊的備份點,來獲取更多空間。

      而從 Mac OS Lion 版本之後,Mac OS 的 Time Machine 會在你沒有外接硬碟時,在本機建立一整套備份。 (如果你有開 Time Machine 的話)

      平常我們都是將資料備份到外接硬碟,外出時也不會帶整個外接硬碟出門,沒接上外接硬碟,Time Machine 就會開始讀取你整顆硬碟的內容,全部備份到 /Volumn/MobileBackups 這個路徑底下。

      平常 SSD 空間就很不夠用了,這時候居然還來 Local Backup 全部複製一份寫進 SSD?

      剛剛有講到 mdutil 是 Spotlight,要處理 Time Machine 相關設定,把本機備份關掉,可以透過 tmutil:

      $ sudo tmutil disablelocal

      這樣就可以把本機備份關掉了。

      其他 tmutil 相關使用說明,可以用下列指令查詢:

      $ man tmutil


      另外,如果你在系統偏好設定中變更了 Time Machine 的備份硬碟,或是關閉又打開 了 Time Machine,Time Machine 的本機備份模式就會自動開啟,你必須再執行一次上面的指令才能再次關閉本機備份。



      3. 設定 noatime 掛載


      首先要先講 noatime,什麼是 noatime?atime 就是指所謂檔案存取時間 (File Access Time),你平日在開啟檔案時,就會更新這個 access time,每更新一次 access time,硬碟就必須讀寫,減少硬碟的壽命。

      新版的 Mountain Lion 會在 All Files 分頁或是 Spotlight 內列出所有最近使用的檔案,並且利用這個 access time 做排序。

      如果你不需要這個功能,可使用下列步驟來停止使用 Access Time:

      $ sudo -i
      $ cat > /Library/LaunchDaemons/com.disk.noatime.plist

      接著貼上:

      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
      "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
      <plist version="1.0">
      <dict>
              <key>Label</key>
              <string>com.disk.noatime</string>
              <key>ProgramArguments</key>
              <array>
                      <string>mount</string>
                      <string>-vuwo</string>
                      <string>noatime</string>
                      <string>/</string>
              </array>
              <key>RunAtLoad</key>
              <true/>
      </dict>
      </plist>



      4. 變更睡眠時資料存放的位置



      當你闔上螢幕時,Mac OS X 會進入到睡眠模式,此時會將目前在執行中的資料都暫時存放在記憶體中,若快沒電時,則將所有在記憶體內的資料寫入到硬碟,防止資料流失。

      若你有 4GB 的記憶體,一次睡眠,有可能會把 4GB 全數寫入硬碟,開機時再全部讀回。

      如果你不在乎執行中資料流失的話,可把設定改成睡眠時 "只存放在記憶體中"

      可透過下列指令設定:

      $ sudo pmset -a hibernatemode 0

      其中 "-a" 代表不論是電池模式 (-b)、電源模式 (-c)、UPS 模式 (-u),皆使用此設定。

      查看目前模式可輸入:

      $ sudo pmset -g


      5. 關閉不必要的 Chrome 分頁

      你若打開 iotop 查看 I/O 的讀寫狀況,你會發現 Google Chrome 在背景也做了不少 I/O:

      2014 Apr 25 11:16:49,  load: 2.21,  disk_r:    720 KB,  disk_w:   1356 KB
      
        UID    PID   PPID CMD              DEVICE  MAJ MIN D            BYTES
          0     24      1 cfprefsd         ??        1   2 W             4096
        501   8797    383 Google Chrome H  ??        1   2 W             4096
        501   8798    383 Google Chrome H  ??        1   2 W             4096
        501   8799    383 Google Chrome H  ??        1   2 W             4096
        501   4948    209 iTerm            ??        1   2 W            20480
          0    157      1 mds_stores       ??        1   2 R            57344
        501   8797    383 Google Chrome H  ??        1   2 R           126976
        501    221    209 SystemUIServer   ??        1   2 W           200704
        501    383    209 Google Chrome    ??        1   2 R           557056
        501    381    209 iPhoto           ??        1   2 W           585728
        501    383    209 Google Chrome    ??        1   2 W           585728

      為什麼呢? 因為 Google Chrome 為了高效的執行效率,所有運算的結果都會儲存成快取資料,執行時因此消耗大量記憶體,當分頁越來越多,Google Chrome 就會開始把沒有調閱的分頁暫時儲存到硬碟裡,等到切換過去時,再將結果全部讀出來。

      當然,這只是其中一項原因,以下是幾個 Chrome 相關的 Bug Report 可參考看看:

      Heavy I/O generated by chrome during inactivity - what happen/some craches? 

      frequent small disk I/O



      5. 盡量不要讓記憶體呈現滿載的狀況

      現代的操作系統大多會在記憶體不夠使用的時候,將記憶體內未使用到的內容搬動到磁碟裡,等到要使用時,再把另外一份空間寫入 Disk 挪空間出來放要使用的程式。

      6. 操作大檔或備份請愛用外接硬碟

      覺得一般外接硬碟太慢嗎?用 Time Capsule 備份又不給力一兩年就壞掉嗎?狂推 "WD My Book Thunderbolt Duo 6TB(3TBx2) 3.5吋雙硬碟儲存系統"


      透過 Thunderbolt 讓你效能不輸 SSD 又能有極高的穩定性

      目前來講,WD, Hitachi 是可靠度最高的兩個硬碟廠牌,backblaze 發佈了一篇文章講述過去 backblaze 使用了兩萬七千顆硬碟壽命之統計記錄:



      可參考: http://blog.backblaze.com/2014/01/21/what-hard-drive-should-i-buy/

      這邊說明一下為什麼寧可用 WD Thunderbolt Duo 也不要用 AirPort Time Capsule,雖然 Time Capsule 可以用 Wifi 進行 Time Machine 備份,但你若自己去 Google 一下 "AirPort Time Capsule" "無法使用" 或 "壞掉" 你會發現大部分的消費者用了一兩年 Time Capsule 就壞掉了,因為 Time Capsule 為了外型漂亮,散熱做得並不好,一旦長時間複製檔案,很容易就造成過熱的狀況。

      而且,看上方硬碟 Failure Rate 你覺得還有什麼牌子能贏過 HITACHI 或 WD 的穩定度嗎?XD

      相同的價錢,明明就可以買到更高傳輸效能的 Thunderbolt 儲存設備,且既然都要備份了,備份講究的就是穩定度,還是選個比較靠譜的 WD 比較好。



      7. 透過 iotop 觀測

      每個人使用電腦的習慣都不同,開發的環境也都不同,最後一道防線就是多利用 fs_usage 與 iotop 等工具來觀察作業系統背景在跑些什麼程式執行了大量了 I/O ,如果你發現了某程式進行大量 I/O 你又很少用的話,就盡可能少開。

      一般來說, fs_usage 倒出來的內容太多,很難從幾百幾千行的內容得到一些結論,可利用 iotop 來取得一些 sample 資料:

      $ iotop -C 5 12

      上面的指令每五秒印出結果,每個循環取得 12 個樣本資料,可印出如下:


      Tracing... Please wait.
      2014 Apr 25 19:35:38,  load: 2.06,  disk_r:    184 KB,  disk_w:    260 KB
      
        UID    PID   PPID CMD              DEVICE  MAJ MIN D            BYTES
          0     24      1 cfprefsd         ??        1   2 W             4096
        501    216    209 cfprefsd         ??        1   2 W             4096
          0     20      1 syslogd          ??        1   2 R             8192
        501    241    209 CalendarAgent    ??        1   2 W             8192
        501  12535    209 Mail             ??        1   2 R            20480
        501    241    209 CalendarAgent    ??        1   2 R            49152
        501  16062      1 com.apple.inter  ??        1   2 W            61440
        501  16062      1 com.apple.inter  ??        1   2 R           110592
        501    221    209 SystemUIServer   ??        1   2 W           188416


      上面的結果來看,第一行 "disk_r" 是總讀取量, "disk_w" 則是總寫入量

      第二行 "PID" 是指該程序的 Process ID,"D" 欄位中 "W" 是寫入, "R" 是 讀取,"BYTES" 欄位則顯示讀出或寫入的位元組數量

      以此類推,你可以很容易的找出消耗你 SSD 壽命的罪魁禍首。


      8. 透過 Activity Monitor 觀測

      Mac OS 內建的 Activity Monitor 其實是一個非常好用的工具,除了拿來看 Disk IO 的狀況,還可以觀測那個 App 消耗最多計算能源 (Computation Power):

      點擊 Bytes Written 就可以依照順序排出 I/O 操作最多的應用程式。


      以上圖我的狀況來說,因為開機後進行了一些大量檔案複製操作,因此可以忽略 Finder, kernel_task 等的統計數據。 mds_stores 則表示我的 Spotlight 在更新搜尋索引的時候所寫入的容量,再來就是 Google Chrome 的寫入狀況,目前使用狀況大致在 10-12 個分頁左右。

      當然每個人操作狀況不同,結果也會不同,就看大家怎麼去優化系統了。







      2014年1月20日 星期一

      Zend VM: zend object, persistent zval 以及 persistent hash table

      趁現在還有印象,筆記一下關於 Zend VM 內部的 persistency mechanism:

      1. ALLOC_INIT_ZVAL 基本上就是 ALLOC_ZVAL 加上 INIT_ZVAL

      2. MAKE_STD_ZVALALLOC_ZVAL 加上 INIT_PZVAL

      3. INIT_PZVALINIT_ZVAL 的差異是 PZVAL 會將 refcount__gc 設置為 1 而 INIT_ZVAL 基本上只是將所有的欄位設置為 0。 所以 ALLOC_INIT_ZVAL 出來的 zval 如果沒有處理好,會被 gc 清掉。

      4. zend object 不支援 persistent,其中一部分原因在於 object 基本上分為 zend_object 以及 zend_object_value, zend_object 是 persistent,但 zend_object_value 不是,且 object 本身的 properties 跟 handlers 都是使用 emalloc 申請來的,這些記憶體會在 request 結束的時候一起 free 掉。

      5. HashTable 結構可以 allocate 成 persistent memory,其中幾例是 EG(persistent_list)EG(symbol_table). 基本上需先使用 pemalloc allocate persistent memory 再使用 zend_hash_init 初始化 HashTable 結構變數,其中最後一項參數 persistent 必須為 1,則 allocate bucket 時,bucket 本身的記憶體才會是 persistent。

      6. zend_hash_copy 基本上是直接對 buckets 做拷貝的動作,所以 zval* 實際上還是同一個,只是在 copy_ctor_func 你可以透過 Z_ADDREF_P 來增加 reference counting。

      7. 要對 HashTable 完全拷貝,只能另外自己寫 recursive copy function,對於 value 內的 zval* 得另外 allocate zval* 出來,這樣在 persistent_list 內才不會被 request 結束時的 clean up handler 清除掉。

      8. 也就是說 object 沒有辦法 persistent 保留在程序中,在 APC 的作法,則是針對 object 去做 serialize。 沒錯就是那個 serialize 函數。

      9. zval 基本上都是 copy on write,如果在 extension 內要明確的複製 zval 結構到新的記憶體空間,則必須使用 SEPARATE_ZVAL

      10. 只要是 emalloc 來的記憶體,不管 refcount 是否大於 0,"幾乎" 都會在 RSHUTDOWN 時被清除掉。

      2014年1月7日 星期二

      Pux - 以新的概念重新設計 PHP Router

      時下開源的 PHP Router 如 Symfony/Routing 或 Zend\Mvc\Router 多半是以一個 Persistent  HTTP server 的角度來設計,然而一般 PHP 是通過 CGI 或 Apache 來執行,這樣的設計會讓您的 PHP 應用程式在執行時間跟編譯時間消耗太多效能,有過多的方法呼叫、類別載入、物件建置等等。

      在這些舊有的模式下,每一個新的 HTTP 請求進來,PHP 就必須重新把每個模組的路徑載入,且透過多個 method call 把路徑定義到一個 PHP Array,再使用 PHP 去對每個路徑做比對。

      有些 Router 的設計甚至做了更多的預先處理,譬如: Symfony/Routing 得利用 RouteCompiler 把路徑編譯成 PCRE Pattern, Symfony/Routing 甚至強迫每個 Route 都一定得使用 PCRE 來比對。

      雖然在小型應用程式還過得去,但在稍微大一點的 PHP 應用程式,Controller 與 Route Path 動扎幾十幾百個,整體消耗下來的多餘計算其實相當可觀。

      這些重複的預先處理其實是不必要的,如果能夠避免預先處理或者函數呼叫,就可以提昇整體效能。

      然而,直接定義 Route 成 PHP Array 雖然可以增加效能,但反而卻增加了開發成本。



      因此,針對這些問題,筆者在去年底跨年夜,重新設計了一個新的 PHP Router。


      Pux (http://c9s.github.io/Pux/) 是一個以效能為導向所設計的 PHP Router,針對 PHP 本身在 CGI 或 Apache 環境上的執行時間 (Run-time) 特性,以新的方式設計的 PHP Router。

      Pux 簡化了每個路徑的資料結構,並自動將路徑類型分為兩種類型,一種是需透過 PCRE 正規表示來比對的路徑,另外一種則是純字串的靜態路徑。

      會這麼做的原因,是因為 PCRE 正規表示的比對相對比純字串比對慢,甚至靜態路徑可以直接透過 Hash 表來查找。

      此外 Pux 針對在 Production 環境上效能重點,支持了使用 C 語言開發的 PHP Extension,只要安裝這個 Extension,就可以避免 PHP Class 重新載入,路徑比對的速度也會更快,可得到更高的執行效能。


      使用


      使用 Pux,您可在任何一個 framework 內建置您的 Route 定義檔,在檔案最尾端回傳 Mux 物件

      // load your composer autoload if it's needed
      // require '../vendor/autoload.php';
      use Pux\Mux;
      $mux = new Mux;
      $mux->get('/hello', ['HelloController','helloAction']);
      return $mux;
      接著使用 Pux 提供的命令列工具將定義檔編譯成 PHP Array:

      pux compile -o hello_mux.php hello_routes.php
      接著在您的應用程式內,只要寫一行 require 引入這個檔案就可以直接使用 Mux 做路徑比對的動作:

      $mux = require "hello_mux.php";
      $route = $mux->dispatch('/hello');

      效能


      以重新設計過的 Pux Router (Phux 舊名),在 iMac 2012 Mid 機器上 (Rough Benchmark) 與 Symfony/Routing 的效能比較,以下是參考數據:




      反應時間的部份, Pux 純 PHP 版本平均需要 8-10ms 的反應時間,但 Symfony 最 Minimal 的載入至少需要 9ms 平均 30-40ms 左右

      Pux - Requests per second
      Symfony/Routing - Requests per second


      測試案例的程式碼可在 router-benchmark 找到,兩者的測試案例都相當簡單,只有一個 /hello route ,並且純粹就 dispatch 的速度來做比較。

      註: Symfony/Routing 的測試案例不包含 Controller, Symfony, UrlGenerator, Apache2 Rule Dumper。

      Testing with route dispatch only. (no controller)

      Hardware:
      • iMac Mid 2011
      • Processor 2.5 GHz Intel Core i5
      • Memory 12 GB 1333 MHz DDR3
      • Software OS X 10.9.1 (13B42)
      Environment:
      • Apache 2.2 + prefork worker
      • PHP 5.5.6 + opcache