天天看點

javascript、ruby和C性能一瞥(3) :上彙編

在博文(1)和(2)裡分别用了4中方式寫一個素數篩選的算法,分别是javascript in browser、node.js、ruby和c;最終的結果是c最快,node.js其次,js in b雖然也不慢,但極不穩定,是以排在第三,ruby最慢。

現在我們在linux64中用彙編語言重寫sieve算法,看看動用最終的武器:彙編語言,我們能不能進一步優化素數篩選算法。

如果忘了算法邏輯,不要緊,下面分别再次貼出node.js、ruby以及c的sieve代碼:

首先是node.js:

然後是ruby:

最後是c的代碼:

下面嘗試用彙編重寫sieve函數,需要注意的幾點是:

可以不調用c庫中的sqrtx标準函數,直接使用浮點fsqrt指令;

可以将絕大部分記憶體變量放到寄存器中以加速存取;

隻關心sieve函數的算法,而用c代碼調用彙編的sieve,這樣可以發揮各自的長處;否則我還得寫個讀取輸入參數的前導代碼,不值當的;

注意彙編和c的調用接口:在linux64中,參數并不壓棧傳遞;因為sieve隻有一個參數,是以放在rdi中傳遞,傳回值還是放在rax中。

需要調用mmap申請足夠的記憶體以便做篩表。注意這裡沒有寫足夠詳細的錯誤處理,更詳細的操作請參考本貓的【linux下64位彙編的系統調用】系列博文。

最後要注意的是,代碼優化和代碼編寫一定不要同時進行!這在所有程式設計語言中都适用,彙編中尤為重要!否則必成一鍋粥鳥!因為誰都不可能上來就寫優化後的代碼,一定是先功能邏輯正常後在着手考慮優化的問題。本貓第一遍寫的是最保守代碼,全部變量放在記憶體中,随用随取,用完儲存。在代碼邏輯正确後(這時計算sieve 100000000所花時間為4xxx ms),在逐漸将記憶體變量轉放到寄存器中。

要說明的是該段代碼肯定還可以進一步優化,但本貓就到這裡為止了,希望能夠抛磚引玉。先把結果說一下吧:用彙編寫的sieve版本是最快的,超過了c代碼,在本貓 intel(r) core(tm)2 duo cpu t7100 @ 1.80ghz上跑出了最快的37xx毫秒,比c版的平均要快100-200毫秒,而且非常穩定。

最後貼出c的main.c和彙編的sieve.s代碼:

main.c:

彙編的sieve.s: