スキップしてメインコンテンツへ

WebAssemblyモジュールがデータを安全に交換する方法

2021年6月24日お知らせ

By Marco Fioretti

WebAssembly binary format (Wasm) は、任意の言語で記述されたソフトウェアが、Webブラウザまたはスタンドアロン仮想マシン内で「一度コンパイルすればどこでも実行できる」ように開発されました(ランタイム)どのプラットフォームでも利用でき、それらのプラットフォーム用に直接コンパイルされたコードとほぼ同じ速度です。 Wasmモジュールは、そのおかげで、本当にポータブルな方法で実行される任意のホスト環境と対話できます。 WebAssembly System Interface (WASI).

しかし、それだけでは十分ではありません。実際に使えるようにするために 驚きなし 可能な限り多くのシナリオで、Wasm実行可能ファイルには少なくとも2つのものが必要です。 1つは、オペレーティングシステムだけでなく、同じ種類の他のプログラムとも直接対話する機能です。 Wasmでこれを行う方法は次のように呼ばれます 「モジュールリンク」、そしてこのシリーズの次の記事のトピックになります。モジュールリンクが役立つための前提条件である他の機能は、交換する機能です。 データ構造 誤解やデータの損失なしに、あらゆる種類の。

Wasmモジュールがデータを交換するとどうなりますか?

これはコンパイルターゲットにすぎないため、WebAssembly形式は、基盤となるマシンにできるだけ近づけることを目的とした低レベルのデータ型のみを提供します。プログラマーが好きな言語でソフトウェアを書くことを任せながら、移植性が高く、パフォーマンスの高いモジュールを提供するのはこの選択です。その言語の複雑なデータ構造をネイティブのWasmデータ型にマッピングする負担は、ソフトウェアライブラリとそれらを使用するコンパイラに委ねられています。

ここでの問題は、効率を上げるために、第1世代のWasm構文とWASIが、文字列やその他の同様に基本的なデータ型をネイティブにサポートしていないことです。したがって、たとえば、PythonソースからコンパイルされたWasmモジュールとRustソースからコンパイルされたWasmモジュールが、文字列が使用されるすべての状況でまったく同じ「文字列」の概念を持つという本質的な保証はありません。

その結果、異なる言語からコンパイルされたWasmモジュールがより複雑なデータ構造を交換したい場合、あるデータが1つのモジュールから別のモジュールに移動するたびに、いわば「翻訳が失われる」ことが重要になる可能性があります。具体的には、これにより、Wasmモジュールを汎用アプリケーションに直接埋め込むことと、Wasmモジュールから外部ソフトウェアを直接呼び出すことの両方が防止されます。

問題の性質を理解するために、そのようなデータが第1世代のWasmおよびWASIモジュールでどのように渡されるかを調べることは有用です。

WebAssemblyがJavaScriptおよびCプログラムと通信するための元の方法は、文字列などを次のようにシミュレートすることです。 手動で管理する メモリのチャンク。

たとえば、関数で path_open、文字列は、Wasmモジュールに予約されている線形メモリ内のオフセットとその文字列の長さをそれぞれ表す整数のペア(i32)として渡されます。これは、最も単純で最も頻繁なケース、異なる文字エンコード、または ガベージコレクション(GC) 使用されています。さらに悪いことに、文字列を交換するWASIモジュールは、互いのメモリにアクセスすることを余儀なくされ、パフォーマンスとセキュリティの両方の理由から、この方法が最適とはほど遠いものになります。

理論的には、データを交換したいWasmモジュールは、次のような従来のJavaScript互換のデータ受け渡しメカニズムも使用できます。 WebIDL。これは、もちろん、すべてのコンポーネントを記述するために使用されるインターフェイス記述言語です。 データ型 任意のWebアプリケーションプログラミングインターフェイス用(API).

ただし、実際には、これでは何も解決されません。まず、Web IDL関数が受け入れることができるため、つまり、それらを呼び出したWasmモジュールに返されます。 より高いレベルの構成 WebAssemblyが理解するよりも。 2つ目は、WebAssemblyを使用するということは、直接ではなく、を介してデータを交換することを意味するためです。 ECMAScriptバインディング、独自の複雑さとパフォーマンスのペナルティがあります。要約すると、特定のトリックは今日は機能しますが、すべての場合に機能するわけではなく、決して将来性があるわけではありません。

解決策:インターフェースと参照型

上記のすべての問題に対する実際の解決策は、Wasmバイナリ形式とWASIの両方を次のように拡張することです。

  • 文字列やリストなどのより複雑なデータ構造を直接サポートする
  • Wasmモジュールが対応する変数を静的に型チェックし、それらを直接交換できるようにしますが、内部の線形メモリを共有する必要はありません。

この目的のためだけに展開されている2つの仕様があります。メインのものは単に呼ばれます インターフェイスタイプ とその仲間 参照型.

どちらのタイプも、元のWasmコアにすでに追加されている低レベルの機能に依存しています。 「マルチバリュー」マルチメモリのサポート。最初の拡張機能により、Wasm関数は以前のように1つだけではなく、任意の数の値を返すことができ、Wasm命令シーケンスは任意の数のスタック値を処理できます。もう1つは、Wasmモジュール全体、または単一の関数が同時に複数のメモリを使用できるようにするためです。 たくさんの理由 変数の交換に加えて。

これらの機能に基づいて、インターフェイスタイプは、文字列やその他の「高レベル」のデータ構造を次のように定義します。 変更なし Wasmランタイムが使用できます。参照型は全体像を完成させ、Wasmアプリケーションが実際にそれらのデータ構造を外部アプリケーションと交換する方法を指定します。

仕様はまだ完全には完成していません。インターフェイスタイプは値を交換できますが、交換できません リソースへのハンドルバッファ、たとえば、 「ファイルを読み取り、バッファに直接書き込む」.

ただし、ここで説明するすべての機能を連携させることで、WasmモジュールとWASIインターフェイスは、Wasmにコンパイルする前に、それらを破損することなく、使用されている言語に関係なく、最も複雑なデータ構造を効率的に処理および交換できます。

Linux Foundationのトレーニングと認定に関心をお寄せいただきありがとうございます。私たちは、中国のトレーニングサイトからより良いサービスを提供できると考えています。このサイトにアクセスするには、以下をクリックしてください。

Linux Foundationのカルチャに対するフィードバックは、より適切に、中国のカルチャウェブサイトに反映されることを期待しています。