WebAssembly - Node.jsとの連携

こんにちは、未来のプログラマーたち!今日は、WebAssemblyとNode.jsの世界に一緒に飛び込んでみましょう。これらの用語が未知の言語のように聞こえるかもしれませんが、このチュートリアルの終わりまでに、あなたはこれらを流暢に話すことができるようになるでしょう!

WebAssembly - Working with Nodejs

WebAssemblyとは?

WebAssembly、しばしばWasmと略されますが、プログラミング世界のスーパーヒーローです。C、C++、Rustなどの言語で書かれたコードを、ウェブブラウザでほぼネイティブスピードで実行できるバイナリ命令書式です。ブラウザ内で複雑な3Dゲームをプレイできるようなパワフルな機能がWebAssemblyにもたらされます!

なぜNode.js?

さて、あなたはきっと思うでしょう、「これにはNode.jsはどう関係があるんだ?」。実は、Node.jsは魔法を起こすバックステージのクルーのような存在です。JavaScriptのランタイムで、ウェブブラウザの外でJavaScriptを実行できるようにしてくれます。WebAssemblyとNode.jsを組み合わせることで、WasmのスピードとNode.jsの多様性を兼ね備えた環境が得られます。

環境の設定

コードに取り組む前に、まずは作業環境を整えましょう。心配しないでください、新しいスマートフォンを設定するよりも簡単です!

  1. 公式サイトからNode.jsをインストールします(https://nodejs.org
  2. ターミナルまたはコマンドプロンプトを開きます
  3. プロジェクト用の新しいディレクトリを作成します:
    mkdir wasm-nodejs-tutorial
    cd wasm-nodejs-tutorial
  4. 新しいNode.jsプロジェクトを初期化します:
    npm init -y

素晴らしい!これでコーディングを始める準備が整いました。

最初のWebAssemblyモジュール

簡易的なWebAssemblyモジュールを作成して、二つの数を足すものを作ってみましょう。C言語で書いて、WebAssemblyにコンパイルします。

ステップ1: Cコードの作成

以下の内容でadd.cというファイルを作成します:

#include <emscripten.h>

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

これが象形文字のようでパニックに陥るかもしれませんが、説明します:

  • #include <emscripten.h>は、CをWebAssemblyにコンパイルするのに役立つEmscriptenライブラリをインクルードします。
  • EMSCRIPTEN_KEEPALIVEは、この関数をJavaScriptからアクセス可能にする特殊なディレクティブです。
  • int add(int a, int b)は、二つの整数を受け取り、その和を返す関数です。

ステップ2: WebAssemblyへのコンパイル

このCコードをWebAssemblyにコンパイルするために、Emscriptenをインストールする必要があります。Emscriptenのウェブサイト(https://emscripten.org/docs/getting_started/downloads.html)のインストール手順に従ってください

インストールが完了したら、以下のコマンドを実行します:

emcc add.c -s WASM=1 -s EXPORTED_FUNCTIONS='["_add"]' -o add.js

このコマンドは、私たちのCコードをWebAssemblyにコンパイルし、add.wasmadd.jsという二つのファイルを作成します。

Node.jsでのWebAssemblyの使用

さあ、エキサイティングな部分です。Node.jsで私たちのWebAssemblyモジュールを使ってみましょう!

以下の内容でindex.jsというファイルを作成します:

const fs = require('fs');
const path = require('path');

const wasmBuffer = fs.readFileSync(path.join(__dirname, 'add.wasm'));

WebAssembly.instantiate(wasmBuffer).then(wasmModule => {
const add = wasmModule.instance.exports._add;
console.log('5 + 3 =', add(5, 3));
});

これを分解します:

  1. 必要なNode.jsモジュールをインポートします:fsはファイルシステム操作に、pathはファイルパス操作に使います。
  2. WebAssemblyファイルを読み込んでバッファーに格納します。
  3. WebAssembly.instantiate()を使ってWebAssemblyモジュールを読み込み、コンパイルします。
  4. 読み込みが完了すると、wasmModule.instance.exports._addを通じて私たちのadd関数にアクセスできます。
  5. 最後に、関数を呼び出して結果をログにします。

このスクリプトを実行するには:

node index.js

すべてが正しく動作した場合、以下のように表示されるはずです:5 + 3 = 8

おめでとうございます!あなたは初めてのWebAssemblyモジュールをNode.jsで実行しました!

性能比較

さて、私たちのWebAssembly関数とネイティブのJavaScript関数の性能を比較してみましょう。

以下をindex.jsに追加します:

function jsAdd(a, b) {
return a + b;
}

const iterations = 1000000;

console.time('WebAssembly');
for (let i = 0; i < iterations; i++) {
add(5, 3);
}
console.timeEnd('WebAssembly');

console.time('JavaScript');
for (let i = 0; i < iterations; i++) {
jsAdd(5, 3);
}
console.timeEnd('JavaScript');

このコードは、WebAssembly版とJavaScript版のadd関数を100万回実行し、それぞれの実行時間を計測します。

スクリプトを再実行すると、WebAssembly版がより早いと感じるでしょう!

結論

私たちはWebAssemblyとNode.jsの可能性の一部しか触れていません。考えてみてください、CやRustで書かれた複雑なアルゴリズムやゲームエンジン、甚至是非のアプリケーションが、ほぼネイティブスピードでNode.jsで動作するなど、どんな可能性があるか!

コードを学ぶことは、自転車に乗るのと似ています。最初は不安定かもしれませんが、練習を積むことですぐにスムーズに乗れるようになります。実験を続け、学び続け、そして何より楽しみましょう!

以下に、私たちが使用した主なメソッドをまとめた表を示します:

メソッド 説明
WebAssembly.instantiate() WebAssemblyモジュールをコンパイルおよびインスタンス化します
fs.readFileSync() ファイルを同期的に読み込みます
path.join() パスセグメントを結合します
console.time() タイマーを開始します
console.timeEnd() タイマーを終了し、経過時間をログします

未来のWebAssemblyウィザードたち、快適なコーディングを!

Credits: Image by storyset