paint-brush
並列実行に関する研究: 知っておくべきことすべて@sin7y
9,966 測定値
9,966 測定値

並列実行に関する研究: 知っておくべきことすべて

Sin7Y20m2023/01/03
Read on Terminal Reader

長すぎる; 読むには

FISCO-BCOS 2.0 では、トランザクション処理にグラフ構造を使用しています。開発者は、有向非巡回グラフ モデル (DAG) PTE に基づいて Parallel Transaction Executor (PTE) を設計し、マルチコア プロセッサの利点を十分に活用して、ブロック内のトランザクションを並列で実行できるようにします。
featured image - 並列実行に関する研究: 知っておくべきことすべて
Sin7Y HackerNoon profile picture
0-item

序文

この研究では、イーサリアムに似た実装システムを比較し、トランザクションの並列実行の難しさと可能性を分析します。


この調査のために分析されたチェーンは、UTXO スキームを含まないアカウント モデル設計スキームに基づいていることに注意してください。

研究対象

  1. ブロック内でのトランザクション検証の並列実行をサポートするコンソーシアム ブロックチェーンの 1 つ、FISCO-BCOS。


  2. Khipu パブリック チェーン、イーサリアム プロトコルのスカラ実装。


  3. Aptos パブリック チェーン、Move Virtual Machine。

並列実行の問題点

従来のトランザクション実行プロセスを見てみましょう。


実行モジュールは、ブロックから各トランザクションを取り出し、順次実行します。


最新の世界の状態は実行プロセス中に変更され、トランザクションの完了後に状態が加算され、ブロックの完了後に最新の世界の状態になります。


次のブロックの実行は、現在/前のブロックからの世界の状態に厳密に依存するため、このシーケンシャルなシングルスレッド実行プロセスは並列実行にはあまり適していません。



以下は、現在の Ethereum 並列実行方法における主な競合です。


  1. アカウントの競合: 2 つのスレッドがアドレス アカウントの残高またはその他の属性を同時に処理する場合、それが順次処理の結果と一致していること、つまり、世界の状態が明確な有限状態マシンであるかどうかを確認するにはどうすればよいでしょうか?


  1. 同じアドレスのストレージ競合: 両方のコントラクトが同じグローバル変数のストレージを変更した場合。


  2. Cross-Contract Call Conflict:コントラクト A が最初にデプロイされた場合、コントラクト B はコントラクト A のデプロイが完了するまで待機してコントラクト A を呼び出す必要があります。

並列実行スキーム

FISCO-BCOS

概要

FISCO-BCOS 2.0 では、トランザクション処理にグラフ構造を使用しています。開発者は、有向非巡回グラフ モデル (DAG) に基づいて Parallel Transaction Executor (PTE) を設計しました。


PTE を使用すると、マルチコア プロセッサの利点を最大限に活用できるため、ブロック内のトランザクションを可能な限り並行して実行できます。


同時に、シンプルで使いやすいプログラミング インターフェイスをユーザーに提供するため、ユーザーは並列実装の面倒な詳細を気にする必要がありません。


ベンチマーク テスト プログラムの実験結果は、従来のシリアル トランザクション実行スキームと比較して、理想的な条件下で 4 コア プロセッサ上で実行されている PTE が約 200% ~ 300% のパフォーマンス向上を達成できることを示しており、計算の向上は数に比例します。コアの。


コアが多いほど、パフォーマンスが向上します。

一般的なスキーム

非巡回有向グラフは、多くの場合有向非巡回グラフ (DAG) と呼ばれます。


トランザクションのバッチでは、各トランザクションが占有する相互に排他的なリソースが識別されます。次に、ブロック内の一連のトランザクションと相互に排他的なリソースの占有関係に従って、トランザクション依存の DAG が構築されます。


以下の図に示すように、インバウンド度が 0 (依存する事前注文タスクがない) のすべてのトランザクションを並列に実行できます。右側のトランザクション DAG は、左側の元のトランザクション リストの順序に基づくトポロジカル ソートによって取得できます。


モジュラー アーキテクチャ


  • ユーザーは、SDK を介して直接的または間接的にトランザクションを開始します。


  • その後、トランザクションはノード間で同期され、同じパッケージ化権限を持つノードがシーラー (TxPool) を呼び出して (txpool) から一定量のトランザクションを取得し、それらをブロックにパッケージ化します。その後、ブロックはコンセンサスユニットに送られ、ノード間のコンセンサスに備えます。


  • コンセンサスに達する前にトランザクションの検証が実行され、ここから PTE が作業プロセスを開始します。アーキテクチャ図からわかるように、PTE は最初にブロック内のトランザクションを順番に読み取り、DAG コンストラクターに入力します。DAG コンストラクターは、各トランザクションの依存関係に従ってすべてのトランザクションを含むトランザクション DAG を構築します。その後、PTE はワーカー プールをウェイクアップします。複数のスレッドを使用して、トランザクション DAG を並行して実行します。 Joiner は、DAG がワーカー プール内のすべてのスレッドによって実行されるまで、メイン スレッドを中断します。この時点で、Joiner は各トランザクションの状態変更レコードに基づいて状態ルートと受信ルートを計算し、その結果を上位レベルの呼び出し元に返します。


  • ブロックが検証された後、ブロックはチェーンにアップロードされます。トランザクションが実行された後、各ノードの状態が一貫している場合、コンセンサスに達し、ブロックが基礎となるストレージに書き込まれ、ブロックチェーンに永続的に記録されます。

トランザクション DAG の構築プロセス


  1. パックされたブロックからブロック内のすべてのトランザクションを取得します。


  2. トランザクション数を頂点の最大数として DAG インスタンスを初期化します。


  3. すべてのトランザクションを順番に読み取ります。トランザクションがマージ可能である必要がある場合は、その競合フィールドを解決し、以前のトランザクションがそれと競合するかどうかを確認してください。その場合、対応するトランザクション間に依存エッジを構築します。トランザクションがマージ可能でない場合は、以前のすべてのトランザクションが実行された後に実行する必要があると見なされるため、トランザクションとそのすべての先行トランザクションの間に依存エッジが作成されます。


: すべての従属エッジが作成されると、それらをマージすることはできず、順次実行することしかできません。

DAG 実行プロセス


  1. メイン スレッドは、最初にハードウェア コアの数に基づいてスレッドの小さなグループを初期化し、ハードウェア コアに障害が発生した場合、他のスレッドは作成されません。


  2. DAG が完了していない場合、スレッド ループは、DAG の waitPop メソッドから、in 次数が 0 の Ready トランザクションが取り出されるのを待ちます。実行するトランザクションの取り出しに成功すると、トランザクションが実行されます。失敗した場合、DAG は実行を完了し、スレッドは終了します。

問題と解決策

  1. 同じブロックに対して、すべてのノードが実行を完了し、同じ状態 (3 つのルート ノードが一致) であることを確認するにはどうすればよいでしょうか?


FISCO BCOS は、ステート ルート、トランザクション ルート、およびレシート ルートのトリプルが互いに等しいことを検証して、ステートが合意されているかどうかを判断します。トランザクション ルートは、ブロック内のすべてのトランザクションに基づいて計算されたハッシュ値です。


すべてのコンセンサス ノードが同じブロック データを処理する限り、トランザクション ルートは同じでなければならず、これは比較的簡単に保証できます。重要なのは、トランザクション後に生成された状態とレシート ルートが同じであることを確認することです。


異なる CPU コアで並列実行される命令間の実行順序を事前に予測できないことはよく知られており、並列実行されるトランザクションについても同様です。


従来のトランザクション実行スキームでは、すべてのトランザクションが実行されるたびに状態ルートが変更され、変更された状態ルートがトランザクション レシートに書き込まれます。


すべてのトランザクションが実行された後、最終状態ルートはブロックチェーンの現在の状態を表します。同時に、すべてのトランザクション レシートに基づいてレシート ルートが計算されます。


従来の実装では、状態ルートがグローバル共有変数として機能することがわかります。


トランザクションが並行して順不同で実行される場合、トランザクションは異なるマシンで異なる順序で実行され、最終状態ルートの一貫性が保証されず、受信ルートも保証されないため、状態ルートの従来の計算は適用できなくなります。一貫している。


FISCO BCOS では、最初にトランザクションが並列実行され、各トランザクションの状態変化の履歴が記録されます。すべてのトランザクションが実行された後、履歴に基づいてステート ルートが計算されます。


同時に、すべてのトランザクションが実行された後、トランザクション確認の状態ルートが最終的な状態ルートになるため、トランザクションが並行して実行された場合でもコンセンサス ノードが合意に達することができます。


  1. 2 つのトランザクションが依存しているかどうかを判断する方法は?


2 つのトランザクションが依存していないが、依存していると判断された場合、不要なパフォーマンスの低下につながります。逆に、両方のトランザクションが同じアカウントの状態を書き換えるが、並行して実行される場合、アカウントの最終的な状態は不確実になる可能性があります。


したがって、依存関係の決定は、パフォーマンスに影響を与える重要な問題であり、ブロックチェーンが適切に機能するかどうかを決定することさえできます。


単純な転送トランザクションでは、送信者と受信者のアドレスに基づいて、2 つのトランザクションが依存しているかどうかを判断できます。例として、A→B、C→D、D→Eの3つの振込取引を考えてみます。


D→E トランザクションは C→D トランザクションの結果に依存することは簡単にわかりますが、A→B トランザクションは他の 2 つのトランザクションとは関係がないため、並行して実行できます。


この種の分析は、単純な転送のみをサポートするブロックチェーンには当てはまりますが、スマート コントラクトを実行するチューリング完全ブロックチェーンでは正確ではない可能性があります。これは、ユーザーが作成した転送コントラクトで何が起こっているのか正確にはわからないためです。 .これが起こり得ることです。


A→Bの取引はCとDの口座状況とは関係ないように見えますが、ユーザーの根底にある実装では、Aは特別口座であり、Cの口座から一定の手数料を引かなければなりません。 Aの口座を通じて送金されたすべてのお金。


このシナリオでは、3 つのトランザクションはすべて関連しているため、並行して実行することはできません。これまでの依存関係の分析方法でトランザクションを分割すると、どうしてもミスが発生してしまいます。


ユーザーのコントラクトの内容に基づいて、トランザクションに実際に存在する依存関係を自動的に推測できますか?答えはノーです。前述のように、契約上の依存関係や実行プロセスを静的解析で解析することは困難です。


FISCO BCOS では、取引依存関係の割り当ては、契約内容に精通している開発者に任されています。具体的には、トランザクションが依存する相互に排他的なリソースは、一連の文字列で表すことができます。


FISCO BCOS は、開発者にインターフェースを公開します。開発者は、トランザクションが依存するリソースを文字列の形式で定義し、チェーン上の実行者に通知します。


エグゼキューターは、開発者が指定したトランザクションの依存関係に従って、ブロック内のすべてのトランザクションをトランザクション DAG に自動的に配置します。


たとえば、単純な転送コントラクトでは、開発者は各転送トランザクションの依存関係が {送信者アドレス + 受信者アドレス} であることを指定するだけです。


さらに、開発者が転送ロジックで別のサードパーティ アドレスを導入する場合、依存関係を {送信者アドレス + 受信者アドレス + サードパーティ アドレス} として定義する必要があります。


このアプローチは直感的でシンプルかつ一般的であり、すべてのスマート コントラクトに適用されますが、開発者の肩にかかる責任も大きくなります。


開発者は、トランザクションの依存関係を指定する際に細心の注意を払う必要があります。依存関係が正しく記述されていない場合、結果は予測できません。

並列フレームワーク コントラクト

FISCO BCOS では、開発者が並行契約のフレームワークを使用できるように、契約書作成に関するいくつかの仕様を設定しています。仕様は次のとおりです。

並列相互排他

2 つのトランザクションを並行して実行できるかどうかは、2 つのトランザクションが相互に排他的であるかどうかによって異なります。相互排除とは、2 つのトランザクションの一連のストレージ変数の交差を指します。


