Skip to content

[Refactor] Declare pipeline neighbours from a shared declaration function - #43459

Merged
ch-wan merged 5 commits into
mainfrom
cheng/refactor/pp-neighbour-declarations
Oct 10, 2026
Merged

ch-wan merged 5 commits into
mainfrom
cheng/refactor/pp-neighbour-declarations

Conversation

@ch-wan

@ch-wan ch-wan commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator

This PR is part of a stack (oldest at bottom):

Motivation

To bind the first and last stages of a pipeline rank's stack, make_layers built the neighbouring layers again on the meta device and read the stages they declared. Building a layer can have effects the meta device does not stop (streams, communicators, explicit devices), its choices had to be re-derived from modules nobody runs, and every bound stage kept its neighbour's and its producer's declaration, holding their reads, updates and transforms (and so their modules) for the life of the model.

Modifications

  • Record a neighbouring stage by the facts binding reads of it. facts_of() reduces a declaration to the capabilities binding reads (UpdateFacts, ReadFacts, TransformFacts) without the declarations it is chained to; neighbours and recorded producers are such facts. A read's kernels are not among them: binding takes kernels only from the reads of the stages it binds, so ReadFacts supplies none. A rank whose first stage would have to run the output transform of an attention on the previous rank is rejected (no model declares one); a test covers it.
  • Let a model declare its neighbouring stages without building the layers. make_layers / make_pp_layers take stage_facts: the model's shared declaration function, which returns the stages the layer at a global index declares, and which the layer also uses for its own declarations. Under PP each layer a rank builds is checked against it; a mismatch names the layer, the stage and each differing field with both values. Models not yet migrated stay on a transitional list that keeps the meta-device path; Nemotron-H, whose declarations already came from its pattern, gives a function and leaves the list. [Refactor] Declare every model's stages from a shared function and drop the meta-device neighbour build #43460 migrates every other model and removes the list.
  • Drop stage boundary forwarding and exports nothing uses; drop the keyword arguments no output move is given.
  • Describe neighbouring stages, exit policies and sum completers as they are in the layer boundary developer guide, including a table of exit policies and which of their decisions are fixed at construction.

Accuracy Tests

How the stack is checked:

  • Unit tests: the 39 layer-boundary test files pass at the head of every PR in the stack, apart from the two AMD-only test_aiter_allreduce_fusion_amd cases that fail on NVIDIA on main too; the test files that changed most recently pass on every commit. The full test/registered/unit suite (926 files, one process per file) shows no failure on the stack's head that main does not have.
  • Plan comparison: an out-of-tree harness binds every stage plan for seven stack shapes (decoder layers with and without an output transform, MHC, Nemotron-H patterns, Kimi K3, Qwen4-exp, NemotronNAS-style attention-only layers, single-stage appends) over 23 parallel configurations, four boundary reductions and one or two pipeline ranks (3,928 scenarios), and compares each bound plan leaf by leaf with the parent commit's.
  • GPU: dummy weights are seeded per TP rank and scaled (0.05, norms 1.0), so that a sum taken twice or a mixture of rank partials changes the output; the default dummy weights give every rank the same shard and would hide both.
  • GPU results below were measured on these commits as based on main 516fd1c1b5 unless marked as on the merge candidate; rebasing onto current main had no conflicts, and the CPU checks were run again after it.
  • On the merge candidate (head of this stack against main 961bcf481d, H200, asymmetric dummy weights unless noted): DeepSeek-V2-Lite input-scattered PP2 is bit-identical to PP1 (main crashes on it); Nemotron-H cut between a mixer and its FFN differs from PP1 only at the rounding level of the FFN-to-mixer cuts (main: 0.93 and 2.21); Llama, Qwen3-MoE, GPT-OSS and DeepSeek-V2-Lite are bit-identical to main at PP1 and PP2; GSM8K (real DeepSeek-V2-Lite weights, 250 questions, TP4 with attention DP2 and the dense FFN fully data-parallel) gives 0.360 at PP2, 0.364 at PP1 and 0.368 on main at PP1, medians of three runs whose run-to-run spread is 0.012–0.016. The last revision of the stack changed only comments, docstrings, an equivalent condition and explicitly set attributes after these runs; its bound plans are identical.

For this PR: the plan comparison differs only in how a pipeline rank's neighbour appears in a bound plan (a ReadFacts instead of the read object), on an edge whose read does not run on the sending rank.

Speed Tests and Profiling

No forward-path change.

Checklist


CI States

