Fix the CP4 calibration hang: decide warm-ups from world-wide compile telemetry (#840) - #843
Conversation
The cost-calibration harness stopped warming a candidate up when the local rank's forward was compile-free. Compile telemetry is per process, so a CP rank whose local shapes still recompiled ran one more warm-up of the previous layout while its peers moved to the next candidate; the context-parallel all-to-alls then paired different layouts and deadlocked. This is the "deterministic CP4 hang" of issue #840 on Ellavox groups 1 and 4: rank 3 recompiled on its second depth_one warm-up, ranks 0-2 did not. Every recorded compile status is now the gathered union over all ranks (`_world_compile_statuses`), and warm-up completion is a pure function of that (`_warmup_complete`); the tp2-public compile-free gate uses the same world-wide view. Regression test on CPU. Diagnostics kept as dev tools: `trainer_rank_collective_trace.py` runs a calibration cell with every torch.distributed collective logged per rank and a stall watchdog; `trainer_rank_collective_diff.py` finds the first collective where ranks disagree. The CP4 recipe gains `CELL_SET=ellavox` to re-measure single Ellavox groups. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Groups 1 and 4 at CP4 were excluded from the calibration certificate while their hang (issue #840) was open. Re-measured with the fixed harness (campaign tr-cost-cp4-840, every candidate compile-free on all ranks, 8 measured rounds), both cells are now certified as held-out validation cells: group 1 is an odd Ellavox group (pre-registered holdout) and group 4 is held out by argument, so the 45 training cells and therefore the shipped table are unchanged (same hash). Metrics on all 58 cells / 3,849 pairs: 98.1% pairwise, p95 regret 2.9%, max 4.2%, no clear misses; shipped-table regret on the two new cells 0% and 2.8%. Manifest: no exclusions, the two cells attributed to the new campaign; the certificate/manifest tests assert 58 identities; README and design brief updated with the root cause. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Codex review — no blocking findingsThe root-cause analysis and fix are coherent. The divergent behavior was in the calibration harness: a rank-local compile-status decision could advance CP peers to different forced layouts. Gathering compile statuses before making the next-forward decision gives every rank the same bounded warm-up sequence; the new regression test covers the decisive rank-3-recompile case. The extra collective is confined to the dev/acceptance harness and does not affect TrainerRank's production path. The evidence update is also internally consistent: both formerly excluded CP4 cells are complete, the explicit Verification on |
Closes #840.
Root cause: a harness desynchronization, not a context-parallel runtime bug
The "deterministic CP4 hang" on Ellavox groups 1 and 4 came from
dev/trainer_rank_landing_acceptance.py --phase cost-calibrate: the warm-up loop stopped a candidate's warm-ups when the local rank's forward was compile-free. Compile telemetry is per process, and one CP rank (rank 3, whose local shard shapes differ) recompiled on its seconddepth_onewarm-up while ranks 0–2 did not. Ranks 0–2 moved on to the next candidate; rank 3 ran a thirddepth_one. The context-parallel all-to-alls then paired two different layouts and deadlocked.Evidence (instrumented run of the unmodified harness, every
torch.distributedcollective logged per rank):uniform_depth_2depth_oneno_sharingautomatic(one warm-up behind)This also explains why a standalone repro of the same layouts on a fresh cluster never hung (identical per-rank sequences), and why the same cells at CP1/CP2 were fine. The static plan checks (attention
_build_runtime_plan, GDN rank plans, plan-cache keys) were all consistent across ranks; no runtime code changes.Fix
compile_statusesis now the gathered union over all ranks (_world_compile_statuses, oneall_gather_objectper run), and warm-up completion is a pure function of it (_warmup_complete). Thetp2-publiccompile-free gate uses the same world-wide view.tests/unit/test_trainer_rank_calibration_harness.py.CELL_SET=ellavox ELLAVOX_GROUPS="1 4", 8 measured rounds). The evidence rows show the mechanism directly: the warm-up attempts that used to desynchronize now record world statuses["none", "recompile"]and all ranks take one more warm-up together.Certificate: the two cells folded in, table unchanged
The certificate now covers all 58 manifest cells (3,849 within-cell pairs) with no exclusions. Group 1 is an odd Ellavox group (pre-registered holdout) and group 4 is held out by argument (
--holdout "cp4|g4"), so the 45 training cells are the same as before and the fitter reproduces the shipped table bit for bit (same hash; the opt-inART_COST_CERTIFICATE_REFIT=1test passes). Metrics on all 58: 98.1% pairwise, p95 regret 2.9%, max 4.2%, no clear misses; shipped-table regret on the two new cells is 0% (g1) and 2.8% (g4, picksminimum_effective_span_495at 911 ms overuniform_depth_2at 887 ms).For the record: a plain refit on all 58 cells lands on an equivalent-quality but different table (
gdn_token_per_rank→ 0,token_per_rank+25%, identical metrics); the coordinate search has equivalent optima there, so swapping production coefficients for no measurable gain was rejected.Tooling
dev/trainer_rank_collective_trace.py: runs a calibration cell with every collective traced per rank (operation, caller, splits, layout label, forward index) plus a stall watchdog that dumps all thread stacks and exits, so a deadlock surfaces in minutes instead of the 30-minute NCCL timeout.dev/trainer_rank_collective_diff.py: prints the layout each rank ran per forward and the first collective where ranks disagree.dev/trainer_rank_cost_calibration_cp4.sky.yaml:CELL_SET=ellavoxruns only the groups inELLAVOX_GROUPS.The Ellavox corpus stays git-ignored and mounted; only timings and features were recorded.
Checks
uv run prek run --all-filesgreen;tests/acceptance/trainer_rank_planner+tests/unit/test_trainer_rank*.py+tests/unit/test_planner_cost*.py: 582 passed, 17 skipped (including the opt-in full refit). All SkyPilot clusters torn down.🤖 Generated with Claude Code