たとえば、資産譲渡のシナリオでは、トランザクションはユーザー間の譲渡操作です。 transfer(X, Y) は、ユーザー X からユーザー Y への転送インターフェイスを表し、相互排除は次のようになります。



  • 相互に排他的なパラメーター: コントラクト インターフェイス内のコントラクト格納変数の "読み取り/書き込み" 操作に関連するパラメーター。転送インターフェイス transfer(X, Y) を例にとります。 X と Y は相互に排他的なパラメーターです。


  • Mutex: ミューテックス パラメータに従ってトランザクションから抽出された特定のミューテックス コンテンツ。転送インターフェイス transfer(X, Y) を例にとります。このインタフェースを使用した A 転送トランザクションでは、特定のパラメータは transfer(A, B) であり、この操作のミューテックスは [A, B] です。別のトランザクションでは、transfer(A, C) が呼び出され、この操作のミューテックスは [A, C] です。


2 つのトランザクションを同時に実行できるかどうかを判断することは、2 つのトランザクションのミューテックスが交差するかどうかを判断することです。交差部分が空のトランザクションは並列に実行できます。


FFISCO-BCOS は、パラレル コントラクト、プリコンパイル コントラクト、およびソリディティ コントラクトを記述する 2 つの方法を提供します。ここでは後者についてのみ説明します。同じことが事前にコンパイルされたコントラクトにも当てはまります。

Solidity コントラクト パラレル フレームワーク

並列の堅牢なコントラクトを作成するには、その上で、ParallelContract.sol を並列化したいコントラクトの基本クラスにします。 registerParallelFunction() メソッドは、並列化できるインターフェイスを登録するために呼び出されます。


パラレル コントラクト コードは次のとおりです。

 pragma solidity ^0.4.25; //Precompile the contract interface contract ParallelConfigPrecompiled { function registerParallelFunctionInternal(address, string, uint256) public returns (int); function unregisterParallelFunctionInternal(address, string) public returns (int); } //The parallel contract base class needs to be registered and the subcontract needs to be implement enable or disable interface contract ParallelContract { ParallelConfigPrecompiled precompiled = ParallelConfigPrecompiled(0x1006); function registerParallelFunction(string functionName, uint256 criticalSize) public { precompiled.registerParallelFunctionInternal(address(this), functionName, criticalSize); } function unregisterParallelFunction(string functionName) public { precompiled.unregisterParallelFunctionInternal(address(this), functionName); } function enableParallel() public; function disableParallel() public; }


次の例は、並列フレームワーク コントラクトに基づいて記述された転送コントラクトです。


 pragma solidity ^0.4.25; import "./ParallelContract.sol"; // Introduce ParallelContract.sol contract ParallelOk is ParallelContract // useParallelContract as a base class { // Contract implementation mapping (string => uint256) _balance; // Global mapping // The mutually exclusive variables from and to are the first two parameters at the beginning of transfer (). It can be seen that the contract requirements are still very strict, which will make users uncomfortable to write function transfer(string from, string to, uint256 num) public { _balance[from] -= num; // From is the key of the global mapping, and is a mutually exclusive parameter _balance[to] += num; //// To is the key of the global mapping, and is a mutually exclusive parameter } // The mutex variable name comes first as an argument to the beginning of set() function set(string name, uint256 num) public { _balance[name] = num; } function balanceOf(string name) public view returns (uint256) { return _balance[name]; } // Register contract interfaces that can be parallel function enableParallel() public { // The function definition string (note that there are no Spaces after ",") and the first few arguments are mutex arguments (mutex arguments must be first when designing a function) //The number 2 indicates that the first two are mutex parameters, and the system decodes the mutex according to the function signature and abi registerParallelFunction("transfer(string,string,uint256)", 2); // critical: string string // registerParallelFunction("set(string,uint256)", 1); // critical: string } // Deregister the parallel contract interface function disableParallel() public { unregisterParallelFunction("transfer(string,string,uint256)"); unregisterParallelFunction("set(string,uint256)"); } }


インターフェイスを並列にできるかどうかを判断する

パラレル コントラクト インターフェイスは、次の条件を満たす必要があります。

  • 外部コントラクトは呼び出されません。
  • 他の関数インターフェイスは呼び出されません。

Mutex パラメータの決定

