Skip to content

[Feature] Megatron LayerNorm sequence parallelism (--enable-layernorm-sp) - #30915

Merged
Fridge003 merged 2 commits into
sgl-project:mainfrom
MartinHua:feat/layernorm-sp-generalize
Sep 1, 2026
Merged

Fridge003 merged 2 commits into
sgl-project:mainfrom
MartinHua:feat/layernorm-sp-generalize

Conversation

@MartinHua

@MartinHua MartinHua commented Jul 12, 2026 •

Copy link
Copy Markdown
Contributor

[Feature] Megatron LayerNorm sequence parallelism (--enable-layernorm-sp)

What

Megatron-style LayerNorm sequence parallelism (arXiv:2205.05198) for prefill under pure tensor parallelism: the row-parallel all_reduce becomes reduce_scatter (g-bar) + all_gather (g), so LayerNorm/residual run on 1/tp of the tokens -- less activation memory, and no extra communication volume, since the two forms move the same bytes. Both collectives are fused with the adjacent GEMMs via torch symmetric memory. Off by default and byte-identical when off.

Design

All SP logic lives in the new layers/layernorm_sp.py. There are no changes under srt/models/ and none to dp_attention.py.

  • LayerCommunicator owns the region: it entry-scatters on the first decoder layer, then delegates prepare_attn / prepare_mlp / postprocess_layer to an all-SCATTERED sibling.
  • Row/ColumnParallelLinear auto-participate in g / g-bar while the region is active, so no per-model marking.
  • LogitsProcessor exit-gathers before the LM head.
  • flags.sp.enabled is materialized once per worker by initialize_layernorm_sp, beside initialize_dp_attention.
  • Enabling another family is one line in SP_SUPPORTED_ARCHITECTURES plus validation on hardware.

The exit gather recomputes its condition from config + forward_mode rather than reading the per-forward sp_active flag: that flag is written by Python inside the CUDA-graph-captured region, so it is stale on replay, and reading it there fed sequence-sharded hidden states to the LM head.

Requirements

arg_groups/layernorm_sp_hook.py rejects these at startup, from the resolution pipeline: an architecture outside SP_SUPPORTED_ARCHITECTURES (Qwen3ForCausalLM; others lack the LayerCommunicator and participant-linear wiring), tp_size <= 1, --enable-dp-attention (SP shards over the full TP group, which under DP attention spans groups holding different sequences), and speculative decoding (the captured aux hidden states would be sequence-sharded).

Silent gates, not errors: prefill (EXTEND) only, and the fused path needs unquantized bias-free bf16/fp16 linears, falling back to plain collectives otherwise. NVLink/NVSwitch is required in practice but not yet enforced.

Tests

test/registered/unit/layers/test_layernorm_sp.py (CPU) covers the allowlist, the prefill-only rule, the four guards, and a regression case pinning that the exit-gather predicate ignores sp_active. test/registered/models_e2e/test_layernorm_sp.py (2 GPU) runs Qwen3-8B with --tp 2 --enable-layernorm-sp against a GSM8K threshold.

Results

GSM8K is Qwen3-8B, tp=2, 1319 questions; prefill latency is the same model at 16K. Accuracy deltas are fp-reordering noise.

  • 2xH100 (this revision): GSM8K 0.904 off / 0.906 on; prefill 0.28173 -> 0.27180 s (-3.5%).
  • 2xB200 (sm100, torch 2.13): GSM8K 0.909 off / 0.908 on; prefill 0.14388 -> 0.13589 s (-5.6%). The fused symmetric-memory kernels are numerically correct on sm100 and are what delivers the win there -- forced off, SP measures 0.1421-0.1442 s, neutral against baseline. An FP8 checkpoint (quantized, so the plain-collective fallback) also serves correctly.
  • 8xH100, an earlier revision of this branch: -16.5% prefill latency and -27.7% activation memory (Qwen3-32B, 32K), and no recompile storm under piecewise-prefill compile (44 vs 40 baseline). Not re-run on the current revision.

The gain scales with TP degree.

Follow-ups

  • More model families (one allowlist line each, plus validation).
  • B300 (B200 / sm100 is covered above; both report capability major 10, so the same code paths apply).
  • Further out: DP-attention-aware SP, speculative decoding, an explicit NVLink precondition, chunked/split-prefill.

CI States

Latest PR Test (Base): ❌ Run #33325891316
Latest PR Test (Extra): ❌ Run #33325891165
Latest PR Test (AMD ROCm 7.2): ❌ Run #33325891318

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@MartinHua
MartinHua force-pushed the feat/layernorm-sp-generalize branch from 694aec5 to ba0f52f Compare July 12, 2026 05:31
@MartinHua
MartinHua marked this pull request as ready for review August 2, 2026 06:12
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

Comment thread python/sglang/srt/server_args.py Resolved
Comment thread python/sglang/benchmark/one_batch.py Resolved
Comment thread python/sglang/srt/models/qwen2.py Resolved
@Fridge003

Copy link
Copy Markdown
Collaborator

Please solve the conflicts

@MartinHua
MartinHua force-pushed the feat/layernorm-sp-generalize branch 7 times, most recently from bea9beb to b9486c9 Compare August 12, 2026 03:31
Comment thread python/sglang/srt/runtime_context.py Resolved
Comment thread python/sglang/srt/server_args.py Resolved
@MartinHua
MartinHua force-pushed the feat/layernorm-sp-generalize branch 6 times, most recently from f7333c4 to 0a4e746 Compare August 29, 2026 03:59
@MartinHua
MartinHua requested a review from yizhang2077 as a code owner August 29, 2026 03:59
@Fridge003 Fridge003 self-assigned this Aug 29, 2026
@Fridge003 Fridge003 added the release-highlight Candidate PR for release note highlight label Aug 29, 2026
@Fridge003 Fridge003 added the run-ci CI: run the baseline test suite on this PR label Aug 29, 2026
@MartinHua
MartinHua force-pushed the feat/layernorm-sp-generalize branch 2 times, most recently from 1f7429a to 6bcba46 Compare August 29, 2026 19:56
Generic Megatron-style LayerNorm SP under pure tensor parallelism
(arXiv:2205.05198), fully decoupled from model code: no changes under
python/sglang/srt/models/ and none to dp_attention.py. The row-parallel
all-reduce is split into reduce-scatter (g-bar) + all-gather (g) so the
LayerNorm/residual regions run on sequence-sharded activations, cutting prefill
activation memory with no extra communication volume. Prefill (EXTEND) only;
Qwen3 dense; off by default and byte-identical when off.

All SP logic lives in the new layers/layernorm_sp.py: the Qwen3 allowlist, the
config guards, the per-forward sp_active flag, the entry-scatter / exit-gather
collectives, and the fused g / g-bar matmul fast-paths (torch symmetric memory,
unquantized bf16/fp16, with plain collective fallbacks).

Integration is entirely in the shared layers:
- LayerCommunicator builds an all-SCATTERED sibling when the model is SP-enabled
  and delegates prepare_attn / prepare_mlp / postprocess_layer to it while the
  region is active (LayerNorm on the shard, no collectives). It also owns the
  region: on the first decoder layer it re-evaluates activation and entry-scatters,
  so the state is self-correcting each forward. The all-SCATTERED case is added to
  CommunicateWithAllReduceAndLayerNormFn: under pure TP attn_tp_size == tp_size > 1,
  so the existing same-group-size shortcut does not cover it.
- Row/ColumnParallelLinear auto-participate while sp_active (no per-model
  marking): column does the g all-gather+matmul then narrows to the real token
  count; row does the matmul+g-bar reduce-scatter.
- LogitsProcessor exit-gathers back to the full sequence before the LM head.
  It recomputes the decision from config + forward mode and takes the token count
  from input_ids rather than reading sp_active / module state, because it can sit
  outside the CUDA-graph-captured region, where Python writes made inside that
  region do not re-execute on replay and would read stale.

ServerArgs.check_server_args validates the flag (supported architecture,
tp_size > 1, not --enable-dp-attention, not speculative decoding) so an
unsupported model fails loud instead of silently ignoring it.
Unit test (no server): the Qwen3 allowlist, the prefill-only activation rule, the
four config guards, and that the exit-gather predicate stays independent of the
sp_active flag (the flag is stale on CUDA-graph replay).

E2E test: Qwen3-1.7B with --tp 2 --enable-layernorm-sp, GSM8K accuracy above a
threshold -- SP must not regress model output.
@MartinHua
MartinHua force-pushed the feat/layernorm-sp-generalize branch from 6bcba46 to fa37892 Compare August 30, 2026 17:39
@Fridge003
Fridge003 merged commit 562b661 into sgl-project:main Sep 1, 2026
251 of 289 checks passed
@MartinHua
MartinHua deleted the feat/layernorm-sp-generalize branch September 1, 2026 23:51
StevenChenSE pushed a commit to StevenChenSE/sglang that referenced this pull request Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-highlight Candidate PR for release note highlight run-ci CI: run the baseline test suite on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants