LoginSignup
14
10

More than 5 years have passed since last update.

(Learn the Hard Way)nodejs-8でのWebAssembly自体を調べてみた

Last updated at Posted at 2017-06-14

この文書は、nodejs-8で有効化されているWebAssemblyがどういう仕組なのかを調査したものです。主題はWebAssemblyそのものの仕組みについてであり、普通のWebAssembly記事でやってるような既存のC/C++資産利用やemscripten使用法や速度比較といったブラックボックス利用を主題にした話ではありません。

WebAssemblyとは

WebAssemblyは、ブラウザ上で実行可能なプログラム実行環境の標準の一つです。

ただし現時点では直接実行させるのではなく、JavaScript環境から実行させるものとなっています。WebAssemblyオブジェクトを介して、WebAssemblyプログラムのバイナリをWebAssembly.Moduleとしてロードし、そこからJavaScript側で関数として取り出して呼び出すことでコード実行させることができるもの、となっています。

そして現在、v8等のブラウザに搭載されるJavaScriptエンジン自体が、WebAssemblyプログラムの実行環境を備え、ブラウザ上で利用可能となっています。node.jsではバージョン8からデフォルトでブラウザと同じWebAssemblyオブジェクトが利用可能となりました。

WebAssemblyのMinimum Viable Product(MVP)

WebAssemblyの最初の仕様は、実用性を損なわない範囲で最低限必要な機能のみを備えたものを早期に出荷することを目指して作られ、MVPと呼ばれています。スレッドやGCなどの複雑な機能の追加は、MVP以後にされるべきものとされています。

たとえば、以下は、WebAssemblyの設計指針上の制約ではなく、MVPな要素だとしています:

  • module形式だけ実行できる
  • 関数の戻り値が一つ
  • 扱えるのは32ビットと64ビットの値だけ

将来的には、Post-MVP部分として追加されていくことでしょう。現状の制限がWebAssemblyの設計指針からくるものなのか、単にMVP以後へ後回しされたものでしかないのか注意してみていく必要があります。(この文の全体にわたって、MVP的な内容への言及には"現在"、"現時点"、"現状"と言った形容をつけてあります)

WebAssemblyプログラムの2つのフォーマット

WebAssemblyのプログラムファイルには、2つの表現形式があります。

テキストフォーマット(=.wastファイル)では、()で木構造データを表す、S式スタイルを採用しています。

そして、JavaScriptから実行させるには、バイナリフォーマット(=.wasmファイル)を使用します。

WebAssemblyのmodule

現在WebAssemblyプログラムは"module"の単位で提供します。すなわちwastファイルのS式のルート要素は(module)になります。

moduleは以下のトップレベル要素をバンドルしたものとなっています。

  • (global): 名前付きグローバル変数値(要初期値)
  • (memory): インデックス値でアクセスするリニアな可変ヒープ領域
  • (table): インデックス値でアクセスする固定型要素のリスト
  • (func): 関数のコード

  • (import): ロード時に渡す必要のある名前付きの値

  • (export): 外からモジュールへアクセスできる名前付きの値

  • (type): funcの型シグネチャ定義

  • (data): memoryの初期化値

  • (elem): tableの初期化値

  • (start): ロード時に呼び出される関数の指定

global/memory/table/funcは、それぞれ固有に存在するアクセス命令セットの対象となるオブジェクトであり、外部のJavaScriptと連携するためのimport/exportの対象にもなります。

import/export式は、トップレベルで、名前付きglobal/memory/table/funcに対して割り当てます。typefuncの型(引数と戻り値の型)の定義で、トップレベルに存在します。ただし、テキストフォーマットでは、import/export/typefunc/global/memory/table式の内部に埋め込む表現も可能です。

data/elem/startはロード時の初期化用の仕組みです。存在しなくても問題ありません。

ちなみにtableは、現状ではmoduleにひとつだけ置くことができ、要素は"anyfunc"(関数)のみ可能となっています。命令の中にtableの要素の関数を呼び出すcall_indirectがあります。静的に型チェックする通常のfuncへのcall命令と違って、call_indirectでは実行時に型チェックをするという違いがあります。

ツールbinaryen

WebAssembyフォーマットを扱うためのツールは数種類あるけれど、"binaryen"が、homebrewですでにパッケージとして用意されており、導入しやすいです。

# on macOS
$ brew install binaryen