インターフェイスをプログラミングする前に、インターフェイスの相互に排他的なパラメーターを決定します。インターフェイスの相互に排他的なパラメーターは、グローバル変数に対して相互に排他的です。相互に排他的なパラメーターを決定するための規則は次のとおりです。


  • グローバル マッピングがインターフェイスによってアクセスされる場合、マッピングのキーは相互に排他的なパラメーターです。


  • グローバル配列がインターフェイスによってアクセスされる場合、配列の添字は相互に排他的なパラメーターです。


  • インターフェイスが単純型のグローバル変数にアクセスする場合。単純型のグローバル変数はすべてミューテックス パラメータを共有し、異なる変数名をミューテックス オブジェクトとして使用します。


たとえば、コントラクトには単純型の複数のグローバル変数があり、さまざまなインターフェイスがさまざまなグローバル変数にアクセスします。


異なるインターフェイスを並列処理する場合は、変更されたグローバル変数を使用してインターフェイス パラメーターにミューテックス パラメーターを定義し、呼び出し中にどのグローバル変数が使用されるかを示す必要があります。


呼び出されると、トランザクションのミューテックスを識別するためにグローバル変数の変更された「変数名」がミューテックス パラメータにアクティブに渡されます。


例: setA(int x)globalAをグローバル パラメーターとして変更する場合、 setAset(string aflag, int x)として定義する必要があります。呼び出されると、 setA("globalA", 10)が渡されます。変数名“globalA”を使用して、このトランザクションのミューテックスがglobalAであることを示します。

パラメータの型と順序を決定する

相互に排他的なパラメーターを決定したら、ルールに従ってパラメーターの型と順序を決定します。ルールは次のとおりです。


  • インターフェイス パラメーターは、string、address、uint256、および int256 に制限されています (今後、さらに多くの型がサポートされる予定です)。


  • 相互に排他的なパラメーターはすべて、インターフェイス パラメーターに指定する必要があります。


  • 相互に排他的なすべてのパラメーターは、インターフェイス パラメーターの最初の場所にあります。


FISCO-BCOS の並行取引は、利用者の契約書の仕様に大きく依存していることがわかる。


ユーザが作成する契約書の仕様が標準化されていないと、システムが急いで並列実行し、帳簿の根本的な矛盾を引き起こす可能性があります。

キプ

概要

Khipu 氏は、ユーザーが契約書の作成時にエラーなしで静的な競合を引き起こすアドレスの範囲を識別してラベル付けすることは非現実的であると考えています。これは、FISCO-BCOS の見解とは対照的です。


競合状態がどこで、どのような条件で発生するかは、確実性の獲得が現在の状態に関係する場合にのみ判断できます。


この種の判断は、現在の契約プログラミング言語では、コードの静的分析が完全に正確で見逃されていない結果を得ることをほとんど不可能にしています。


Khipu は、この問題に対処するためのより包括的な試みを行い、それを実装するプロセスを完了しました。

一般的なスキーム

Khipu では、同じブロック内の各トランザクションは、前のブロックのワールド ステートから開始され、並列に実行され、実行中にすべての理想的なエクスペリエンス パスで発生した上記の 3 つの競合状態が記録されます。


並列実行フェーズに続くのはマージ フェーズで、パラレル ワールドの状態が 1 つずつマージされます。トランザクションをマージするときは、まず記録された静的条件から、以前にマージした競合状態と競合するかどうかを判断します。


そうでない場合は、直接マージします。その場合、マージされたワールドの以前の状態から開始して、トランザクションが再度実行されます。


最後にマージされた世界の状態は、ブロックのハッシュに対してチェックされます。これが最後の防衛線です。チェックが正しくない場合、以前のマージは破棄され、ブロックが再度実行されます。

並列度指数

ここで Khipu は、並列処理のインデックスを導入しています。これは、ブロック内で、再度実行することなく結果を直接組み合わせることができるトランザクションの割合を指します。


作成ブロックから最新ブロックまでの数日間にわたる Ethereum リプレイの Khipu の観察は、この比率 (並列処理) が平均で 80% に達することを示しています。


一般に、コンピューティング タスクを完全に並列化できる場合、1 つのチェーンのスケーラビリティは無限大です。いつでもノードに CPU コアを追加できるからです。そうでない場合、理論上の最大レートはアンダルの定理によって制限されます。


システムを高速化できる限界は、並列化できない部分の逆数に依存します。つまり、99% 並列化できれば、最大 100 倍高速化できます。しかし、95% の並列化しか達成できない場合、最大 20 倍の速度しか得られません。


イーサリアム上の全トランザクションのうち、約80%が並列化でき、20%が並列化できないため、Khipuの速度制限は約5倍です。

紛争マーカー

evm コードの命令を理解することによって、限られた数の命令がストレージの読み取りおよび書き込みプロセスを作成したことが判明したため、これらの読み取りおよび書き込みプロセスを記録して読み取りおよび書き込みコレクションを形成することができましたが、静的コード分析では、これらのプロセスが記録されていることを確認できませんでした。


そのため、各ブロックを処理する際に、各トランザクションを 1 回事前実行する必要があります。実行前のプロセスでは、トランザクションが同じアカウントまたはストレージに対する読み取りと書き込みであるかどうかがわかり、トランザクションごとに readSet と writeSet が作成されます。


ブロックチェーンに 100 のトランザクションがある場合、これらの 100 のトランザクションはスレッド プールを介して並列に実行できます。各コントラクトには同じ初期の世界状態があり、実行中に 100 個の readSet と writeSet が作成され、それぞれ 100 個の新しい状態が作成されます。


事前実行が終了すると、次の段階の処理が開始されます。理想的には、100 個の readSet エントリと writeSet エントリが競合しない場合、それらを直接マージして、ブロック内のすべてのトランザクションの最終的な世界状態を生成できます。ただし、トランザクションは多くの場合、それほど理想的ではありません。


これを処理する正しい方法は、最初のトランザクションの実行後の readSet および writeSet と、2 番目のコントラクトの実行後の readSet および writeSet を比較し、それらが同じアカウントまたはストレージを読み書きしたかどうかを確認することです。


もしそうなら、それは2つの取引が競合していることを意味します.次に、最初のトランザクションの完了に続いて 2 番目のトランザクションが開始され、再度実行されます。


同様に、マージ ステート マシンが継続すると、競合セットが蓄積され続け、後続のトランザクションが前のトランザクションと競合する限り、すべてのトランザクションが実行されるまで、それらは順次実行されます。


イーサリアムのメインネットでのトランザクションのリプレイを通じて、競合が多い場合、ほとんどの場合、相互に関連するトランザクションと同じブロック内の交換であることがわかりました。これも、このプロセスと一致しています。


一般的なプロセス


特定の並行処理

アプトス

概要

Aptos は Diem の Move 言語と MoveVM 上に構築され、並列実行を可能にする高スループット チェーンを作成します。 Aptos のアプローチは、ユーザー/開発者に対して透過的でありながら関連付けを検出することです。


つまり、トランザクションは、状態のどの部分 (メモリの場所) を使用するかを明示的に示す必要はありません。

一般的なスキーム

Aptos は、Block-STM と呼ばれる修正版のソフトウェア トランザクション メモリを使用し、Block- STMに基づく並列実行エンジンを実装します。


Block-STM は MVCC (Multi-version Concurrency Control) を使用して、書き込みと書き込みの競合を回避します。同じ場所へのすべての書き込みは、TX-ID と書き込み tx が再実行された回数を含むバージョンと共に保存されます。


トランザクション (tx) がメモリ位置の値を読み取るとき、tx の前に発生したその位置に MVCC から書き込まれた値と、関連するバージョンを取得して、読み取り/書き込みの競合があるかどうかを判断します。


Block-STM では、トランザクションはブロック内で事前にソートされ、実行中の並列実行のためにプロセッサ スレッド間で分割されます。並列実行では、トランザクションを実行するための依存関係がないと見なされます。


トランザクションによって変更されたメモリ ロケーションが記録されます。実行後、すべてのトランザクション結果を確認します。検証中に、前のトランザクションによって変更されたメモリ ロケーションにアクセスするトランザクションが見つかった場合、そのトランザクションは無効になります。


取引結果を更新してから、再度取引を実行してください。このプロセスは、ブロック内のすべてのトランザクションが実行されるまで繰り返されます。ブロック STM は、複数のプロセッサ コアが使用されている場合に実行を高速化します。加速は、トランザクションがどの程度相互依存しているかによって異なります。


