Skip to content

Raise EP routed-row pricing to each checkpoint's observed routing - #981

Draft
bradhilton wants to merge 8 commits into
dalinar/cp-layout-recompute-floorfrom
dalinar/routed-share-observation
Draft

bradhilton wants to merge 8 commits into
dalinar/cp-layout-recompute-floorfrom
dalinar/routed-share-observation

Conversation

@bradhilton

@bradhilton bradhilton commented Sep 26, 2026 •

Copy link
Copy Markdown
Collaborator

At EP>1, TrainerRank prices the rows each rank's experts receive at a fixed allowance over balanced routing (1.4x at EP2). A checkpoint whose routing is more skewed than that is under-priced every time, and nothing notices. This PR measures each checkpoint's routed share on the micro-batch wave path. When that share plus a 0.10 margin exceeds the allowance, the checkpoint's routed rows are raised to match. Observations never lower a charge. Reloading a checkpoint starts over from the allowance.

Scope: HybridEP with the EP group equal to the CP group and TP/ETP 1, the only path where routed rows are priced from the CP group.

What changes

  • Observation. HybridEP keeps each MoE layer's received rows per local expert after combine. At the handoff of a forward_micro_batches wave with one group (with or without gradients), each rank reads the worst layer's received pairs, divided by top-k times the balanced rows that pricing starts from.
    • dp_rank_forward, split and multi-group waves record nothing. They are still priced from whatever the checkpoint has recorded.
  • Exchange. At EP>1, the existing handoff MAX reduce carries the share and the checkpoint's route epoch as epoch, -epoch. A share is recorded only if every rank observed the same epoch. An empty, unsupported, failed, cancelled or different-checkpoint rank records nothing, so every rank keeps an identical table.
  • Identity. A checkpoint gets a new route epoch after its load or snapshot commits on every rank. Epochs are never reused.
    • Loads, snapshots and discards now check that every rank targets the same name and epoch.
    • snapshot_checkpoint starts from its source's share, since it has the same weights. A snapshot loaded from disk starts cold.
    • Reloading a name forgets the old content's share, and discarding a snapshot forgets its share.
    • A failed load rolls back to the previous epoch.
    • Optimizer steps keep the epoch, so a training slot's share is a high-water mark.
  • Pricing. _plan_group_routed_rows scales a group's balanced rows by (share + 0.10) / allowance when that exceeds 1. The scaled rows feed every consumer: plan cost, admission, splits and the checkpoint floor.
  • Reporting. last_forward_telemetry()["routed_share"] gives each wave's observed share. Planner-miss reports include it, along with the share behind each group's routed rows.

Before/after

  • Estimates are unchanged while observed shares stay below 1.3 at EP2. Every measured EP2 case so far is in that range: pretrained up to 1.22 and a trained policy up to 1.24 in the routing probe.
  • A checkpoint observed at 1.35 (one production run was inferred at that level) would be priced at 1.45 on the waves after that observation.

Not in this PR: pricing below the allowance (the "warm discount" of Part B2). It waits on measurements that this PR makes possible.

Testing:

  • 24 new CPU unit tests. They cover simulated peers, a two-process gloo exchange, real checkpoint load/snapshot/discard paths, and admission with raised rows. The trainer-rank unit suite passes.
  • A local 2xH200 run: Qwen3.6-35B-A3B, 40 real layers, CP2/EP2, 12 real-data calls per arm.
    • Every call recorded a share.
    • With learning rate 0, shares stayed at 1.15-1.16 and estimates were unchanged.
    • Training at lr 1e-4 reached a share of 1.364, and B1 raised the following calls' charges.
    • No call was under-estimated.

Stacked on #978 (itself on #963); neither base is merged, so review after them.

🤖 Generated with Claude Code

bradhilton and others added 4 commits September 26, 2026 04:20
At EP>1 TrainerRank prices the rows each rank's experts receive at a fixed
allowance over balanced routing (1.4x at EP2); skew beyond it went unseen.
Record each checkpoint's worst-layer routed share at the micro-batch
handoff, from HybridEP's retained tokens_per_expert, through the existing
handoff MAX reduce (EP>1 only: share, epoch, -epoch). A share is recorded
only when every rank observed the same checkpoint route epoch. Epochs are
assigned after a load or snapshot commits on every rank, never reused, and
forgotten on reload or discard.

When a checkpoint's share plus a 0.10 margin exceeds the allowance, its
balanced routed rows are scaled up to match. Charges never go down. The
share is reported in last_forward_telemetry() and planner-miss reports.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Review follow-ups:
- Loads and snapshot discards now check that every rank targets the same
  name and route epoch, so every rank forgets and assigns the same epochs.
- Cancellation during the observation still reaches the handoff exchange,
  as a failed forward.
- Epochs at or above 2**40 are not observed, so float64 transport stays
  exact.
- No-grad waves are observed: they dispatch the same way, and observations
  only raise charges.
- A snapshot starts from its source's share, since it has the same weights.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@bradhilton
bradhilton had a problem deploying to trainer-rank-gpu-validation September 26, 2026 05:13 — with GitHub Actions Error
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@bradhilton
bradhilton deployed to trainer-rank-gpu-validation September 26, 2026 05:23 — with GitHub Actions Active
@bradhilton
bradhilton marked this pull request as ready for review September 26, 2026 15:53
@bradhilton
bradhilton deployed to trainer-rank-gpu-validation September 26, 2026 15:54 — with GitHub Actions Active
@bradhilton
bradhilton marked this pull request as draft September 26, 2026 16:08
Picks up main (0e0c31b) through the updated #963 and #978 branches. Clean
merge; the source equals the composition measured in M9 plus #998.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@bradhilton
bradhilton deployed to trainer-rank-gpu-validation September 26, 2026 23:14 — with GitHub Actions Active
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@bradhilton
bradhilton had a problem deploying to trainer-rank-gpu-validation September 27, 2026 00:32 — with GitHub Actions Error

This branch was successfully deployed

No deployments
trainer-rank-gpu-validation — 3999370b Deployed Sep 27, 2026 by bradhilton via Run on 2x H200 #830
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant