
Reddioから見る並列EVMの最適化への道
TechFlow厳選深潮セレクト

Reddioから見る並列EVMの最適化への道
イーサリアムレイヤー2のReddioにおける並列EVMの実装アプローチの概要。
執筆:霧月、Geek web3
ご存知の通り、EVMはイーサリアムの「実行エンジン」および「スマートコントラクト実行環境」として位置づけられており、イーサリアムにおける最も重要なコアコンポーネントの一つであると言える。パブリックチェーンは数万ものノードから構成されるオープンネットワークであり、各ノードのハードウェア仕様には大きな差がある。複数のノード上でスマートコントラクトが同一の結果を出力し、「一貫性」を満たすためには、異なるデバイス上でも同じ実行環境を構築する必要があり、その目的を達成できるのが仮想マシン(VM)である。
イーサリアムの仮想マシンEVMは、Windows、Linux、macOSなどの異なるオペレーティングシステムやデバイス上で、スマートコントラクトを同じ方法で実行できる。このクロスプラットフォーム互換性により、どのノードでもコントラクトを実行した結果が一致することを保証している。典型的な例がJava仮想マシン(JVM)である。

普段ブロックエクスプローラーで見かけるスマートコントラクトは、すべてまずEVMバイトコードにコンパイルされ、その後ブロックチェーン上に保存されている。EVMがコントラクトを実行する際には、これらのバイトコードを順番に読み取り、各命令(opCode)に対応するガスコストが発生する。EVMは各命令の実行におけるガス消費量を追跡しており、その消費量は操作の複雑さに応じて決まる。
また、イーサリアムのコア実行エンジンとして、EVMはトランザクションを逐次的に処理する方式を採用しており、すべてのトランザクションは単一キュー内で順序付けられ、確定的な順序で一つずつ実行される。並列化を行わない理由は、ブロックチェーンが厳密な一貫性を維持しなければならず、すべてのノードにおいて一連のトランザクションが同じ順序で処理される必要があるためである。もしトランザクション処理を並列化すると、順序の正確な予測が困難になり、対応するスケジューリングアルゴリズムを導入しない限りは整合性が保てないが、これは非常に複雑になる。

2014~2015年のイーサリアム創設チームは時間的制約から、設計がシンプルでメンテナンスしやすい逐次実行方式を選択した。しかし、ブロックチェーン技術の進化とユーザー層の拡大に伴い、TPSやスループットに対する要求が高まり、Rollup技術が登場し成熟した後、EVMの逐次実行によるパフォーマンスボトルネックが特にイーサリアムL2で顕在化してきた。
SequencerはLayer2のキーコンポーネントとして、単一サーバー形式ですべての計算タスクを処理する。Sequencerと連携する外部モジュールの効率が十分に高い場合、最終的なボトルネックはSequencer自体の性能に依存するため、逐次実行方式は大きな障壁となる。
opBNBチームはDA層やデータ読み書きモジュールを極限まで最適化することで、Sequencerが毎秒約2,000件以上のERC-20転送を実行可能とした。この数字は高く見えるが、ERC-20転送よりもはるかに複雑なトランザクションを処理する場合、TPSは大きく低下する。従って、トランザクション処理の並列化は今後の必然的なトレンドである。
以下ではより具体的な詳細から入り、従来のEVMの限界と並列EVMの利点について詳しく解説する。
イーサリアムトランザクション実行の二大コアコンポーネント
コードモジュールの観点から、EVM以外にgo-ethereumにおいてトランザクション実行に関わるもう一つの主要コンポーネントがstateDBであり、イーサリアムのアカウント状態とデータストレージを管理している。イーサリアムはMerkle Patricia Trieと呼ばれる木構造をデータベースインデックス(ディレクトリ)として使用しており、EVMによる各トランザクションの実行によってstateDB内の特定データが変更され、その変更は最終的にMerkle Patricia Trie(以下、グローバルステートツリー)に反映される。