Latest PR Test (Base): 🚫 Run #38080873880
Latest PR Test (Extra): 🚫 Run #38080873610
Latest PR Test (AMD ROCm 10): 🚫 Run #38080873786

@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Oct 10, 2026
@ch-wan
ch-wan force-pushed the cheng/refactor/pp-neighbour-declarations branch from ca7d821 to 78ab7b5 Compare October 10, 2026 03:23
@ch-wan
ch-wan force-pushed the cheng/refactor/exit-decisions-at-construction branch 2 times, most recently from 0d9d24a to 6225fa8 Compare October 10, 2026 06:21
@ch-wan
ch-wan force-pushed the cheng/refactor/pp-neighbour-declarations branch 2 times, most recently from 8425495 to a39d224 Compare October 10, 2026 06:28
@ch-wan
ch-wan force-pushed the cheng/refactor/exit-decisions-at-construction branch from 6225fa8 to 06cc9bc Compare October 10, 2026 19:42
@ch-wan
ch-wan requested a review from fzyzcjy as a code owner October 10, 2026 19:42
Base automatically changed from cheng/refactor/exit-decisions-at-construction to main October 10, 2026 19:42
@mintlify

mintlify Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
sglang-doc 🔴 Failed – Oct 10, 2026, 7:43 PM

💡 Tip: Enable Automations to automatically generate PRs for you.

A stage the stack binds next to, on another pipeline rank, was the
declaration of a layer built on the meta device to read it, and every
bound stage kept its producer's declaration. A declaration holds the
stage's read, update and output transform: objects that reference the
layer's modules and state. So the neighbour layers built for a rank, and
on this rank each stage's predecessor, stayed referenced by the bound
stages for the life of the model, though binding reads only the
capabilities those objects declare.

facts_of() reduces a declaration to those capabilities (UpdateFacts,
ReadFacts, TransformFacts) without the declarations it is chained to.
A read's kernels are not among them: binding takes kernels only from the
reads of the stages it binds, so ReadFacts supplies none.
The neighbouring stages and the producer each stage records are now such
facts, which also replaces the history-free copy the stack made of each
producer. A rank whose first stage would run the output transform of an
attention on the previous rank, which this rank does not have, is
rejected; no model declares one.
To bind the first and last stages of a pipeline rank's stack, make_layers
built the neighbouring layers again on the meta device and read the
stages they declared. Building a layer can have effects that the meta
device does not stop (streams, communicators, explicit devices), and the
layer's choices had to be re-derived from modules nobody runs.

make_layers and make_pp_layers take stage_facts: the model's shared
declaration function, which returns the stages the layer at a global
index declares without building it, and which the layer uses for its
own declarations too, so both ranks derive a boundary from one source. A
neighbour query takes what the function returns (as DeclaredStages).
Under pipeline parallelism each layer a rank builds is checked against
the function, since the other ranks bind its stages from the function
alone. A model on a transitional list may still omit it and have its
neighbours built; any other model that declares stages under pipeline
parallelism must give one. Nemotron-H, whose declarations already came
from its pattern alone, gives one and leaves the list.
StageBoundary.snapshot repeated residual_batch.snapshot with one caller,
LongCat-Flash, which now calls residual_batch.snapshot like the other
models. StagePlan.produced forwarded to the path's output for one
caller. The package exported ExitDecision, FfnExit, LayerResidualOps,
NORM_READOUT, StageContract, moe_gathers_over_moe_cp and
token_axis_sizes, and imported VariantEdges, though nothing imports any
of them from the package; their modules still define them.
…y are

The layer boundary guide said a pipeline rank builds the neighbouring
layers on the meta device to read their stages, and that a stage's
declaration depends only on the config and the layer index. It now
describes the shared declaration function a model gives make_layers,
the check of each built layer against it, and the transitional list.
It gains a table of the exit policies (FFN, operation-scheduled, mixer,
stack end, pipeline handoff), with which of their decisions are fixed
when a plan is built and which are made per batch. ProducerReduction
says that each output has one completer, the boundary, except where an
FFN declares output_complete.
Seven output moves accepted **kwargs that no caller fills: an exit, a
two-batch-overlap split and a test call them with the output, the
residual and the batch, and bind the rest by name. Their signatures now
say exactly what they take.
@ch-wan
ch-wan force-pushed the cheng/refactor/pp-neighbour-declarations branch from a39d224 to be00038 Compare October 10, 2026 19:42
@ch-wan
ch-wan merged commit 3b16eee into main Oct 10, 2026
23 of 28 checks passed
@ch-wan
ch-wan deleted the cheng/refactor/pp-neighbour-declarations branch October 10, 2026 19:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant