More Related Content
Similar to WASM(WebAssembly)入門 ペアリング演算やってみた
Similar to WASM(WebAssembly)入門 ペアリング演算やってみた (20)
More from MITSUNARI Shigeo
More from MITSUNARI Shigeo (20)
WASM(WebAssembly)入門 ペアリング演算やってみた
- 2. • WASM = ブラウザで高速なアプリケーションを実行す
るためのバイトコード、ツール
• 現在開発中(のため様々なものがよく変わる)
• 一次資料(http://webassembly.org/)が重要
• 少し歴史
• その昔のIEのActiveX(x86)
• 2000年ActionScript(Flash)
• 2010年ChromeでNaCl(x86/arm)
• 2013年Firefoxでasm.js(汎用), ChromeでPNaCl(汎用)
• 2014年HTML5
• 2017年PNaClからWebAssemblyへ
• WebGL, WebGPU(Apple), NXT(Google)
• deeplearn.js https://pair-code.github.io/deeplearnjs/
• WebDNN https://mil-tokyo.github.io/webdnn/ja/ 2 / 16
概要
- 3. • デザインゴール
• 高速で、安全で、well-defined
• ハードウェア, 言語, プラットフォームに依存しない
• open
• https://webassembly.github.io/spec/
• 仮想マシン
• スタックマシン
• レジスタはi32, i64, f32, f64, 将来SIMDも
• 通常の四則演算, 論理演算をサポート
• ビットを数える(popcnt), clz, ctzがある
• 浮動小数→整数変換(ceil, floor, trunc, nearest)
• 分岐, min, max
• carry演算, 64x64→128bit乗算, 128/64bit除算がない
3 / 16
WASMの仕様
- 4. • C/C++からWASM
• emscripten, emcc
• アセンブラ, 逆アセンブルなど各種変換
• WABT(https://github.com/WebAssembly/wabt/)
• wast2wasm, wasm2wast
• https://cdn.rawgit.com/WebAssembly/wabt/7e56ca56/demo/
wast2wasm/ (デモ)
• LLVMからWASM
• llc foo.ll -march=wasm32
• アセンブリコードをwastに変換(s2wasm)
• ClangからWASMの直接出力?
• いろいろあるが流動的なので本家を見て
4 / 16
様々なツール
- 6. • square.wasmをfetchしてインスタンス化
6 / 16
square.html
<html><head><script>
fetch('square.wasm')
.then(response => response.arrayBuffer())
.then(buffer => WebAssembly.compile(buffer))
.then(module => new WebAssembly.Instance(module))
.then(instance => {
let square = instance.exports.square
let button = document.getElementById('run')
let x = 15
button.value = 'square of ' + x + ' ='
button.addEventListener('click', function() {
document.getElementById('result').innerText = square(x)
})
})
</script></head><body>
<input type="button" id="run" value="loading WebAssembly"/>
<span id="result">0</span>
</body></html>
- 7. • Emscripten SDKのインストール
• main()つきCのソース
• buildしてブラウザで表示
7 / 16
Cサンプル
git clone https://github.com/juj/emsdk.git
cd emsdk
./emsdk install sdk-incoming-64bit binaryen-master-64bit
./emsdk activate sdk-incoming-64bit binaryen-master-64bit
#include <stdio.h>
int add(int x, int y) { return x + y; }
int main() { printf("add %d¥n", add(3, 5)); }
emcc t.c -s WASM=1 -o t.html
emrun --no_browser --port 8080 .
- 9. • 当初の期待
• mclはLLVMビットコードを出力
• x86/x64/arm/arm64専用コードはllcが生成(or Xbyak)
• -march=wasm32すればよいだけと思っていた
• 現時点での制限
• 64bit整数より大きい整数を扱えない
• 本来のLLVMはi256(256bit整数)などが使える
• 内部で自動的に64bit x 4(32bit x 8)の演算に変換される
• 32bit×32bit→64bit乗算しかない
• そもそもcarry演算命令が無いのでCで書いてもそんなに変わ
らないのでは(最適化コンパイラを信じれば)
9 / 16
当てが外れたLLVM for WASM
Unsupported: %z = mul i128 %x, %y
LLVM ERROR: Binary operator type not yet supported for integer types larger
than 64 bits
- 10. • WASM仕様再掲
• carry演算, 64x64→128bit乗算, 128/64bit除算がない
• add/subは64bit単位で加算
• x + y < yならcarry発生として繰り上げ
10 / 16
多倍長演算部分をCで再実装
template<class T>
T addN(T *z, const T *x, const T *y, size_t n) {
T c = 0;
for (size_t i = 0; i < n; i++) {
T xc = x[i] + c;
if (xc < c) {
z[i] = y[i];
} else {
xc += y[i];
c = y[i] > xc ? 1 : 0;
z[i] = xc;
} }
return c; }
- 11. • 32x32→64, 64/32→32を使って実装
• 結構めんどい & 遅い(せっかくの64bit環境が…)
• 将来WASMがサポートすれば1命令にできる
• とりあえず出来た
• https://github.com/herumi/mcl/blob/master/include/mcl/vint.
hpp
• pure C++
• 四則演算と論理演算サポート
• 巾乗
• mod pにおける逆元
• gcd, Legendreシンボル
• 素数判定
11 / 16
64 x 64→128, 128 / 64→64
- 12. • CPU : Core i7-7500U 2.7GHz(Skylake)
• native : gcc 5.4.0
• emcc : clang 4.0.0(emscripten 1.37.15)
• -DMCL_USE_VINT -DMCL_VINT_64BIT_PORTABLE
• Vint w/ 64-bit mulは64 x 64→128乗算を使うか/使わないか
• 将来WASMが対応すれば切り替えられる
• ので今はwastを直接書いてがんばらない
• demo for Broswer
• http://herumi.github.io/mcl/demo/pairing.html
12 / 16
性能
Xbyak LLVM Vint w/
64-bit mul
Vint wo/
64-bit mul
Firefox
55
Chrome 61 Edge
msec 0.35 0.50 1.37 2.3 3.19 3.42 5.48
- 13. • モジュール化オプションとexportする関数を指定
• -s WASM=1 -s "MODULARIZE=1" -s
"EXPORTED_FUNCTIONS=[$(EXPORTED_MCL)]"
• EXPORTEDMCLはヘッダファイルから関数を自動抽出した
• ついでにJS用プロトタイプ宣言用ファイルも生成
• int, pointerは全てnumberに(stringもあるが今回は避けた)
13 / 16
JSから呼び出す
// bn.h
void mclBnFr_setInt(mclBnFr *y, int64_t x);
int mclBnFr_setStr(mclBnFr *x, const char *buf,
size_t bufSize, int ioMode);
// exported-mcl.js
mclBnFr_setInt =
mod.cwrap('mclBnFr_setInt', 'null', ['number', 'number', ])
_mclBnFr_setStr = mod.cwrap('mclBnFr_setStr',
'number', ['number', 'number', 'number', 'number', ])
- 14. • moduleインスタンスを作るsetupWASM
• stackやheapの面倒を見てくれる
14 / 16
moduleインスタンスを作る
function setupWasm(fileName, nameSpace, setupFct) {
var mod = {}
fetch(fileName)
.then(response => response.arrayBuffer())
.then(buffer => new Uint8Array(buffer))
.then(binary => {
mod['wasmBinary'] = binary
mod['onRuntimeInitialized'] = function() {
setupFct(mod, nameSpace)
}
Module(mod)
})
return mod
}
- 16. • moduleインスタンスのRuntimeを使う
• stackSave/stackRestoreでstackの保存と復元
• mod.HEAP8にstackAllocで取得した位置を利用する
• _malloc, _freeを使ってもよい
16 / 16
JSのstringをCのconst char*に変換
// bufはstring
gen_setStr = function(func) {
return function(x, buf, ioMode) {
if (ioMode == null) { ioMode = 0 }
var stack = mod.Runtime.stackSave()
var pos = mod.Runtime.stackAlloc(buf.length)
for (var i = 0; i < buf.length; i++) {
mod.HEAP8[pos + i] = buf.charCodeAt(i)
}
r = func(x, pos, buf.length, ioMode)
mod.Runtime.stackRestore(stack)
}
}