※この記事は New WebAssembly Instrumentation の内容を引用しています
背景
---
Internet Computer (IC) は、汎用アプリケーションをホストするブロックチェーン ベースのプラットフォームです。各アプリケーションは WebAssembly (Wasm) 仮想マシンで実行され、セキュリティ、安全性、パフォーマンス、決定論が確保されます。
IC 上のアプリケーション ライフサイクルには、次の手順が含まれます。
開発。開発者は、サポートされている高級言語 (Rust、Motoko、Typescript、Python など) と対応する Canister Development Kit ライブラリ (CDK) を使用して、汎用アプリケーションを実装します。
Wasm のコンパイルと展開。アプリケーションのソース コードはコンパイルされ、CDK ライブラリにリンクされます。結果として得られる Wasm バイナリは IC に展開され、キャニスターと呼ばれます。
デプロイされると、キャニスターはユーザーや他のキャニスターからのメッセージを受信できるようになります。最初のメッセージ実行の前に、新しくデプロイされた Wasm バイナリはさらに 2 つの変換を行う必要があります。
インストルメンテーション。インターネット コンピュータは、実行された命令の数をカウントするために、システム レベルのコードを Wasm バイナリに挿入します。
ネイティブ コンパイル。最後に、インストルメント化された Wasm バイナリはネイティブ x86 コードにコンパイルされ、ネイティブに近いアプリケーション パフォーマンスを実現します。
それでは、計測プロセスについて詳しく見ていきましょう。
インターネット コンピュータは、実行された命令の数をカウントする小さなコード スニペットを挿入して Wasm バイナリを計測します。これは、キャニスターの実行が終了し、キャニスターが適切に充電されていることを確認するために必要です。実行された命令の数は、他のブロックチェーンではプログラム カウンターとも呼ばれます。
実行されたすべての命令は正確にカウントされる必要があるため、計測手順はキャニスターのパフォーマンスとインターネット コンピュータのブロック レートの一貫性にとって重要です。
古い Wasm インストルメンテーション アルゴリズムは適切に機能しますが、言語インタープリタなどの特定の種類のアプリケーションには非効率的です。新しい Wasm インストルメンテーションはこれらの非効率性に対処し、そのようなアプリケーションで桁違いのパフォーマンスを実現します。これにより、開発者はキャニスターをより効率的かつ安価に実行できます。
古い計測アルゴリズム
---
古いインストルメンテーション アルゴリズムの高レベルな考え方は、「直線コード」(基本ブロック) を検出し、各基本ブロックの先頭で命令カウンターを更新するためにいくつかの Wasm 命令を挿入することです。関数やループなどの再入可能ブロックの場合、アルゴリズムは命令外条件のチェックも追加します。
古い計測アルゴリズムは、インターネット コンピュータ ジェネシス以前に作成されました。当時は燃料/ガスを測定する他の方法がなかったためです。
-問題のあるケース-
問題1: 過大評価
---
古いインストルメンテーション アルゴリズムでは、制御フローが複数のブロックに渡って転送される場合を適切に処理できません。このアルゴリズムは、実行される命令の数を過大評価します。
オリジナルの Wasm コード:
(ブロック $b1
(ブロック $b2
br $b1
)
[N 個の他の命令]
)
インストルメント化された Wasm コード:
(ブロック $b1
> instruction_counter -= N
(ブロック $b2
> instruction_counter -= 1
br $b1
)
[N 個の他の命令]
)
上記の例では、命令はN 個の命令を飛び越えてbrブロックの末尾に制御を移します。ただし、アルゴリズムは N 個の命令をブロックの一部と見なし、ブロックの先頭で考慮します。多くの言語はネストされたブロックとラベル付きブランチを使用して式を実装するため、実際には過大評価が一般的であると考えられます。 $b1$b1switch/match
この問題の根本的な原因は、アルゴリズムが基本ブロックを誤って検出したことです。「直線コード」の重要な特性は、ブロック全体を無条件に実行する必要があることです。この特性は、上記の例で示されているように当てはまりません。
問題2: パフォーマンス
---
基本ブロックには、単一のエントリ ポイントと単一の終了ポイントがあります。と呼ばれるWasm 命令blockがありますが、この命令は「直線コード」の始まりも終わりもマークしません。古いアルゴリズムでは、このblock命令が誤って基本ブロックの始まりとして扱われていました。
はグローバル変数に格納されるためinstruction_counter 、更新には 2 回のメモリ アクセス (1 回のロードと 1 回のストア) が必要です。古いインストルメンテーションでは、各blockWasm 命令の後にこれらが誤って挿入されていました。これは、ネストされた 's を多数含む一部のアプリケーションに大きな悪影響を及ぼしましたblock。
問題3: スケジュールと公平性
---
従来のインストルメンテーションでは、すべての命令のコストは同等として扱われていましたが、実際の命令のコストは命令の種類によって異なります。たとえば、除算は加算よりもコストがかかるため、Wasm 命令の数が同じであっても、除算のコストを高く設定するのは妥当でしょう。
また、すべての命令に対して均等に課金することは、インターネット コンピュータ スケジューラにとって大きな課題となります。決定論的であるためには、ラウンドごとに同じ数の命令をスケジュールしようとします。これらの命令のほとんどが「軽い」場合、ラウンドは早く終了しますが、ほとんどの命令が「重い」場合、ユーザーは予期しない遅延に気付く可能性があります。
新しい計測アルゴリズム
---
問題 1 (過大評価) と問題 2 (パフォーマンス) に対処するために、NNS による投票のための新しい計測アルゴリズムが提案されました。このアルゴリズムは、基本ブロック (「直線コード」) を適切に検出し、メモリ アクセスの数を減らします。
具体的には、新しいインストルメンテーション アルゴリズムは次のように動作します。
各再入可能ブロック (関数エントリまたはループ命令) の先頭に、次のスニペットを挿入します。
instruction_counter = instruction_counter — N
if instruction_counter < 0
call out_of_instructions()
instruction_counter次のブロックの開始まで、このブロック内の最上位レベルの命令の数(他のブロックにネストされていない)だけ減算します。
カウンターがゼロを下回った場合は、out_of_instructions() ハンドラーを呼び出して、一時停止 (DTS を使用) するか、実行を終了します。
各基本ブロックの先頭(各if, else, br, br_if, br_table, end, return, unreachable, return_call, return_call_indirect 命令の後)に以下を挿入します。 instruction_counter = instruction_counter — N
instruction_counter次のブロックの開始まで、このブロック内の最上位レベルの命令の数(他のブロックにネストされていない)だけ減算します。
古いインストルメンテーション アルゴリズムと新しいインストルメンテーション アルゴリズムを簡単に比較してみましょう。以下は以前に見た例です。
古い Wasm の計測機器
(ブロック $b1
> instruction_counter -= N
(ブロック $b2
> instruction_counter -= 1
br $b1
)
[N 個の他の命令]
)
新しい Wasm 計測
(ブロック $b1
(ブロック $b2
br $b1
)
> instruction_counter -= N
[N 他の命令]
)
新しいアルゴリズムでは、ブロック命令は「ストレインライン コード」の始まりでも終わりでもないため、各ブロック命令の後にデクリメントは行われず、したがってこの例ではメモリ アクセスの回数が半分になります。これは微妙な違いのように思えるかもしれませんが、多くの言語インタープリタは、インタープリタ ループのまさに中心で非常に複雑な match/switch ステートメントを使用します。
詳細については、 github.com/dfinity/icinstrument() の関数ソース コードを参照してください。
費用の調整
---
各 Wasm 命令には、ブロックチェーンの世界では標準的な慣例である「重み」が割り当てられます。通常、命令ごとにコストが異なり、たとえば、イエローペーパーで指定されているように、EVM のオペコードごとにガス コストが異なります。そのため、問題 3 (ブロック レートと公平性) に対処するために、計測を再調整しながら、各 Wasm 命令に新しい「重み」が割り当てられました。
これにより、命令コスト モデルが不均一になり、特定のワークロードの合計サイクル消費量に影響する可能性があります。この問題に対処するために、コミュニティに変更を伝え、DFINITY チームと内部的に連携し、テストを行うために多大な労力が費やされました。
各 Wasm 命令に正しい重みを割り当てるために、ほとんどの命令のベンチマークが行われました。ベンチマークにより、CPU が各 Wasm 命令に費やす実際の時間を適切に推定できます。
詳細については、 github.com/dfinity/ic wasm_instructions_bench() の関数を参照してください。
ベンチマークの結果はCPU命令テーブルと非常に密接に相関しており、ベンチマークが意味を持ち、開発者にとって驚きがないことが確認できます。コスト調整の簡単な概要:
結果
---
以下に、新しいインストルメンテーション アルゴリズムと新しい Wasm 命令の重みによって、いくつかのアプリケーションがどのように影響を受けるかを示します。これは網羅的なリストではありませんが、キャニスター開発者がさまざまな種類のアプリケーションに対して何を期待すべきかを示しています。
命令数が X% 増加しても、イングレス料金とメッセージ実行料金は含まれないため、全体のコストが X% 増加するわけではないことに注意してください。
全体的に、インタープリタは、実行される命令の数と実行時間の両方の点で桁違いに効率的です。これにより、TypeScript、JavaScript、Python などのインターネット コンピュータ上の新しい言語への扉が開かれます。
残りのワークロードについては、結果は古い計測結果の +/- 20% の範囲で変動します。これはわずかなオーバーヘッドであり、開発者にとって驚くべきことではありません。
新しいインストルメンテーションは、すべてのメインネット サブネットと開発環境 ( dfxv0.15+) に実装され、展開されています。
参考文献
ドキュメント: ICP 上の WebAssembly
ICP 開発者フォーラム:新しい Wasm インストルメンテーション
質問や提案がある場合、またはインターネット コンピュータ開発者や DFINITY エンジニアと会いたい場合は、forum.dfinity.orgに参加してください。
インターネット コンピュータについて詳しくは、以下をご覧ください。
Comments