workerd は、高性能な JavaScript と WebAssembly (WASM) の実行エンジンで、V8 エンジンを活用してスクリプトや WASM モジュールを処理します。本記事では、workerd がクライアントから受け取った WASM モジュールをどのようにしてコンパイルし、実行するのか、その流れを解説します。
クライアントから送信されたリクエストに含まれる WASM モジュールを workerd は受け取ります。このモジュールはサーバーの設定(config::Worker::Module)として登録され、後のステップでコンパイル・実行されます。
次に、workerd は WASM モジュールをコンパイルして、V8 エンジン上で実行できる形に変換します。このプロセスは compileWasmGlobal 関数によって実行されます。
static v8::Local<v8::WasmModuleObject> compileWasmGlobal(JsgWorkerdIsolate::Lock& lock,
capnp::Data::Reader reader,
const jsg::CompilationObserver& observer) {
lock.setAllowEval(true);
KJ_DEFER(lock.setAllowEval(false));
AllowV8BackgroundThreadsScope scope;
return jsg::compileWasmModule(lock, reader, observer);
}この関数では、WASM モジュールを V8 エンジン用にコンパイルします。初回のコンパイルは Liftoff という高速なコンパイラを使い、即座に実行可能な状態にします。その後、バックグラウンドスレッドを使用して最適化されたコンパイルが進行し、次回以降のリクエスト時にはさらに高速な実行が可能になります。
コンパイルされた WASM モジュールは、JavaScript のスクリプトや他のサーバーコードからアクセスできるように、グローバル変数としてバインディングされます。この処理は compileScriptGlobals 関数によって行われます。
kj::Array<Worker::Script::CompiledGlobal> WorkerdApi::compileScriptGlobals(
jsg::Lock& lockParam,
config::Worker::Reader conf,
Worker::ValidationErrorReporter& errorReporter,
const jsg::CompilationObserver& observer) const {
for (auto binding : conf.getBindings()) {
if (binding.isWasmModule()) {
auto name = lock.str(binding.getName());
auto value = Impl::compileWasmGlobal(lock, binding.getWasmModule(), observer);
compiledGlobals.add(Worker::Script::CompiledGlobal{
{lock.v8Isolate, name},
{lock.v8Isolate, value},
});
}
}
return compiledGlobals.finish();
}このコードでは、サーバー設定からバインディングされた WASM モジュールを確認し、モジュールをグローバルスコープにバインドします。これにより、WASM モジュールがスクリプト内でグローバル変数として利用できるようになります。
バインディングが完了すると、クライアントからのリクエストに応じて WASM モジュールが実行されます。V8 エンジンがこの実行を管理し、必要に応じて最適化されたコードが利用されます。WASM モジュールは JavaScript や他のコードと統合されて動作し、必要な処理を実行します。このプロセスの中で、最初のリクエストに対しては初期コンパイル済みのモジュールが実行され、次回以降のリクエストでは最適化された実行が行われます。
workerd はバックグラウンドで WASM モジュールを最適化して再コンパイルします。この処理により、初回リクエスト時に最小限の遅延でレスポンスを返し、次回以降のリクエストに対して最適なパフォーマンスを提供します。サーバーレスアーキテクチャのような環境では、コールドスタートが発生する場合もありますが、workerd は高速なコンパイルとバックグラウンド最適化により、コールドスタートの影響を最小限に抑えています。
以下は、workerd が WASM モジュールを受け取って処理する流れの図解です。
+-------------------+ +------------------------+
| Client Request | | Workerd Server |
+-------------------+ +------------------------+
| |
| Send WASM Module |
v v
+-------------------+ +------------------------+
| Receive WASM | --------> | CompileWasmGlobal() |
+-------------------+ +------------------------+
| |
v |
+-------------------+ +------------------------+
| Compile WASM | --------> | Bind to Global Scope |
+-------------------+ +------------------------+
| |
v v
+-------------------+ +------------------------+
| Execute WASM with | <-------- | Optimize in Background |
| V8 Engine | +------------------------+
+-------------------+
workerd は、クライアントから送信された WASM モジュールを効率的にコンパイル・実行するために V8 エンジンを活用しています。初期コンパイルから最適化までの流れは、サーバーのパフォーマンスを最大限に活かすために設計されており、コールドスタート時にも迅速な処理が行われます。WASM モジュールのコンパイルと実行のプロセスは、高速化と最適化を両立しており、WebAssembly を活用したアプリケーションのパフォーマンス向上に大きく寄与しています。