最佳化 float32 將拉近 asm.js 與原生碼之間的效能差異

作者:
瀏覽:673

asm.js 即是 JavaScript 簡化後的子集,可輕鬆進行最佳化,並適合作為如 C 與 C++ 語言的編譯目標。在 2013 年稍早時,Firefox 已可執行 asm.js 並達到原生程式碼的一半速度。也就是說,由 emscripten 編譯過的 C++ 程式碼執行速度,可達到相同 C++ 原生編譯程式碼 50% 的速度。Mozilla 認為只要能同時提升 emscripten (可從 C++ 產生 asm.js 程式碼) 與 JavaScript 引擎 (用以執行轉譯而得的 asm.js 程式碼) 之後,將可更貼近原生程式碼的執行速度。

雖然目前達到的多項加速特點,均仍屬於小規模且特定的功能,但其中仍有幾項重要功能。舉例來說,Firefox 最近剛能夠最佳化某些浮點作業,以 32 位元浮點執行並取代之前的 64 位元雙精度 (64-bit Doubles) 浮點,進而改善該連結中所提到某些條件的實質效能。任何剛好可透過此方式最佳化的 JavaScript 程式碼,均可因這種通用的最佳化功能而受惠。隨著完成此最佳化之後,就沒有理由不把 float32 添增到 asm.js 的型別系統 (Type system) 中,讓所有編譯為 asm.js 的程式碼均可受惠。

我們最近在 emscripten 與 SpiderMonkey 中皆完成了實作,下圖即為效能分析表:

最佳化 float32 將拉近 asm.js 與原生碼之間的效能差異

執行時間是以 clang 項目為標準,所以數值越低代表效能越好。紅色柱 (firefox-f32) 代表 Firefox 搭配 float32 而執行 emscripten 產生的程式碼。根據圖表所示,Firefox 搭配最佳化 float32 之後,執行所有效能基準可達原生 App 的 1.5 倍耗時或更短。與 2013 年稍早所提「為原生 App 的 2 倍耗時」相較,明顯又是效能提升的一大步。若要想單看 float32 最佳化所提升的效能,就直接與旁邊橘色柱 (firefox) 比較即可。在使用如 skinning、linpack、box2d 等偏重浮點運算的效能基準中,提升的速度仍相當可觀。

另一項必須注意的重點在於,這些數字不僅代表單一的原生編譯器,同時也包含 clang 與 gcc。在某些效能基準之下,clang 與 gcc 之間的差異極大。我們說到「約原生 App 速度的 N 倍耗時」,所用的「原生速度 (Native speed)」其實是比較不嚴謹的用詞。

若使用如 box2d、fasta、copy 等的效能基準測試,則相較於 clang 與 gcc 之間的差距,asm.js 與 clang 之間的距離亦類似甚至更接近。且 asm.js 甚至在 box2d 上微幅領先 clang (而 gcc 也同樣在 box2d 上大幅領先 clang,可能是 clang 後端的 codegen 正好有點凸槌)。

整體而言,「原生速度」不僅是單一數字,也代表某個範圍。看來 asm.js 可在 Firefox 上更貼近該範圍。也就是說:雖然 asm.js 平均來看是比 clang 與 gcc 來得慢,但整體範圍與各種原生編譯器之間的差距已經不遠。

另請注意的是,emscripten 是刻意預設關閉 float32 程式碼的產生功能。雖然 float32 可同時提升效能並確保 C++ 浮點語意正確無誤,但因為必須添增 Math.fround 呼叫,所以也讓程式碼更長。如此將在某些條件下 (尤其是 JavaScript 引擎尚未支援 Math.fround) 造成較不利的影響。

這裡有幾個方法能暫時解決此問題。如「outlining」這個編譯選項可縮減函式長度的上限。我們另外還有幾個想法,期待能提升 emscripten 中的程式碼產生功能。所以我們會開始花時間實驗這些想法,並隨時注意瀏覽器何時才能全數支援 Math.fround (目前僅有 Firefox 與 Safari 支援)。希望不久的將來,就能讓 emscripten 預設開啟 float32 最佳化功能。

總結

總結來看,上圖所顯示的 asm.js 效能更接近原生 App 的速度了。基於前面提到的理由,我不建議現在就使用 float32 最佳化功能。當然,我希望短時間內就會解決此一問題,讓大家都能盡情使用,且其提升的效能確實可觀。不論是 emscripten 或 JavaScript 引擎,現在都正進行或規劃大幅強化的工作。所以即使是目前效能 (約原生 App 耗時的 1.5 倍或更短一點) 也絕對還有進步的空間。

 

原文連結:Gap between asm.js and native performance gets even narrower with float32 optimizations