Aptos が使用するスキームは、上記の Khipu とほぼ同じですが、実装にはいくつかの違いがあります。詳細は次のとおりです。


  • Khipu は、ブロック内トランザクションに並列実行と順次検証を使用します。ただし、Aptos はブロック内のトランザクションの並列実行と検証を実装します。これら 2 つの方式には、長所と短所があります。 Khipu は実装が簡単で、効率はわずかに低くなります。 Block-STM により、Aptos は多くのスレッドで同期とシグナル操作を使用します。これは非常に効率的ですが、コードの実装が困難です。


  • Move はグローバル リソース アドレッシングをネイティブにサポートしているため、Aptos は並列実行に役立つ場合、ブロック間であってもトランザクションを並べ替えます。 Aptosは、このスキームは並列の効率を向上させるだけでなく、MEVの問題も解決できると主張しています。ただし、これがユーザー エクスペリエンスに影響するかどうかはまだ検討中です。


  • Aptos は、実行速度を最大化するために実行中に結果の書き込みセットをメモリに保存し、次に実行するブロックのキャッシュとして使用します。繰り返し書き込みは、安定したメモリに 1 回だけ書き込む必要があります。

ベンチマークテスト

Aptos は、ブロック STM 統合後に対応するベンチマークを作成し、10k トランザクションのブロックの順次実行と並列実行を比較しました。比較結果は次のようになります。


上の図からわかるように、Block STM は 32 スレッドを並列に実行した場合の順次実行よりも 16 倍高速であり、高競合下では 8 倍以上高速です。

結論

上記の比較と分析に基づいて、一部のスキームでは、静的および動的分析によって依存関係を見つけることができるように、コントラクトを作成するときに確立されたルールに従ってユーザーがストレージを作成する必要があると結論付けることができます。


Solana と Sui は同様のスキームを使用していますが、ユーザーの認識は異なります。このスキームは、本質的に、より良い分析結果を得るためのストレージ モデルの変更です。


Khipu と Aptos は、ユーザーに依存しないスキームです。並列実行のオーバーヘッドは開発者にかかっていないため、コントラクトを作成するときにこれについて考える必要はありません。


仮想マシンは実行前に依存関係を動的に分析するため、依存関係のない並列実行が実現されます。


これは実装が難しく、並列度はトランザクションのアカウント区分にある程度依存します。トランザクションの競合が多い場合、頻繁に再実行することでパフォーマンスが大幅に低下します。


Aptos は、ユーザーが作成したコントラクトを将来的に最適化して、依存関係をより適切に分析し、実行を高速化する予定であると述べました。


シリアルベースのスキームをパラレル スキームに変更するだけで、パブリック チェーン環境でトランザクション スループットが 3 ~ 16 倍向上します。これを大きなブロックと大きなガス制限と組み合わせることができれば、L2 スループットはさらに最適化され、潜在的に約 1 倍になります。 100回。


実装と効率に関するエンジニアリングの観点から、OlaVM は Khipu スキームとカスタマイズされたストレージ モデル ソリューションを採用する可能性が高く、Block-STM の導入によって引き起こされる複雑さを回避しながらパフォーマンスを向上させ、エンジニアリングの最適化を促進します。


参考文献

  1. FISCO-BCOS Github、 FISCO-BCOS
  2. Khipu GitHub、GitHub - khipu-io/khipu: イーサリアムに基づくエンタープライズ ブロックチェーン プラットフォーム
  3. Aptos GitHub、GitHub - aptos-labs/aptos-core: Aptos は、より優れたテクノロジーとユーザー エクスペリエンスを通じてブロックチェーンの広範な使用をサポートするために構築されたレイヤー 1 ブロックチェーンです。

私たちに関しては

2021 年に設立され、一流のブロックチェーン開発者によって運営されている Sin7y は、EVM、レイヤー 2、クロスチェーン、プライバシー コンピューティング、自動支払いソリューションなど、最も重要で最先端の技術を探求するプロジェクト インキュベーターおよびブロックチェーン技術研究チームです。 .


現在、OlaVM と呼ばれる、EVM 互換で高速かつスケーラブルな ZKVM に取り組んでいます。私たちと話をすることに興味がある場合は、お気軽にTG グループに参加するか、 [email protected]までメールでお問い合わせください。