FABRKNT
Inside Reth — シンク・拡張・SDK
Reth スタック — シンク・拡張・SDK
レッスン 3 / 17·CONTENT10 分25 XP
コース
Inside Reth — シンク・拡張・SDK
レッスンの役割
CONTENT
順序
3 / 17

レッスン2 — Reth のパイプライン: 10 ステージ、順番付き

問い

ノードがネットワーク参加 1 分後、10M ブロック分のヘッダーを引っ張った。1 ブロックずつ処理 = 数週間 vs Reth の Staged Sync = 数時間 — トリックは「1 つの 操作 を数千ブロックにまたがって処理」。10 ステージ、固定順序、順序は恣意的ではない — どの制約がどの順序を強いるか?

原理(最小モデル)

  • 10 ステージのパイプライン. Header → Body → SenderRecovery → Execution → AccountHashing → StorageHashing → Merkle → TransactionLookup → IndexHistory → Finish。
  • 3 制約が順序を規定. Merkle はハッシング後 + AccountHashing/StorageHashing は理論並列だが順次実行(MDBX 競合 + DAG 複雑性)+ SenderRecovery が並列化の最大勝ち。
  • MerkleStage がハッシング後である理由. ソート済みハッシュ化キーを消費 → 全ソート集合が必要 → インターリーブ不可。
  • AccountHashing と StorageHashing が並列実行されない理由. MDBX 書き込み競合 + パイプライン単純性(DAG スケジューラ複雑化)。
  • SenderRecoveryStage が並列化の勝ち. 巨大バッチサイズ(10-30M 署名)+ データ依存なし + 純粋計算。Rayon で全 CPU コア展開。
  • ExecutionStage は順次の状態依存. ブロック N のストレージ書き込みがブロック N+1 の読み込みに影響 → Optimistic execution(Block-STM)なしでは並列化困難。
  • 100× 高速化の 3 要因. バッチ化(~10×)+ ステージ内並列化(~10×)+ I/O 償却(~3×)。

具体例

10 ステージのフロー:

flowchart LR
    H[HeaderStage] --> B[BodyStage]
    B --> S[SenderRecoveryStage]
    S --> E[ExecutionStage]
    E --> AH[AccountHashingStage]
    AH --> SH[StorageHashingStage]
    SH --> M[MerkleStage]
    M --> T[TransactionLookupStage]
    T --> I[IndexHistoryStages]
    I --> F[FinishStage]
#ステージ何をするホットループ
1HeaderStageブロックヘッダーをダウンロードネットワーク I/O
2BodyStagetx 本体 + uncle をダウンロードネットワーク I/O
3SenderRecoveryStage各 tx の sender を ECDSA で復元CPU(並列)
4ExecutionStageRevm を走らせ、状態差分を蓄積CPU(Revm)
5AccountHashingStageアカウント変更をハッシュ化キーでソートsort + write
6StorageHashingStageストレージ変更をハッシュ化キーでソートsort + write
7MerkleStageMPT ルートを更新tree compute
8TransactionLookupStagetx_hash → (block, index) インデックスsort + write
9IndexAccount/StorageHistoryStage履歴アクセスインデックスsort + write
10FinishStage帳簿付け、確定なし

3 順序制約:

制約 1: MerkleStage はハッシング後:

  • Merkle はソート済みハッシュ化キーを消費
  • AccountHashing/StorageHashing が Merkle 開始前にソート完了 + commit 必要
  • インターリーブ不可 = Merkle が処理するブロック範囲について全ソート集合が必要

制約 2: AccountHashing と StorageHashing は順次実行(理論並列だが):

  • 両方 ExecutionStage 出力を消費、独立ソート済み変更集合を生成 → 理論並列可能
  • なぜ順次? ① MDBX 書き込み競合(並列でロック争い、計算面の利得なし)+ ② パイプライン単純性(並列分岐 = DAG スケジューラ複雑化、利得限界的)

制約 3: SenderRecoveryStage が並列化の最大勝ち:

  • ECDSA 復元 = 純粋 CPU、共有状態なし、embarrassingly parallel
  • Rayon で全 CPU コア展開
  • 巨大バッチサイズ: 各ブロック 100-300 tx × 100K+ ブロック = 1 呼び出しで 10-30M 署名
  • データ依存なし + 純粋計算
  • ExecutionStage は順次状態依存(ブロック N のストレージ書き込み → ブロック N+1 の読み込み)→ Optimistic execution(Block-STM)なしでは並列化困難

100× 高速化の内訳:

  • バッチ化 ~10×: Sender 復元 + ハッシング + Merkle ルート計算が 1 呼び出しで数千ブロックに償却
  • ステージ内並列化 ~10×: 特に SenderRecoveryStage の Rayon 全コア展開
  • I/O 償却 ~3×: ディスク書き込みがブロックごとではなくステージ境界の大きなソート済みバッチ
  • 掛け合わせ ~300× 理論値、実際は 100-200× に着地(ハードウェア依存)

失敗例(誤解)

「Merkle ステージはハッシング中にインターリーブ可能」— 間違い。Merkle はソート済みハッシュ化キーを消費 → 全ソート集合が必要 → AccountHashing/StorageHashing が完全に commit 完了してから Merkle が始まる。

「並列化 = どこでも勝つ」— 間違い。AccountHashing/StorageHashing は理論並列だが MDBX 書き込み競合で利得なし + パイプライン複雑化。並列化は条件付きの勝ち:embarrassingly parallel + データ依存なし + 純粋計算が揃ったときだけ。

「ExecutionStage を Block-STM で並列化すれば 10×」— 可能だが複雑。ブロック N のストレージ書き込み → ブロック N+1 の読み込み = 順次状態依存。Block-STM は投機的並列実行 + 衝突再実行で並列化可能だが独自のコンセンサス的複雑性を伴う。

ステップで組み立てる

Step 1: 10 ステージを順に言える

Header → Body → SenderRecovery → Execution → AccountHashing → StorageHashing → Merkle → TransactionLookup → IndexHistory → Finish。

Step 2: 3 順序制約

Merkle はハッシング後 / AccountHashing と StorageHashing は順次(MDBX 競合 + DAG 複雑性)/ SenderRecovery が並列化の最大勝ち。

Step 3: なぜ SenderRecovery が勝つか

巨大バッチ + データ依存なし + 純粋計算 = Rayon で全コア展開。

Step 4: ExecutionStage が並列化困難な理由

順次状態依存(ブロック N → N+1)、Block-STM は可能だが複雑。

Step 5: 100× の 3 要因

バッチ化 ~10× + ステージ内並列化 ~10× + I/O 償却 ~3× = 掛けて ~300×、実際 100-200×。

答え合わせ

  • AccountHashing と StorageHashing を並列実行しない理由: ① ディスク書き込み競合(両方 MDBX に書く → ロック争い → 計算面の利得を打ち消す)、② パイプライン単純性(並列分岐 = DAG スケジューラが必要 → 複雑性増 vs 利得限界的)。Frontiers 2025 トークがこのトレードオフを論じる。
  • SenderRecovery がパイプラインで特別な理由: ① 巨大バッチ(100K+ ブロック × 100-300 tx = 10-30M 署名 / 呼び出し)、② データ依存なし(各復元独立)、③ 純粋計算(復元の合間に I/O なし)。Rayon で全コア展開できる embarrassingly parallel の教科書例。
  • 100× 高速化の帰属: バッチ化(~10×)が ECDSA セットアップを 100K+ ブロックに償却 + Merkle ルートを範囲ごとに 1 回(ブロックごとではない)でさらに ~10× + MDBX が効率的に書ける大きなソート済みバッチで ~3× = 掛けて ~300× 理論、ハードウェア依存で実際 100-200×。

合格基準

  • 10 ステージを順に即答できる。
  • 3 順序制約を言える。
  • SenderRecovery が並列化の勝ちである 3 理由を言える。
  • ExecutionStage が並列化困難な順次状態依存を 1 文で説明できる。
  • 100× 高速化を 3 要因に分解できる。

まとめ(3行)

  • 10 ステージパイプライン(Header → Body → SenderRecovery → Execution → AccountHashing → StorageHashing → Merkle → TransactionLookup → IndexHistory → Finish)、固定順序、3 制約が順序を規定。
  • SenderRecovery が並列化最大勝ち(embarrassingly parallel + Rayon 全コア)、AccountHashing/StorageHashing は理論並列だが順次(MDBX 競合 + DAG 複雑性)、ExecutionStage は順次状態依存。
  • 100× 高速化 = バッチ化 ~10× × ステージ内並列化 ~10× × I/O 償却 ~3× = ~300× 理論、実際 100-200×。