WebAssembly Threads を Chrome 70 で試用可能

WebAssembly スレッドのサポートは、Chrome 70 でオリジン トライアルとしてリリースされています。

Alex Danilo

WebAssembly(Wasm)を使用すると、C++ や他の言語で記述されたコードをコンパイルしてウェブで実行できます。ネイティブ アプリの非常に便利な機能の 1 つは、並列計算のプリミティブであるスレッドを使用できることです。ほとんどの C および C++ デベロッパーは、アプリケーションのスレッド管理用に標準化された API である pthreads を使い慣れているでしょう。

WebAssembly コミュニティ グループは、スレッドをウェブに導入して、実際のマルチスレッド アプリケーションを実現すべく取り組んでいます。その一環として、V8 では WebAssembly エンジンのスレッドに必要なサポートが実装され、オリジン トライアルを通じて提供されています。オリジン トライアルを利用すると、デベロッパーは新しいウェブ機能が完全に標準化される前に試すことができます。これにより、意欲的なデベロッパーから実際のフィードバックを収集できます。これは、新機能の検証と改善に欠かせません。

Chrome 70 リリースは WebAssembly のスレッドをサポートしています。関心をお持ちのデベロッパーの方は、ぜひこのスレッドを使用してフィードバックをお寄せください。

Threads とワーカーの場合

ブラウザは、2012 年から Chrome 4 で Web Worker を介した並列処理をサポートしています。「メインスレッド上」などの用語を実際に耳にするのは一般的です。ただし、Web Worker は、可変データをデバイス間で共有せず、通信をメッセージの受け渡しに依存します。実際、Chrome は各エントリに新しい V8 エンジンを割り当てます(アイソレーションと呼ばれます)。分離は、コンパイル済みコードも JavaScript オブジェクトも共有しないため、pthread などの可変データは共有できません。

一方、WebAssembly スレッドは、同じ Wasm メモリを共有できるスレッドです。共有メモリの基礎となるストレージは、SharedArrayBuffer で実現されます。これは、ワーカー間で単一の ArrayBuffer のコンテンツを同時に共有できる JavaScript プリミティブです。各 WebAssembly スレッドは Web Worker で実行されますが、共有 Wasm メモリにより、ネイティブ プラットフォームの場合と同様に動作します。つまり、Wasm スレッドを使用するアプリケーションは、従来のスレッド化されたアプリケーションと同様に、共有メモリへのアクセスを管理する責任があります。pthreads を使用する既存のコード ライブラリは C または C++ で数多くあります。これらは Wasm にコンパイルしてトゥルー スレッド モードで実行できるため、より多くのコアで同じデータを同時に処理できます。

簡単な例

スレッドを使用する単純な「C」プログラムの例を以下に示します。

#include <pthread.h>
#include <stdio.h>

// Calculate Fibonacci numbers shared function
int fibonacci(int iterations) {
    int     val = 1;
    int     last = 0;

    if (iterations == 0) {
        return 0;
    }
    for (int i = 1; i < iterations; i++) {
        int     seq;

        seq = val + last;
        last = val;
        val = seq;
    }
    return val;
}
// Start function for the background thread
void *bg_func(void *arg) {
    int     *iter = (void *)arg;

    *iter = fibonacci(*iter);
    return arg;
}
// Foreground thread and main entry point
int main(int argc, char *argv[]) {
    int         fg_val = 54;
    int         bg_val = 42;
    pthread_t   bg_thread;

    // Create the background thread
    if (pthread_create(&bg_thread, NULL, bg_func, &bg_val)) {
        perror("Thread create failed");
        return 1;
    }
    // Calculate on the foreground thread
    fg_val = fibonacci(fg_val);
    // Wait for background thread to finish
    if (pthread_join(bg_thread, NULL)) {
        perror("Thread join failed");
        return 2;
    }
    // Show the result from background and foreground threads
    printf("Fib(42) is %d, Fib(6 * 9) is %d\n", bg_val, fg_val);

    return 0;
}

このコードは、2 つの変数 fg_valbg_val を宣言する main() 関数で始まります。この例では、両方のスレッドから呼び出される fibonacci() という関数もあります。main() 関数は、pthread_create() を使用してバックグラウンド スレッドを作成します。このスレッドのタスクは、bg_val 変数の値に対応するフィボナッチ数列値を計算することです。一方、フォアグラウンド スレッドで実行されている main() 関数は、fg_val 変数について計算します。バックグラウンド スレッドの実行が完了すると、結果が出力されます。

スレッドをサポートするためのコンパイル

まず、emscripten SDK(バージョン 1.38.11 以降が望ましい)をインストールする必要があります。ブラウザで実行可能なスレッドを有効にしてサンプルコードをビルドするには、追加のフラグを emscripten emcc コンパイラに渡す必要があります。コマンドラインは次のようになります。

emcc -O2 -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=2 -o test.js test.c

コマンドライン引数「-s USE_PTHREADS=1」はコンパイル済みの WebAssembly モジュールのスレッドサポートを有効にし、引数「-s PTHREAD_POOL_SIZE=2」は 2 つのスレッドのプールを生成するようコンパイラに指示します。

プログラムを実行すると、内部で WebAssembly モジュールが読み込まれ、スレッドプール内のスレッドごとに Web Worker が作成され、モジュールが各ワーカーと共有されます(この場合は 2)。これらは pthread_create() の呼び出しが行われるたびに使用されます。各ワーカーは同じメモリを使用して Wasm モジュールをインスタンス化し、連携できるようにします。7.0 での V8 の最新の変更では、ワーカー間で渡される Wasm モジュールのコンパイル済みネイティブ コードを共有するため、非常に大規模なアプリでも多数のワーカーにスケーリングできます。スレッドプール サイズが、アプリケーションに必要な最大スレッド数と同じになるようにしてください。そうしないと、スレッドの作成が失敗する可能性があります。同時に、スレッドプールのサイズが大きすぎると、メモリのみを使用するだけで不必要なウェブワーカーが作成されてしまいます。

使い方

WebAssembly モジュールをテストする最も簡単な方法は、Chrome 70 以降で試験運用版の WebAssembly スレッドのサポートを有効にすることです。次のように、ブラウザで URL about://flags に移動します。

Chrome フラグのページ

次に、次のような試験運用版の WebAssembly スレッド設定を見つけます。

WebAssembly スレッドの設定

以下に示すように設定を [有効] に変更してから、ブラウザを再起動します。

WebAssembly スレッド設定が有効

ブラウザが再起動したら、次のコンテンツのみを含む最小限の HTML ページで、スレッド形式の WebAssembly モジュールの読み込みを試すことができます。

<!DOCTYPE html>
<html>
  <title>Threads test</title>
  <body>
    <script src="test.js"></script>
  </body>
</html>

このページを試すには、なんらかの形でウェブサーバーを実行し、ブラウザから読み込む必要があります。これにより、WebAssembly モジュールが読み込まれて実行されます。DevTools を開くと、実行からの出力が表示され、コンソールに以下の出力画像が表示されているはずです。

フィボナッチ プログラムからのコンソール出力

スレッドを使用した WebAssembly プログラムが正常に実行されました。前述の手順を使用して、独自のスレッド アプリケーションを試すことをおすすめします。

オリジン トライアルによる現場でのテスト

開発目的の場合は、ブラウザで試験運用版のフラグをオンにしてスレッドを試すこともできますが、フィールドでアプリケーションをテストする場合は、「オリジン トライアル」と呼ばれる方法を使用できます。

オリジン トライアルでは、ドメインに関連付けられたテストトークンを取得することで、ユーザーと一緒に試験運用版の機能を試すことができます。その後、アプリをデプロイして、テスト中の機能をサポートするブラウザ(この場合は Chrome 70 以降)で動作することを期待できます。オリジン トライアルを実施するための独自のトークンを取得するには、こちらの申請フォームを使用します。

ここでは、オリジン トライアル トークンを使用して簡単な例を用意しました。そのため、何もビルドせずにご自身で試すことができます。

並行して実行される 4 つのスレッドが ASCII アートに対してできることを確認するには、こちらのデモもお読みください。

フィードバックをお寄せください

WebAssembly スレッドは、アプリケーションをウェブに移植するのに非常に便利な新しいプリミティブです。pthreads のサポートを必要とする C / C++ のアプリケーションとライブラリを WebAssembly 環境で実行できるようになりました。

この機能を試していただいたデベロッパーの皆様からのフィードバックは、標準化プロセスに役立つだけでなく、その有用性の検証にも役立てさせていただきます。フィードバックを送信するための最善の方法は、問題を報告するか、WebAssembly Community Group標準化プロセスに関与することです。