WebAssembly計算性能測試(JIT和非JIT版)

WebAssembly計算性能測試(JIT和非JIT版)

WebAssembly的一個優點是編譯生成的

WASM提供了一種統一的思路,傳說中可以把“一切”都編譯成WebAssembly二進製文件即wasm,實現“一次編譯,到處運行”。這種思路很具有誘惑力。

本文基於emcc測試WASM的性能。

當前emcc版本已經發展到了2.0.25,基本上bug不多,用起來也比較方便了。網上安裝教程眾多,在此不予贅述。

本文同樣以Cello中的benchmarks目錄下的Nbodies測試集為例。為了進一步加大計算量,我修改了裡面的例子,把迭代循環次數從100000加大到了10000000(放大了100倍)。具體參見:

同時WASM的執行需要nodejs或者是瀏覽器。那麼還有別的方便的東西可以執行嗎?當然有,比如WASM3。

WASM3是個什麼呢?

官網的說法是:Wasm3 是一個高性能的WebAssembly 解釋器。曾經還在Github 上看到,它比大多數其他類型的Wasm 解釋器在解釋執行Wasm 字節碼的速度上快8 倍左右;相對的,會比帶有JIT 的Wasm 編譯器慢4-5 倍。而比單純的Native Code 慢12 倍左右。從整體上來看作為一個解釋器,這樣的數據其實是十分不錯的。

當然以上這句話,現在在官網是看不到了。後面我有測評,wasm3沒有這麼好的性能,估計作者覺得不好意思,就把性能篇刪掉了。

那麼為什麼用wasm3呢?畢竟他用起來速度也不快啊! Wasm3 使用C 語言開發,可以成為項目中的一個庫而存在,這樣就可以在項目中輕鬆的執行wasm二進製文件了,用途廣得很。

首先,把nbodies_c.c和nbodies_cpp.cpp兩個文件生成wasm文件。

標準做法是:

emcc -O3 nbodies_c.c -o nbodies_c.js -s STANDALONE_WASM

運行則是:

node nbodies_c.js

怎麼可能會這麼簡單,必須有錯誤啊!

錯誤如下:

含有一條警告以及一個異常,反正就是不行。

不過wasm3倒是可以執行,wasm3的執行指令是:

wasm3 –repl nbodies_c.wasm

或者

wasm3 nbodies_c.wasm

都能執行。

不過以上執行時間需要9.312秒。

查看node生成的nbodies_c.wasm和nbodies_c.js可知,它生成了一堆亂七八糟的輔助js文件,這些輔助函數可以幫助於其運行與web端或者本地。 nbodies_c.js文件甚至高達123kb。

我顯然不需要這些亂七八糟的,那麼node可以重新來過,只生成wasm文件:

emcc -O2 nbodies_c.c -o nbodies_c4wasm3.wasm emcc -O2 nbodies_c.c -o nbodies_c4wasm3.wasm emcc -O2 nbodies_cpp.cpp -o nbodies_cpp4wasm3.wasm

這時wasm3對應的計算時間9.312秒。

wasm3對應的C++的時間為12.25秒。

現在修復node執行wasm的警告和異常問題

C版本

生成指令改為:

emcc nbodies_c.c -O2 -s WASM=1 -Wall -s MODULARIZE=1 -o nbodies_c.js -s WASM_BIGINT

當然,如下指令生成wasm文件也行:

emcc nbodies_c.c -O2 -o nbodies_c.js -s STANDALONE_WASM -s WASM_BIGINT

運行這條指令後的bug為:我裡面的不知道為什麼。所以還是別用這條指令了。

然後手寫一個簡單的js調用腳本runnbodies.js:

constModule=require('./nbodies_c.js');constwasm=Module({wasmBinaryFile:'nbodies_c.wasm'});wasm.onRuntimeInitialized=function(){console.log(wasm._main());};

顯然,以上就是簡單的執行C程序的main函數。

調用指令為:

node --experimental-wasm-bigint runnbodies.js

成功。 Nodejs執行C版的計算時間0.877sec。

C++版本

對應的c++版的生成指令為:

emcc nbodies_cpp.cpp -O2 -s WASM=1 -Wall -s MODULARIZE=1 -o nbodies_cpp.js -s WASM_BIGINT

然後手寫一個簡單的js調用腳本runnbodiescpp.js:

constModule=require('./nbodies_cpp.js');constwasm=Module({wasmBinaryFile:'nbodies_cpp.wasm'});wasm.onRuntimeInitialized=function(){console.log(wasm._main());};

調用指令為:

node --experimental-wasm-bigint runnbodiescpp.js

Nodejs執行C++版的計算時間0.998sec。

以上就把簡單的C或C++程序轉成了node或者wasm3可以執行的wasm二進製文件。

計算時間對比總結

看了這個結果,發現node v8性能真是可以,加入jit,配合wasm,性能直逼原生C程序,這裡性能甚至超過了原版C++,不可思議,當然emcc的後台是clang,沒準它也有貢獻。以後再細細琢磨去。

Wasm沒有了jit的加持,性能馬上就變成了腳本語言。當然,這個計算能力在腳本語言中也是數一數二的霸主,參考這個測試結果:

具體細節見

可見,wasm3的計算能力遠超腳本性能王Lua,甚至還超過了純C平台的Cello,要知道官網提供的win平台下的wasm3可執行文件才247kb,這麼小的文件就能獲得這麼強悍的性能,還是值得在項目中採用的。畢竟在很多情況下,計算性能並不是首要考量,比如在MCU上等。

由以上測試發現,wasm3的普適性極好,nodejs的奇技淫巧它全不需要。用起來簡單粗暴。但是性能上就大打折扣了,缺了jit它立馬變成了一個腳本語言,當然現在wasm3成了腳本之王,Lua已經乾不過它了,除非上Luajit,可惜Luajit好像死翹翹了。

What do you think?

Written by marketer

小而美的品牌終局是什麼?揭秘30條創始人容易踩坑的品牌真相

為什麼品牌廣告不能停? 如何用社群行銷作品牌廣告?