レッスン14 — 並列実行 — 逐次インタープリターループの先へ
問い
ここまで実行を逐次として扱った = Reth と revm が現状で出荷、mainnet が 10 年動いてきた形。そして EVM 性能を縛る最大の天井。Sei / Monad / MegaETH / Aptos が並列 EVM を出荷済み、Reth 自身も実験的並列パスを持つ。block-stm パターン + Database トレイトへのマッピング、どう動くか?
原理(最小モデル)
- 逐次の天井. mainnet ブロック ~200 tx、衝突する(互いが読む slot に書く)のは 約 10-20% = 残り 80% は意味的理由なしに逐次、これが並列 EVM が狙う賞品。
- block-stm(block-level Software Transactional Memory). Aptos が Move 向けに導入、Sei が EVM に移植、Monad / MegaETH 採用。
- アルゴリズム 4 ステップ. ① N tx を並列で投機実行(複数 worker)、② 各 tx 中に read set + write set 記録、③ commit 順検証(tx i の read set が早い tx j > j の write set と重なるなら i 再実行)、④ N tx 全部逐次順 valid まで検証 + 再実行繰り返し。
- 楽観的並行制御. 衝突は稀と仮定、衝突しない 80% は並列で 1 回、衝突する 20% は逐次再実行、正味 3-8 倍スループット(ワークロード次第)。
Databaseトレイトへのマッピング.basic/storage呼び出しが read-set entry、state 変更が write-set entry、tracker = 標準Databaseをラップ。TrackedDatabase構造.inner: D+read_set+read_accounts、storage()等のオーバーライドで read 記録 →inner委譲、executor / インタープリター書き換え不要。- 本番複雑さ 6 領域. Write set 伝播(MVCC)+ 再実行順序(thrash 防止)+ Read/write set 推定(実行後にしか分からない)+ Hot コントラクト(避けられない直列化)+ ガス会計(再実行で総ガス増)+ 決定論(並列 worker が毎回正確に同じ state root)。
- チェーン別ステータス. Sei v2 デフォルト並列 / Monad 初日から / MegaETH ターゲット / Reth 実験的 / mainnet 逐次(コンセンサスリスクと TVL)。
具体例
TrackedDatabase:
pub struct TrackedDatabase<D: Database> {
inner: D,
read_set: HashSet<(Address, StorageKey)>,
read_accounts: HashSet<Address>,
}
impl<D: Database> Database for TrackedDatabase<D> {
type Error = D::Error;
fn storage(&mut self, addr: Address, key: StorageKey) -> Result<StorageValue, Self::Error> {
self.read_set.insert((addr, key));
self.inner.storage(addr, key)
}
// basic / code_by_hash / block_hash も同様に track
}
100 tx ブロック例(90 独立 + 10 sandwich-arb 同 pool):
| パス | tx | 結果 |
|---|---|---|
| 投機並列 1 | 90 独立 | 1 パス目で全部 commit |
| 投機並列 1 | 10 sandwich | 9 個が古い read set 検出 |
| 再実行波 1-2 | 10 sandwich | 逐次 commit |
総 wall-clock = 1 並列パス + 2 小再実行波(100 逐次ステップでなく)= 衝突はコストになるが比例的にしか効かない。
失敗例(誤解)
「並列 EVM = インタープリター書き換え」— 間違い。Database トレイトの合成性で executor / インタープリター不変、tracking-wrapper パターンで read/write track、これが「Database トレイトの形が重く報われる」場所。
「mainnet が並列実行に保守的なのは技術的問題」— 間違い。コンセンサスリスク + TVL($400B+)、並列バグは全ノード同時影響でチェーン分裂 = mainnet には取れない賭け、新チェーン(Sei / Monad / MegaETH)は TVL 低いところから始め速く iterate できる。
「block-stm は hot コントラクトを並列化できる」— 間違い。同じ pool への全 sandwich tx は 避けられない直列化 = block-stm は助けない、悪化させないだけ、修正はアプリケーション層(複数 pool / AMM 再設計)。
ステップで組み立てる
Step 1: 逐次の天井を理解
mainnet ブロック ~200 tx、衝突 10-20%、残り 80% は意味的理由なしに逐次。
Step 2: block-stm アルゴリズム 4 ステップ
投機並列 + read/write set 記録 + commit 順検証 + 再実行ループ。
Step 3: 楽観的並行制御の数字感覚
衝突しない 80% は並列 1 回、衝突する 20% は逐次再実行、正味 3-8 倍。
Step 4: Database トレイトへのマッピング
TrackedDatabase でラップ、basic / storage を track、executor 不変。
Step 5: 本番複雑さ 6 領域
MVCC + 再実行順序 + set 推定 + hot コントラクト + ガス会計 + 決定論。
Step 6: チェーン別ステータス読み解き
Sei / Monad / MegaETH 並列、Reth 実験的、mainnet 逐次(TVL + コンセンサスリスク)。
答え合わせ
Databaseトレイトが並列実行を可能にする理由: トレイトの 合成性 =TrackedDatabaseで標準Databaseをラップ、basic/storage呼び出しを track(read-set entry)、innerに委譲。executor / インタープリターは書き換え不要、ラッパーが read 記録すれば任意のコードが並列で動く。これが「Databaseトレイトの形が重く報われる」場所、設計時点では並列実行を考えていなくても合成性が後から賞金を返す。- block-stm が 3-8 倍スループットを出す根拠: mainnet ブロック ~200 tx で衝突 10-20%、残り 80% は独立 = 投機並列で 1 パス目に commit、衝突する 20% は再実行で逐次にコスト払う、正味 wall-clock = 1 並列パス + 小再実行波(200 逐次ステップでなく)。ワークロード次第で 3-8 倍、衝突多いほど効果薄い。
- mainnet が並列実行に保守的な理由: コンセンサスリスク + TVL。並列実行のバグは全ノード同時影響でチェーン分裂、mainnet には $400B+ 載っている = 取れない賭け。新チェーン(Sei / Monad / MegaETH)は TVL 低いところから始め速く iterate できる、同じコード同じリスクプロファイル違う失敗コスト。
合格基準
- block-stm 4 ステップ(投機並列 + set 記録 + commit 順検証 + 再実行ループ)を即答できる。
- 楽観的並行制御の数字(衝突 10-20% / 残り 80% / 正味 3-8 倍)を即答できる。
TrackedDatabaseの構造(inner+read_set+read_accounts)と executor 不変の根拠を即答できる。- 本番複雑さ 6 領域を即答できる。
- mainnet vs Sei / Monad / MegaETH の保守性差(TVL + コンセンサスリスク)を 1 文で説明できる。
まとめ(3行)
- block-stm = 楽観的並行制御(投機並列 → set 記録 → 検証 → 再実行ループ)、mainnet 衝突 10-20% / 残り 80% 並列 = 正味 3-8 倍スループット。
Databaseトレイトの合成性でTrackedDatabaseラッパーが read 記録、executor / インタープリター不変 = トレイト設計の合成性が後から並列化の道を開く。- 本番複雑さ 6 領域(MVCC / 再実行順序 / set 推定 / hot コントラクト / ガス会計 / 決定論)、mainnet 保守的は TVL + コンセンサスリスク、Sei / Monad / MegaETH は速く iterate、次は JIT/AOT で同じ天井を別角度から attack。