binaryenには以下のコマンドが含まれています:

  • wasm-as: wastからwasmへ変換する
  • wasm-dis: wasmかwastへ変換
  • wasm-shell: wasmファイルを実行する(unittest用?)
  • asm2wasm: asm.jsモジュール形式のJavaScriptファイルをwasm/wastに変換する
  • s2wasm: アセンブリスタイルのWebAssembly(LLVMでのWebAssemblyバックエンド出力)である.sファイルを wasm/wastに変換する

今回は、wasm-as/wasm-dis/asm2wasmだけを使います。

WebAssembly JavaScript API

WebAssemblyでは、JavaScript上でのWebAssemblyオブジェクトの仕様も決められています。

  • WebAssembly.validate(buf)
  • WebAssembly.compile(buf)
  • WebAssembly.instantiate(buf, imports)
  • new WebAssembly.Module(buf)
  • new WebAssembly.Instance(module, imports)
  • new WebAssembly.Memory(opts)
  • new WebAssembly.Table(opts)
  • new WebAssembly.CompileError
  • WebAssembly.LinkError
  • WebAssembly.RuntimeError

compileinstantiatePromiseです。

また、このAPI仕様で、WebAssemblyの値とJavaScriptの値のマッピングも規定されています。

例1: 手書きwastファイルでwasmファイルを作り、nodejsから呼び出すまで

(他の言語は使わずに)簡単なWebAssemblyプログラムをテキストフォーマットで記述して、それをnodejsで実行させることから始めます。

adder.wastを記述

テキストフォーマット仕様オペレータ仕様を元に、整数加算をする関数addだけを入れたモジュールのwastファイル"adder.wast"は以下のようになります:

(module
    (func (export "add") (param $a i32) (param $b i32) (result i32)
        (return (i32.add (get_local $a) (get_local $b))))
)

WebAssemblyの関数は(固定サイズの)ローカル変数(param及びlocal式)のあるスタックマシンであり、その範疇ではglobalやmemoryは不要です。

現状、変数や値のは、i32i64f32f64の4種類のみです。add演算命令には必ず型指定がつき、引数も戻り値も全て同じ型を使います。このため、4型で相互変換をする命令が多数存在しています。

注意点として、JavaScript APIの仕様により、現状i64型なシグネチャの関数は、JavaScript側から直接アクセスできません。

adder.wasmへ変換

binaryenのwasm-asコマンドを使って、wastファイルをバイナリのwasmファイルに変換します。

$ wasm-as adder.wast > adder.wasm

非同期版use-adder-async.js

ブラウザ等での外部リソースアクセスは通常非同期処理のため、基本は非同期にロードして使うのがよいでしょう。

//use-adder-async.js
"use strict";

const {promisify} = require("util");
const fs = require("fs");
const readFile = promisify(fs.readFile);

readFile("./adder.wasm").
    then(buf => WebAssembly.instantiate(buf, {})).
    then(source => console.log(source.instance.exports.add(10, 20)));      

instantiate(buf, imports)の結果は{module: WebAssembly.Module, instance: WebAssembly.Instance}であり、WebAssembly.Instanceexportsプロパティにwasmからexportした関数があります。

このコードは、以下のように、特別なオプション等の必要なく実行可能です。

$ ls
adder.wasm      adder.wast      use-adder-async.js
$ node use-adder-async.js
30

同期版use-adder-sync.js

一方、nodejs APIのfsにある同期ファイルアクセスを使えば、同期的にもwasmモジュールをロードできます。

//use-adder-sync.js
"use strict";

const fs = require("fs");

const buf = fs.readFileSync("./adder.wasm");
const mod = new WebAssembly.Module(buf);
const inst = new WebAssembly.Instance(mod, {});
const {add} = inst.exports;
console.log(add(10, 20));

ちなみに、WebAssembly JavaScript APIには、非同期的なcompileinstantiateと、同期的なModuleInstanceが用意されていますが、compileで作ったModuleをつかってnew Instanceするなど、ミックスさせて使うことも可能なようです。

例2: JavaScriptにはないWebAssembly演算を使ってみる

現在、64ビット整数演算や、ビット処理rotr/rotl/popcnt/ctz等はJavaScriptには存在していません。
そこでWebAssemblyに現在実装済みのこれらを呼ぶだけの関数をexportするmodule "bitops.wast"を書いてみます:

(module
    (func (export "popcnt") (param $a i32) (result i32)
        (return (i32.popcnt (get_local $a))))

    (func (export "pack") (param $a f64) (result f64)
        (return (f64.reinterpret/i64 (i64.trunc_s/f64 (get_local $a)))))
    (func (export "unpack") (param $a f64) (result f64)
        (return (f64.convert_s/i64 (i64.reinterpret/f64 (get_local $a)))))

    (func (export "clz64") (param $a f64) (result i32)
        (return (i32.wrap/i64 (i64.clz (i64.reinterpret/f64 (get_local $a))))))
)

現在、64ビット整数なシグネチャを持つ関数はJavaScriptから直接アクセスする手段がないので、なんらかの工夫をする必要がでてきます。そこで、以下のような手段が考えられます。

  • memory(ArrayBuffer)を使う
  • 32ビット整数2つに割る
  • ビット列として埋め込んだ64ビット浮動小数を使う

今回は最後のを採用するために、64ビット整数なビット列を作る機能が必要で、(JavaScript側で処理した)浮動小数の数値を64ビット整数ビット列にするpackと64ビット整数ビット列から(JavaScript側で処理する)浮動小数値にするunpackの2つの関数をWebAssembly内で定義しました。

reinterpretは同じビット列として型情報だけ差し替える命令であり、一方converttruncateは数値として型変換する命令です。53ビットを超える64ビット整数値は、64ビット浮動小数で表せられない値があるので、truncateでは切り詰められることになります。

以降、"wasm-as"コマンドでwastファイルをバイナリwasmにして、JavaScriptでロードして利用する手段は前と一緒です。

そこで今回は、nodejsモジュールっぽい"bitops.js"を作成し、それを使ったコードにしてみました。

//bitops.js
"use strict";

const fs = require("fs");

const buf = fs.readFileSync("./bitops.wasm");
const mod = new WebAssembly.Module(buf);
const inst = new WebAssembly.Instance(mod, {});
module.exports = inst.exports;
//use-bitops.js
"use strict";

const {popcnt, pack, unpack, clz64} = require("./bitops.js");

console.log(popcnt(0b101101000)); // => 4
console.log(popcnt(-1)); // => 32
console.log(popcnt(5.5)); // => 2 (as 5.5|0 => 5)
console.log(pack(1)); // => 5e-324: pack (f64) number as i64 bitfield
console.log(unpack(pack(1))); // => 1: unpack i64 bitfield to (f64) number
console.log(clz64(pack(1))); // => 63
$ node use-bitops.js 
4
32
2
5e-324
1
63

例3: asm.jsなモジュールをwasm化して使う

まず、asm.jsなモジュール"adder.asm.js"を手書きします。

// adder.asm.js
function () {
  "use asm";
  function add(a, b) {
    a = a | 0;
    b = b | 0;
    return a + b | 0;
  }
  return {add: add};
}

binaryenの"asm2wasm"コマンドでwasmファイルへ変換します。また、asm2wasmは"-o file.wasm"オプションが存在しなければ(色付きの)wast形式で標準出力します。

$ asm2wasm adder.asm.js -o adder.wasm
$ asm2wasm adder.asm.js 
(module
 (import "env" "memory" (memory $0 256 256))
 (import "env" "table" (table 0 0 anyfunc))
 (import "env" "memoryBase" (global $memoryBase i32))
 (import "env" "tableBase" (global $tableBase i32))
 (export "add" (func $add))
 (func $add (param $a i32) (param $b i32) (result i32)
  (return
   (i32.add
    (get_local $a)
    (get_local $b)
   )
  )
 )
)

今度のwastには、import式が入っている点に注目です。WebAssembly.Instanceの第二引数として、ここで指定された名前の位置に適切なデータを与える必要があります(func内で使ってなくても)。

  • 初期ブロック256、最大ブロック256のmemory
  • 初期長0、最大長0のtable

この"adder.wasm"を呼び出すJavaScriptコードは、以下のようになります。

//use-asmjs-adder.js
"use strict";

const fs = require("fs");

const buf = fs.readFileSync("./bitops.wasm");
const mod = new WebAssembly.Module(buf);
const imports = {
    env: {
        memory: new WebAssembly.Memory({initial: 256, maximum: 256}),
        table: new WebAssembly.Table({
            initial: 0, maximum: 0, element: "anyfunc"}),
        memoryBase: 0,
        tableBase: 0,
    },
};
const inst = new WebAssembly.Instance(mod, imports);

const {add} = inst.exports;
console.log(add(10, 20));

wastでの(import "env" "memory" (memory ...))は、実行時JavaScript側でimports.env.memoryWebAssembly.Memoryをセットしてある、という意味になります。tableも同様です。実質使っていないmemoryBasetableBase0にしています。

この一連のimportは、"asm.js"言語を成立させるための環境を構成するのに必要となる要素です。他の言語からwasmを生成する場合は、その言語環境のために埋め込まれたimportに、何を与えてやらなければいけないのかを把握し、それをJavaScript側で埋め合わせてやる必要があります。

例4: emscriptenからwasmを生成して使う

emscriptenは、llvmを使ってcやc++からJavaScriptコードに変換するシステムですが、その主要コード部分をasm.jsスタイルなコードで生成ができるようになり、さらにその部分をbinaryenによってWebAssemblyに置き換える事ができるように発展していった経緯があります。

emscriptenの設定

homebrewでインストールしたバイナリがそのままで使えますが、設定の調整が必須です。

$ brew install emscripten binaryen
$ emcc

最初にemscriptenのコマンド(Cコンパイラemccなど)を空実行すると"~/.emscripten"ファイル等が生成されます。

しかし、この"~/.emscripten"のうち、EMSCRIPTEN_ROOTLLVM_ROOTBINARYEN_ROOTが正しくないので、以下のように修正します(インストールしたパッケージのバージョンに注意)。

EMSCRIPTEN_ROOT = "/usr/local/Cellar/emscripten/1.37.10/libexec"
LLVM_ROOT = "/usr/local/Cellar/emscripten/1.37.10/libexec/llvm/bin"
BINARYEN_ROOT = "/usr/local/Cellar/binaryen/33"

ほかは実行可能コマンドのあるディレクトリだけど、BINARYEN_ROOTだけはbin/asm2wasmのあるベースの位置になるのにも注意です。

設定ファイル変更後の最初のemcc等を実行したときにはキャッシュ再生成するため、終了までにすこし時間がかかり、キャッシュ関係の余計な出力がでてきます。設定が間違っている場合はここでエラーや警告がでてきます。

adder.c

wasm化するためのCコードのadder.cです。

/* adder.c */
#include <emscripten.h>

EMSCRIPTEN_KEEPALIVE
int add(int a, int b) {
  return a + b;
}

EMSCRIPTEN_KEEPALIVEマクロは、emccのコマンドラインオプション-s EXPORTED_FUNCTIONS="['_add']"でも代用できますが、ここではオプションの単純化のためソースファイル内に埋め込みました。
(C言語での名前addは、ABIとしての外部名_addになり、外部JavaScriptからはこの_addを使います。)

emscriptenモジュールとしてWebAssembly利用

emscriptenは、C言語の機能を実現するための(malloc等の)システム環境を用意した上で、JavaScript上で実行させます。このため、WebAssemblyが使えると言っても、WebAssemblyオブジェクトを直接呼び出すのではなく、emscriptenで生成するemscriptenのモジュール(.jsファイル)として呼び出し、その内部の生成コード内で使われるという関係になります。

また、emscriptenのモジュールには非同期性があるので、JavaScript側から呼び出すには、モジュールへセットした初期化終了のコールバックonRuntimeInitializedのなかで関数を呼び出すことになります。

// use-emscripten.js
"use strict";

const adder = require("./adder.js");
adder.onRuntimeInitialized = () => {
    console.log(adder._add(10, 20)); // "add()"'s binary name is "_add"
};

このemscriptenモジュール"adder.js"を生成するコマンドは、以下のとおりです。

$ emcc adder.c -o adder.js -s WASM=1
$ ls
adder.c    adder.js     adder.wasm

-s WASM=1オプションを付けたemccコマンドによって"adder.js"と同時に"adder.wasm"も生成されます。

この"adder.wasm"を"wasm-dis"コマンドで変換してみるとわかりますが、emscriptenが生成したwasmファイルに含まれるimport式はかなり多く、自前でimportsオブジェクトを用意するのは難しいものになっています。importが多くなるのは、C標準ライブラリ実装のための部分が大きいからです。

別手法: ONLY_MY_CODEでwasm生成

しかし、この"addeer.c"では、標準ライブラリの機能を一切使用していません。そこで、-s ONLY_MY_CODE=1オプションをつかって、(ライブラリ部分を省いた)ソースにあるコードだけでwasmを生成するようにしてみます。