具体的には、stateDBはすべてのイーサリアムアカウント(EOAアカウントおよびコントラクトアカウント)の状態を維持しており、保持するデータには残高やスマートコントラクトコードなどが含まれる。トランザクション実行中、stateDBは対象アカウントのデータを読み書きし、実行終了後に新しい状態を下位のデータベース(例:LevelDB)にコミットして永続化する。
要するに、EVMはスマートコントラクトの命令を解釈・実行し、計算結果に基づいてブロックチェーン上の状態を変更する役割を担い、stateDBはグローバルな状態ストレージとして、すべてのアカウントとコントラクトの状態変化を管理する。両者が協働することでイーサリアムのトランザクション実行環境が構築されている。
逐次実行の具体的プロセス
イーサリアムのトランザクションには2種類あり、EOA間送金とコントラクトトランザクションである。EOA間送金は最もシンプルなタイプで、一般アカウント間でのETH送金を指す。コントラクト呼び出しを含まないため処理速度が非常に速く、操作も簡単なためガス料金も極めて低い。
一方、コントラクトトランザクションはスマートコントラクトの呼び出しと実行を伴う。EVMがコントラクトトランザクションを処理する際には、コントラクトのバイトコード命令を1つずつ解釈・実行していく必要があり、コントラクトのロジックが複雑になればなるほど、必要な命令数やリソース消費も増える。
例えば、ERC-20転送の処理時間はEOA間送金の約2倍程度であり、Uniswapのようなより複雑なスマートコントラクト取引ではさらに時間がかかり、EOA間送金に比べて十数倍遅くなることもある。これはDeFiプロトコルが取引時に流動性プールの管理、価格計算、トークンスワップなど複雑なロジックを処理する必要があり、高度な計算を要するためである。
では逐次実行モードにおいて、EVMとstateDBという二つのコンポーネントはどのように協働してトランザクションを処理しているのか?
イーサリアムの設計では、1つのブロックに含まれるトランザクションは前後順に1件ずつ処理され、各トランザクション(tx)にはその操作を実行するための独立したインスタンスが割り当てられる。各トランザクションは異なるEVMインスタンスを使用するが、すべてのトランザクションは共通の状態データベース(stateDB)を共有する。
トランザクション実行中、EVMは常にstateDBとやり取りを行い、stateDBから関連データを読み取り、変更後のデータをstateDBに書き戻す。

コードレベルでEVMとstateDBの協働プロセスを概観してみよう:
1. processBlock()関数がProcess()関数を呼び出して、ブロック内のトランザクションを処理する;

2. Process()関数内にはforループが定義されており、トランザクションが1件ずつ処理されていることが確認できる;

3. すべてのトランザクション処理が完了すると、processBlock()関数がwriteBlockWithState()関数を呼び出し、さらにstatedb.Commit()関数を呼び出して状態変更をコミットする。

ブロック内のすべてのトランザクションが実行された後、stateDBのデータは前述のグローバルステートツリー(Merkle Patricia Trie)にコミットされ、新たなステートルート(stateRoot)が生成される。ステートルートは各ブロックの重要なパラメータであり、ブロック実行後の新しいグローバル状態の「圧縮結果」を記録している。
容易に理解できるように、EVMの逐次実行モードには明確なボトルネックがある。つまり、トランザクションは順番に処理されなければならず、長時間かかるスマートコントラクトトランザクションがある場合、その処理が終わるまで他のトランザクションは待機せざるを得ない。これではCPUなどのハードウェアリソースを十分に活用できず、効率が大きく制限される。
EVMのマルチスレッド並列最適化手法
逐次実行と並列実行を生活の例えで比較すると、前者はカウンターが1つしかない銀行、後者は複数のカウンターを持つ銀行にたとえられる。並列モードでは複数のスレッドを開き、複数のトランザクションを同時に処理できるため、効率が数倍向上するが、厄介なのは状態衝突の問題である。
複数のトランザクションが同じアカウントのデータを書き換えようとする場合、同時に処理されると競合が発生する。例えば、あるNFTは1個しかミントできないのに、トランザクション1とトランザクション2がそれぞれミントを宣言していた場合、両方のリクエストを満たすと明らかに誤りが生じる。このようなケースへの対処には調整が必要である。実際の運用ではこうした状態衝突がより頻繁に発生するため、トランザクション処理を並列化するには、状態衝突への対策が不可欠である。
ReddioによるEVMの並列最適化原理
ZKRollupプロジェクトのReddioが採用するEVM並列最適化の考え方を見てみよう。Reddioのアプローチは、各スレッドに1件のトランザクションを割り当て、各スレッドに一時的な状態データベース(pending-stateDB)を提供するというものである。詳細は以下の通り:

1. マルチスレッドによる並列トランザクション処理:Reddioは複数のスレッドを設定し、異なるトランザクションを同時処理する。スレッド間は干渉せず、処理速度が数倍に向上する。
2. 各スレッドに一時状態データベースを割り当て:各スレッドに独立した一時的な状態データベース(pending-stateDB)を割り当てる。各スレッドはトランザクション実行中にグローバルなstateDBを直接変更せず、状態変更をpending-stateDBに一時的に記録する。
3. 状態変更の同期:ブロック内のすべてのトランザクションが実行された後、EVMは各pending-stateDBに記録された状態変更を順次グローバルstateDBに反映する。異なるトランザクション間に状態衝突がなければ、pending-stateDBの記録をグローバルstateDBに正常にマージできる。
Reddioは読み書き操作の処理方式を最適化し、トランザクションが正しく状態データにアクセスでき、衝突を回避できるようにしている。
-
読み取り操作:トランザクションが状態データを読み取る必要がある場合、EVMはまずPending-stateのReadSetを確認する。ReadSetに必要なデータがあれば、pending-stateDBから直接読み取る。対応するキー・バリュー(key-value)がReadSetに存在しない場合は、前のブロックに対応するグローバルstateDBから履歴状態データを取得する。

-
書き込み操作:すべての書き込み操作(状態の変更)はグローバルstateDBに直接書き込まれず、まずPending-stateのWriteSetに記録される。トランザクション実行後に衝突検出を行い、その後で状態変更をグローバルstateDBにマージしようとする。

並列実行の鍵となる問題は状態衝突であり、複数のトランザクションが同じアカウントの状態を読み書きしようとするときに特に顕著になる。これを解決するため、Reddioは衝突検出機構を導入している:
-
衝突検出:トランザクション実行中、EVMは異なるトランザクションのReadSetとWriteSetを監視する。複数のトランザクションが同じ状態項目を読み書きしようとしている場合、衝突が発生したとみなす。
-
衝突処理:衝突を検出した場合、該当のトランザクションは再実行が必要とマークされる。
すべてのトランザクションの実行が完了した後、複数のpending-stateDBに記録された変更はグローバルstateDBに統合される。マージが成功すれば、EVMは最終状態をグローバルステートツリーにコミットし、新しいステートルートを生成する。

マルチスレッドによる並列最適化がもたらすパフォーマンス向上は明らかであり、特に複雑なスマートコントラクトトランザクションの処理において顕著である。
並列EVMに関する研究によると、低衝突ワークロード(トランザクションプール内で矛盾やリソース競合が少ない状況)では、ベンチマークテストのTPSが従来の逐次実行に比べて約3~5倍向上する。高衝突ワークロードでは、理論的にはすべての最適化手段を適用すれば最大60倍に達することもある。
まとめ
ReddioのEVMマルチスレッド並列最適化手法は、各トランザクションに一時的な状態データベースを割り当て、異なるスレッドで並列処理を行うことで、EVMのトランザクション処理能力を大幅に向上させた。読み書き操作の最適化と衝突検出機構の導入により、EVM系ブロックチェーンは状態の一貫性を保ちつつ、トランザクションの大規模並列化を実現し、従来の逐次実行モードによるパフォーマンスボトルネックを解決した。これは今後のイーサリアムRollup発展の重要な基盤を築いている。
今後はさらにReddioの実装詳細を深掘りして分析していく予定である。例えば、ストレージ効率のさらなる最適化によるパフォーマンス向上、高衝突時の最適化戦略、GPUを活用した最適化などについても触れていく。
TechFlow公式コミュニティへようこそ
Telegram購読グループ:https://t.me/TechFlowDaily
Twitter公式アカウント:https://x.com/TechFlowPost
Twitter英語アカウント:https://x.com/BlockFlow_News














