著者: Tia、Techub News
ブロックチェーンは分散設計により効率が犠牲になっているため、実行速度の向上は常に緊急に解決する必要がある問題の 1 つです。ブロックチェーンの「実行層」は、あらゆるトランザクションを処理してチェーンに追加する重要な部分です。処理能力を高速化するために、実行層の改善は中核的な戦略の 1 つとなっており、並列実行はこの面での重要なブレークスルーです。
従来のブロックチェーンは通常、シリアル方式を使用してトランザクションを 1 つずつ処理します。これにより、トランザクション速度が大幅に制限され、特にトランザクション集中型のネットワークで輻輳が発生する可能性があります。ただし、並列実行により、複数のトランザクションを同時に処理できるため、実行効率が大幅に向上し、オンチェーンのプレッシャーが軽減されます。
並列処理とは何かをよりよく理解するために、実行から始めて、PBS 後のマージ モードのイーサリアムを例として、実行とは何かを説明し、トランザクション ライフ サイクル全体における実行の位置を示します。
トランザクション実行の特定の側面
- トランザクションはメモリ プールに入り、フィルタリングおよび並べ替えられます。これは、トランザクションのフィルタリングと並べ替えを完了するための Mempool、Searcher、および Builder の対話を含む、トランザクションが送信された後の前処理段階です。
- Builder はブロックを構築します (ただし、実行はしません): Builder は、収益性の高いトランザクションをブロックに配置して、トランザクションのパッケージ化と順序付けを完了します。
- 提案者がブロックを検証して送信する: ブロックの構築が完了すると、ビルダーはブロック提案を提案者に送信します。プロポーザーはブロックの構造とトランザクションの内容を検証し、ブロックをネットワークに正式に送信して実行を開始します。
- トランザクションの実行: ブロックが送信された後、ノードはブロック内のトランザクションを 1 つずつ実行します。すべてのトランザクションがスマート コントラクトの呼び出し、アカウント残高の変更、またはステータスの変更をトリガーするため、これはステータス更新にとって重要な段階です。
- 証人: バリデーターは実行結果とブロックの状態ルートを証人し、それを最終確認として使用します。これにより、実行層でのブロックの信頼性と有効性が保証され、不整合が防止されます。
- 状態の同期: 各ノードは、ブロックの実行結果 (アカウント残高、契約ステータスの更新など) を自身のローカル状態に同期します。各トランザクションの実行後、ノードは、トランザクションで使用する新しい状態ルートを計算して保存します。次のブロックを初期状態として使用します。
もちろん、これはブロック単位のトランザクションのステータス同期にすぎません。最新のオンチェーンステータスを維持するために、ノードは通常、ブロックごとにデータを同期し、ブロックとステータスを継続的に検証します。しかし、POS メカニズムの下でファイナリティを達成したい場合、アグリゲータは各スロットの証人署名を完全な署名に集約して次のスロットのプロポーザに渡す必要があり、検証者はエポック後に、エポック内のすべてのブロックのステータスは投票数に基づいて確認され、一時的なコンセンサス ステータス チェックポイントが形成されます。ブロックとトランザクションは、2 つの連続したエポックが大多数のバリデーターから監視サポートを受けた後にのみファイナリティに達します。
トランザクションのライフサイクル全体の観点から見ると、プロポーザーがビルダーによって送信されたブロックの構造とトランザクションの内容を検証した後に実行が発生します。実際の実行プロセスでは、トランザクションを 1 つずつ処理し、対応するアカウントまたは契約ステータスを更新する必要があります。すべてのトランザクションが実行された後、プロポーザーは新しいステート ルート (マークル ルート) を計算します。これは、現在のブロック内のすべてのトランザクションの実行結果と最終的なグローバル状態の概要です。平たく言えば、完全なブロック実行プロセスには、各トランザクションの実行からマークル ルートの計算まで、イーサリアムを前の状態から次の状態に変更するプロセスで完了する必要がある一連の計算が含まれます。
順次実行
並列実行の反対は逐次実行であり、これは現在のブロックチェーンのより一般的な実行方法です。通常、トランザクションは順番に段階的に実行されます。トランザクションが実行されると、イーサリアムはアカウント ステータスと関連情報 (残高、契約ストレージ データなど) をアカウント ステータス ツリーに更新し、新しいアカウント ステータス ハッシュが生成されます。すべてのアカウント状態ツリーが更新されると、状態マークル ルートと呼ばれる状態ツリーのルート ノードのハッシュが形成されます。状態マークル ルート、トランザクション マークル ルート、および受信マークル ルートが完了すると、ブロック ヘッダーがハッシュされて、ブロックのブロック ハッシュが生成されます。
中でも、トランザクションの実行順序は重要です。マークル ツリーはハッシュ値のバイナリ ツリーであるため、異なる順序で形成されるマークル ルート値も異なります。
並列実行
並列実行環境では、ノードはブロック内のトランザクションを並列処理しようとします。トランザクションを 1 つずつ順番に実行するのではなく、トランザクションを異なる「実行パス」に割り当てて、同時に実行できるようにします。並列実行により、システムはブロック内のトランザクションをより効率的に処理し、スループットを向上させることができます。
すべてのトランザクションの実行が完了すると、ノードは実行結果 (つまり、トランザクションによって影響を受けたステータスの更新) を要約して、新しいブロック ステータスを形成します。この状態はブロックチェーンに追加され、チェーン上の最新のグローバル状態を表します。
ステータスの競合
並列処理ではトランザクションが異なるパスで同時に処理されるため、並列処理の主な問題は状態の競合です。つまり、複数のトランザクションが同じ期間内にブロックチェーン上のデータの同じ部分 (状態) を読み書きする状況が発生する可能性があります。この状況に適切に対処しないと、実行結果が不確実になります。ステータスの更新順序が異なるため、最終的な計算結果は異なります。例えば、
トランザクション A とトランザクション B という 2 つのトランザクションがあり、どちらも同じ口座の残高を更新しようとしているとします。
- トランザクション A: アカウント残高を 10 増やします。
- トランザクション B: アカウント残高を 20 増やします。
アカウントの初期残高は 100 です。
シリアルに実行すると、実行順序の結果は確実になります。
1. 最初にトランザクション A を実行し、次にトランザクション B を実行します。
- 口座残高はまず10増えて110になります。
- さらに 20 個追加すると、最終的に 130 個になります。
2. 最初にトランザクション B を実行し、次にトランザクション A を実行します。
- 口座残高はまず20増えて120になります。
- さらに 10 を追加すると、最終的には 130 になります。
どちらのシーケンスでも、システムはトランザクション実行の順序一貫性を保証するため、最終残高は 130 です。
ただし、並列実行環境では、トランザクション A とトランザクション B が同時に初期残高 100 を読み取り、独自の操作を実行する可能性があります。
- トランザクション A は残高が 100 であることを読み取り、計算後の更新された残高は 110 になります。
- トランザクション B も残高が 100 であることを読み取り、計算後に残高を 120 に更新します。
この場合、トランザクションが同時に実行されたため、トランザクション A とトランザクション B の操作によって相手の結果が「上書き」され、ステータスの競合が発生したため、最終残高は 130 ではなく 120 に更新されるだけでした。 。
このタイプの状態競合の問題は通常「データの上書き」と呼ばれます。つまり、トランザクションが同じデータを同時に変更しようとすると、互いの計算結果が上書きされ、その結果、不正確な最終状態が生じる可能性があります。状態の競合によって引き起こされる可能性のあるもう 1 つの問題は、実行順序が保証できないことです。複数のトランザクションが異なる期間に操作を完了するため、実行シーケンスも異なります。順序が異なると計算結果が異なる可能性があり、結果が不確かになることがあります。
この不確実性を回避するために、ブロックチェーン並列実行システムは通常、いくつかの競合検出およびロールバックメカニズムを導入するか、事前にトランザクションの依存関係分析を実行して、最終状態の一貫性に影響を与えることなくトランザクションが並列実行されることを保証します。
楽観的並列処理と決定的並列処理
起こり得る状態の競合に対処するには、決定的並列処理と楽観的並列処理という 2 つの方法があります。どちらのモードも、効率と設計の複雑さの点でトレードオフがあります。
決定的並列処理では、状態アクセスを事前に宣言する必要があり、トランザクションが順序付けされるときに、バリデータまたはシーケンサーが宣言された状態アクセスをチェックします。複数のトランザクションが同じ状態に書き込もうとした場合、同時実行を避けるために、これらのトランザクションは競合としてマークされます。チェーンごとに、事前に状態アクセスを宣言するさまざまな方法が実装されていますが、通常は次のメソッドが含まれます。
- コントラクト仕様による制約: 開発者はスマート コントラクトで状態アクセス スコープを直接指定します。たとえば、ERC-20 トークンの転送には、送信者と受信者の残高フィールドへのアクセスが必要です。
- トランザクション構造化データによる宣言: ステータス アクセスをマークする特別なフィールドをトランザクションに追加します。
- コンパイラー分析: 高級言語コンパイラーは、コントラクト コードを静的に分析し、状態アクセス セットを自動的に生成できます。
- フレームワークによる必須の宣言: 一部のフレームワークでは、開発者が関数を呼び出すときにアクセスする必要がある状態を明示的に指定する必要があります。
オプティミスティック並列処理では、最初にトランザクションをオプティミスティックに処理し、競合が発生するまで待機してから、影響を受けたトランザクションを順番に再実行します。競合を可能な限り回避するために、オプティミスティック並列設計の核心は、履歴データや静的分析などを通じて状態を迅速に予測して想定することです。つまり、検証が不完全な場合、システムは特定の操作またはステータスの更新が有効であると想定し、パフォーマンスとスループットを向上させるためにすべての検証プロセスを待つことを回避しようとします。
楽観的な並列処理では、状態に関するいくつかの簡単な予測と仮定によって競合を可能な限り回避できますが、特にコントラクトの実行やクロスチェーントランザクションに関しては、避けられない課題がいくつかあります。競合が頻繁に発生した場合、再実行が発生する可能性があります。システムのパフォーマンスが低下し、コンピューティング リソースの消費が増加します。
決定的並列処理では、トランザクションの前に状態の依存関係をチェックすることで、楽観的並列処理で発生する可能性のある競合を回避します。ただし、トランザクションの送信前に状態の依存関係を正確に宣言する必要があるため、開発者に高い要件が課せられ、実装の複雑さが増します。
EVM 並列ジレンマ
状態の競合に対処するには決定論的かつ楽観的な方法があるだけでなく、並列処理を実現する具体的なプロセスにおいてチェーン データベース アーキテクチャの観点から考慮する必要もあります。並列処理における状態競合の問題は、マークル ツリー アーキテクチャに基づく EVM では特に困難です。マークル ツリーは階層的なハッシュ構造であり、各トランザクションが特定の状態データを変更した後、マークル ツリーのルート ハッシュ値も更新する必要があります。この更新プロセスは再帰的であり、リーフ ノードからルート ノードまでレイヤーごとに計算されます。ハッシュは不可逆であるため、つまり、下位層でのデータ変更が完了した後にのみ上位層を計算できるため、この機能により並行して更新することが困難になります。
2 つのトランザクションが並行して実行され、同じ状態 (口座残高など) にアクセスすると、マークル ツリー ノードで競合が発生します。このような競合を解決するには、通常、複数のブランチで一貫したルート ハッシュ値を確保するための追加のトランザクション管理メカニズムが必要です。これを EVM に実装するのは簡単ではありません。並列化と状態の一貫性の間でトレードオフが必要となるからです。
非EVM並列ソリューション
ソラナ
イーサリアムのグローバル ステート ツリーとは異なり、Solana はアカウント モデルを採用しています。各アカウントは独立した記憶域であり、台帳に保存されるため、パスの競合が回避されます。
Solana は決定論的に並列です。 Solana では、各トランザクションは、アクセスするアカウントと必要なアクセス権 (読み取り専用または読み取り/書き込み) を明確に記載して送信する必要があります。この設計により、ブロックチェーン ノードは、トランザクションが実行される前に、各トランザクションがアクセスする必要があるリソースを事前に分析できます。トランザクションの実行が開始される前にすべてのアカウントの依存関係が明確になっているため、ノードはどのトランザクションが同じアカウントにアクセスするのか、どのトランザクションが安全に並行して実行できるのかを判断できるため、インテリジェントなスケジューリングが実現され、競合が回避され、並列スケジューリングの基礎が実現されます。 。
各トランザクションは実行前にアクセスが必要なアカウントと権限を宣言するため、Solana はトランザクション間にアカウントの依存関係があるかどうかを確認できます (Sealevel モデル)。トランザクション間に共有の読み取りおよび書き込みアカウントがない場合、システムはそれらを異なるプロセッサに割り当てて並列実行できます。
アプトス
Aptos の並列実行設計は Ethereum とは大きく異なり、主にアカウント モデルと状態ストレージに反映される、アーキテクチャとメカニズムにいくつかの重要な革新が加えられています。
Ethereum では、トランザクションの実行時にグローバル ステート ツリー (MPT) を頻繁に更新する必要があります。すべてのアカウントと契約のステータスは共有状態ツリーに保存され、トランザクションはこの状態ツリーの一部にアクセスして更新する必要があります。 Aptos は、アカウントを独立した状態単位に分割します。各オブジェクトは、相互に影響を与えることなく独立して存在でき、明確な参照関係がある場合にのみ関連付けられます。オブジェクト間に共通のツリー パスはなく、ロックの競合や完全な並列処理はありません。
Aptos の基礎となるデータ構造は Jellyfish Merkle Tree です。各オブジェクトの状態は、最終的には独立したキーと値のペアとして JMT に保存されます。イーサリアムの MPT とは異なり、Jellyfish Merkle Tree は完全なバイナリ ツリー構造の形式をとっており、ノードのストレージ パスとクエリ パスが簡素化され、検証時間が大幅に短縮されます。さらに、ツリー内の各アカウントの位置は固定されており、ツリー内のノードは独立して格納されるため、複数のアカウントの更新と検索を並行して実行できます。
Aptos は、すべてのアカウントの依存関係を事前に宣言する必要がないという点で、楽観的に類似しています。この目的のために、Aptos は Block-STM を使用します。Block-STM は、事前に設定されたトランザクション シーケンスを使用して依存関係を推定し、それによってアボートの数を減らします。
並列EVM
非 EVM 並列処理と比較して、並列 EVM は、状態の依存関係、競合検出、ガス管理、ロールバック メカニズムなどの問題に対処する際に、より大きな技術的困難に直面します。これをよりよく理解するために、いくつかの並列 EVM プロジェクト (Sui、Monad、Canto など) がこれらの問題をどのように解決しているかを参照できます。
スイ
また、Aptos と同様に、Sui もオブジェクト モデルを使用して状態を処理し、各オブジェクト (アカウント、スマート コントラクトの状態など) を独立したリソースとして使用し、これらのオブジェクトは一意のオブジェクト識別子によって区別されます。トランザクションに異なるオブジェクトが関与する場合、これらのトランザクションは直接競合することなく異なる状態で動作するため、並列処理できます。
Sai は状態を管理するためにオブジェクト モデルを使用しますが、EVM との互換性を保つために、Sui のアーキテクチャは追加のアダプテーション レイヤーまたは抽象化メカニズムを通じてオブジェクト モデルと EVM のアカウント モデルを橋渡しします。
Sui では、トランザクション間に競合がないことを前提として、トランザクションは楽観的な並列処理戦略を使用してスケジュールされます。競合が発生した場合、システムはロールバック メカニズムを使用して状態を復元します。
Sui は、オブジェクト モデルと状態分離テクノロジを使用して、状態の依存関係の問題を効果的に回避します。各オブジェクトは独立したリソースとして機能し、異なるトランザクションを並行して実行できるため、スループットと効率が向上します。ただし、このアプローチのトレードオフは、オブジェクト モデルの複雑さとロールバック メカニズムのオーバーヘッドです。トランザクション間で競合が発生した場合、状態の一部をロールバックする必要があるため、システムの負荷が増大し、並列処理の効率に影響を与える可能性があります。非 EVM 並列システム (Solana など) と比較して、Sui は効率的な並列処理を維持するためにより多くのコンピューティング リソースとストレージ リソースを必要とします。
モナド
Sui と同様に、Monad は楽観的並列処理を使用します。ただし、Monad の楽観的並列処理は、特定のトランザクションが実行される前に依存関係のある一部のトランザクションを予測します。予測は主に Monad の静的コード アナライザーによって完了します。予測には状態へのアクセスが必要ですが、イーサリアム データベースに状態が保存される方法により、状態へのアクセスが非常に困難になり、並列状態読み取りのプロセスをより効率的にするために、Monad はデータベースも再構築しました。
モナド状態ツリーはパーティションに分割され、各パーティションは独自の状態サブツリーを維持します。更新する場合、状態ツリー全体を再構築することなく、関連するフラグメントのみを変更する必要があります。ステータス インデックス テーブルを通じてパーティション内のステータスを迅速に特定し、パーティション間の対話を削減します。
まとめ
並列処理の核心は、マルチパス実行を通じて実行層の実行効率を向上させることです。マルチパス実行を実現するには、チェーンが一連の競合検出およびロールバック メカニズムを実行して、競合が発生しないようにする必要があります。最終状態の一貫性に影響を与えるため、データベースをある程度まで実行および改善します。
もちろん、実行層の効率の向上は並列処理に限定されません。実行リンクの最適化は、データベース上のトランザクションに必要な読み取りおよび書き込み操作を削減することによっても実現できます。チェーン全体の速度の向上にはより広い範囲が含まれ、コンセンサス層の効率の向上も含まれます。
各テクノロジーには独自の制約があります。並列処理は効率を向上させるための 1 つの方法にすぎません。このテクノロジを使用するかどうかの最終決定は、それが開発者にとって使いやすいかどうか、分散化を犠牲にすることなく完了できるかどうかなどを考慮する必要があります。テクノロジーのスタックは多ければ多いほど良いのです。少なくともイーサリアムにとって、並列化は、効率性の向上という観点だけでは、それほど魅力的ではありません。シンプルさの観点から見ても、並列化は最適なソリューションではありません。イーサリアムの現在のロールアップ中心のロードマップを考えてみましょう。