$ emcc adder.c -o adder.js -s WASM=1 -s ONLY_MY_CODE=1 -s SIDE_MODULE=1
$ ls
adder.c    adder.wasm

ちなみに、-s SIDE_MODULE=1は、外側のemscriptenモジュールjsファイルを生成しないオプションです。この"adder.wasm"の中身を"wasm-dis"でみると、

$ wasm-dis adder.wasm
(module
 (type $0 (func (param i32)))
 (type $1 (func (param i32 i32) (result i32)))
 (import "env" "STACKTOP" (global $import$0 i32))
 (import "env" "STACK_MAX" (global $import$1 i32))
 (import "env" "abortStackOverflow" (func $import$2 (param i32)))
 (import "env" "memory" (memory $0 256 256))
 (import "env" "table" (table 0 0 anyfunc))
 (import "env" "memoryBase" (global $import$5 i32))
 (import "env" "tableBase" (global $import$6 i32))
 (global $global$0 (mut i32) (get_global $import$0))
 (global $global$1 (mut i32) (get_global $import$1))
 (global $global$2 (mut f32) (f32.const 0))
 (export "_add" (func $0))
 ...

と、importは7つだけになりました。そして、この7つを自前で用意するコードは以下のようになりました。

//use-onlymycode.js
"use strict";

const fs = require("fs");

const buf = fs.readFileSync("./adder.wasm");
const mod = new WebAssembly.Module(buf);
const imports = {
    env: {
        STACKTOP: 4064,
        STACK_MAX: 5246944,
        abortStackOverflow: function (i32) {throw Error("stack oveflow");},
        memory: new WebAssembly.Memory({initial: 256, maximum: 256}),
        table: new WebAssembly.Table({
            initial: 0, maximum: 0, element: "anyfunc"}),
        memoryBase: 1024,
        tableBase: 0,
    },
};
const inst = new WebAssembly.Instance(mod, imports);
const {_add} = inst.exports;
console.log(_add(10, 20));

STACKTOPSTACK_MAXmemoryBasetableBaseはemscriptenモジュールの"adder.js"にあった値です。STACKTOPSTACK_MAXは生成コード内で使われているので、その関係を満たす必要があります。
もしTOPとMAXをともに0にすると_addからabortStackOverflowが呼ばれます。

例5: メモリで数値配列を扱うWASTプログラムを書く

ここまでは、WebAssembly環境調査のために単純な単数値のみを扱うadd関数のみで扱っていました。ここでは、WebAssembly言語自体の調査として、より高度な処理を書く導入のために、memoryをつかいループを行うプログラムを書いてみます。

"vector.wast"

メモリを用意し、メモリ上の2つの配列を足し合わせるaddToを行うモジュール"vector.wast"は、以下のようになります。

(module
    (memory $0 (export "heap") 1 256)
    (func (export "addTo") (param $a i32) (param $b i32) (param $len i32) (result i32)
        (loop $loop
            (if (i32.eqz (get_local $len))
                (return (i32.const 0)))
            (i32.store 
                (get_local $a)
                (i32.add (i32.load (get_local $a))
                         (i32.load (get_local $b))))
            (set_local $a (i32.add (get_local $a) (i32.const 4)))
            (set_local $b (i32.add (get_local $b) (i32.const 4)))
            (set_local $len (i32.sub (get_local $len) (i32.const 1)))
            (br $loop)
        )
    )
)

32ビット整数配列を指定した長さ部分だけ、ループで足し合わせるコードです。引数の$a$bにメモリ上のアドレス、$lenには数値配列の長さを想定します。

  • 現状、(memory)は一つだけ指定できます(ただし、binaryen/33では、ローカル名`$0'でないと失敗する)
  • 現状、シグネチャとして(result )は必ず数値一つ返す必要があります。
  • (loop label ...)は、内部の(br label)によって繰り返し処理します。(br)しなければ繰り返しません。
  • (i32.store address value)(i32.load address)のアドレスはメモリのバイトオフセットです。
  • メモリデータのバイトオーダーはJavaScriptのTypedArrayと同じ(リトルエンディアン)です。

これまで見てきたツール生成コードのようにimportするのではなく、メモリはexportして使うこともできます。他とメモリを共有しない単独のwastモジュールとしてはexportのほうが使いやすいでしょう。

ちなみに、WebAssemblyの各命令の呼びだしかたは、WebAssemblyのtestsuiteの例を参考に類推しました。

同様に、"wasm-as"コマンドでwasmファイルに変換します。

$ wasm-as vector.wast > vector.wasm

"use-vector.js"

このvector.wasmを呼び出すコードは以下のようになります。

// use-vector.js
"use strict";

const fs = require("fs");

const buf = fs.readFileSync("./vector.wasm");
const mod = new WebAssembly.Module(buf);
const imports = {};
const inst = new WebAssembly.Instance(mod, imports);
const {heap, addTo} = inst.exports;

const len = 8;
const a = 0 * Int32Array.BYTES_PER_ELEMENT,
      b = 8 * Int32Array.BYTES_PER_ELEMENT;
const memA = new Int32Array(heap.buffer, a, len);
const memB = new Int32Array(heap.buffer, b, len);
for (let i = 0; i < len; i++) memA[i] = i + 1;
for (let i = 0; i < len; i++) memB[i] = (i + 1) * 10;

addTo(a, b, len);
console.log(memA); //=> [11, 22, 33, 44, 55, 66, 77, 88]
console.log(memB); //=> [10, 20, 30, 40, 50, 60, 70, 80]

importもしくはexportしたWebAssembly.MemoryオブジェクトのbufferプロパティがArrayBufferであるので、JavaScript側でそこから処理させる型にあったTypedArrayに切り出して使えます。

追記: WebAssembyは速い?のか

なぜWebAssemblyはasm.jsより速いのか」という記事がありますが、注意して読めば、その「速い」は条件付きでの「速い」であるいう話なのです:

  • コードのファイルサイズが小さい => 起動時だけ速い
  • 64ビット整数演算ができる => 64ビット整数値を使わないなら同じ
  • メモリオフセットが使える => (構造体無しで)数値配列を使うだけなら同じ
  • popcntやcopysignが使える => ビット演算しないなら同じ
  • emscriptenが賢くなった => 最初からコンパクトなコードなら同じ

という意味です。

ということで、実際に比較可能な数値配列のみのコードを書いてみました。

ES6コードはmap/reduceを多用する関数型スタイルのコードで、これがベースとなっています。wasmやasm.jsのコードは、この畳込み部分をメモリを使うように差し替えたものです。

ベンチマークとして、セルオートマトンの次ステップ生成の時間を図っています。初期化時間は計測していません。結果は、以下の通りでした。

firefox-54 chrome-59
ES6 70ms 240ms
wasm 80ms 130ms
asm.js 80ms 210ms

記事にあるようにfirefoxではasm.jsコードとwasmコードでは速度にほぼ差がないようです。ただし、Arrayのmapreduceメソッドを使ったコードのほうが若干速いです。最初のTypedArrayのsetをしないコードでnext全体を移植すれば早くなるように見えますが、実行する計算の数から考えれば結果はほぼ変わりません。実際にnext()呼び出し全体を囲って図ったとしてもほぼ同じ結果になりました(chromeも含めて)。

確かにchromeではwasmコードが最速でしたが、ただこれはv8でArrayのインデックスアクセスがかなり高速化されたのに、mapreduce等のメソッドがいまだ高速化されていない、という現状によるものです。ここ2年のchromeとfirefoxの速度の関係はこのようなものです。

ただ、wasmでは実行速度面でのブラウザ間の違いが小さい、とは言えるでしょう。

まとめ

  • WebAssemblyは、JavaScriptから呼び出す実行環境で、JavaScriptエンジンが備える機能である
  • WebAssemblyプログラムは、バイナリフォーマットwasmとテキストフォーマットwastがある
  • WebAssemblyプログラムはmodule構造である
  • wasm moduleにはimportsとexportsがあり、JavaScript側ではimportsを用意し、exportsを利用する
  • wasm moduleは、JavaScriptからは非同期でロードするものであるが、nodejsでは同期ロードも可能である
  • WebAssemblyには、JavaScriptにはないビット演算や64ビット整数演算が存在する
  • 64ビット整数のシグネチャを持つ関数は、JavaScript側から直接呼び出せない
  • 他プログラミング言語から変換したwasmには、その言語環境固有のimportsを用意する必要がある
  • emscriptenでのWebAsemblyは、基本emscripten moduleの内部で利用し、直接触らないものである
  • 追記: WebAssemblyだからといって劇的に早くなるわけじゃないが、ブラウザ間の速度差は小さい
14
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
10