講個小故事:Unix的yes命令

  你知道的最簡單的Unix命令是什麼?

  是echo,它把字符串打印到終端,然後返回true,它的返回值總是0.

  除了echo,還有一個命令也很簡單,它是yes。如果你運行它,不帶任何參數,它會無限輸出字母y到屏幕上,其中每一個y都獨佔一行。

  感覺很沒用?其實它是很有用的

  安裝程序的時候,有的程序需要你不斷地按y和回車,安裝進程才能繼續工作。yes命令可以解救你!它幫你輸入y和回車,這樣你就可以安心去看唐老鴨動畫片了。

  讓我們自己編寫一個yes命令

  這是一個基本的版本,嗯,用BASIC寫的

  然後我們再寫一個Python版本的:

  感覺很簡單?等等,先別急。事實上上面兩個程序都很慢。

  讓我們對比一下系統內置的版本:

  我要自己寫一個快一點的版本,我嘗試用Rust去寫:

  稍微解釋一下這段程序:

  我們循環打印的字符是從第一個命令行參數中獲取的,我們把這個參數保存在變量expletive中。

  我們使用unwrap_or這個方法獲取命令行參數的值,如果沒有命令行參數,那麼就使用"y"。

  讓我們測試一下

  額,這就尷尬了,比Python的版本還慢。這讓我很驚訝,於是我到處尋找該命令的C源碼。

  下面這段代碼是yes命令作爲系統命令發佈的第一個版本,它發佈在Unix 7上,由Ken Thompson於1979年1月10日編寫。

  好像沒什麼特殊的地方。

  如今,這個命令被放在GNU coreutils中,當前這個版本用了整整128行代碼來完成這個功能,你可以在github上找到它的源碼:https://github.com/coreutils/coreutils/blame/master/src/yes.c。已經過了25年,這個命令居然仍然在更新中!最近一次更新大概在一年前。它的效率非常高

  最關鍵的是最後這部分

  哇哦!他們就是用了一個緩衝區來讓寫入操作變得更快。

  緩衝區大小由常量BUFSIZ控制,這個變量在各個系統中不同,以便能最優化寫入效率。在我的系統中,它被定義爲1024bytes,我把它改爲8192bytes,發現程序更快了。

  所以,參考這個思路我更新了我的Rust程序。

  一個重要的細節是,緩衝區大小必須是4的倍數,這樣來保證內存數據的對齊。

  我這個程序可以達到51.3MiB/s,這個速度已經快於我的系統自帶版本了,但是仍然比Reddit論壇上網友曬出的程序慢很多,網友的程序可以達到10.2GiB/s。

  再更新

  我再次求助於社區,有人給我指出了之前網上對這個問題的一些討論。下面是他們優化的代碼,在我的機器上運行效果達到了3GB/s。

  現在這個程序的思路已經完全不同了!

  我們準備了一個字符串緩衝區,這個緩衝區在循環中被不斷複用。

  標準輸出用鎖保護起來。所以,相比於不斷地請求鎖、釋放鎖,我們把標準輸出一直佔用着。

  我們使用平臺原生的 std::ffi::OsString 和 std::borrow::Cow 來避免不必要的分配。

  這裏面唯一我能做出的改進貢獻就是移除一個沒用的小變量。

  學到了什麼?

  很小的一個命令yes裏面居然如此複雜。它使用了輸出緩衝區和內存對齊來提高性能。重新實現這些小程序很有趣,同時我對它們所使用的提高性能的技巧也感到驚歎不已!

  大連×××醫院 http://yiyuan.120ask.com/dlnk/

  大連市×××醫院 http://yiyuan.120ask.com/dlnk/news/

  大連婦科醫院 http://yiyuan.120ask.com/dlfk/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章