Skip to content

[glm] [pcp] fuse norm rope with pcp cache gather - #53173

Draft
GirasoleY wants to merge 3 commits into
vllm-project:mainfrom
GirasoleY:review/glm52-pcp-fused-norm-rope
Draft

GirasoleY wants to merge 3 commits into
vllm-project:mainfrom
GirasoleY:review/glm52-pcp-fused-norm-rope

Conversation

@GirasoleY

@GirasoleY GirasoleY commented Aug 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fuse PCP Q/K preparation, cache quantization, and NVLS publication into one dispatch kernel plus one combine kernel for prefill, mixed, and decode batches.

  • Old: bf16 norm/RoPE: output kv_c, k_pe, indexer_k → 3 all-gathers → quantize + cache-insertion
  • New: fused dispatch + NVLS multicast → local cache combine/scatter

Dispatch

It launches one 256-thread CTA per local token and performs:

  • Q RMSNorm, keeping Q local because each rank already holds the query rows it computes.
  • KV RMSNorm.
  • RoPE on k_pe.
  • For sparse layers, indexer-K LayerNorm and RoPE.
  • Packing directly into a cache-ready representation and NVLS multicast to every PCP rank.

For static E4M3, the fixed transport row is:

| kv_c E4M3: 512 B | k_pe E4M3: 64 B | indexer K E4M3: 128 B | index scale FP32: 4 B | alignment: 12 B | = 720 B/token

For fp8_ds_mla, the fixed transport row is:

| kv_c E4M3: 512 B | KV tile scales FP32: 16 B | k_pe BF16: 128 B | indexer K E4M3: 128 B | index scale FP32: 4 B | alignment: 12 B | = 800 B/token

Full workspace

workspace[double_buffer_index][src rank][max_num_tokens][payload]

Combine

The second kernel reads the received rank-major rows and scatters them into each rank's local paged caches:

  • Prefill: tokens from every PCP rank are inserted.
  • Decode: tokens are replicated, so they are inserted once from rank 0.

Scope and limitations

  • Requires CUDA and NVLS symmetric-memory multicast.
  • Specialized for the GLM-5.2 / DeepSeek-V3.2 MLA dimensions above.
  • Each rank retains a persistent double-buffered staging allocation of approximately 2 * num_ubatches * PCP_world_size * max_local_tokens * packed_row_bytes.

Performance

GLM-5.2 PCP8 on one 8×B300 node, TP1/PCP8/EP8, BF16 activations, static E4M3 KV cache, a 32K batched-token limit, and a 32K long-prefill threshold.

Default portable PCP pipeline (historical stage profile):

Stage Time
Fused Q/K norm + K RoPE 1.441 ms
Indexer-K all-gather 2.064 ms
Indexer-K quantization/insertion 0.423 ms
MLA kv_c all-gather 11.044 ms
MLA k_pe all-gather 2.349 ms
MLA quantization/insertion 6.931 ms
Total 24.252 ms

Final fused NVLS pipeline (latest two-run mean):

Stage Time
Fused norm/RoPE + FP8 quantization + NVLS multicast 8.228 ms
Cache combine/scatter 0.898 ms
Total 9.127 ms

Controlled current-stack full-forward A/B from the Kineto execute_context_1(32768)_generation_0(0) GPU annotation:

Metric Default portable PCP Final fused NVLS Change
Mean across PCP ranks 357.781 ms 343.875 ms −3.89%
Slowest PCP rank 357.933 ms 344.109 ms −3.86%

Each value is the arithmetic mean of two fresh-process runs. The portable baseline and pre-optimization fused path were measured in one current-stack ABBA; the final fused kernel was then measured in a second ABBA against that fused control. All runs used the same B300 node, model snapshot, source tree, eager-mode configuration, exact 32K input, warmup, and profiler procedure. The second ABBA measured the slowest-rank fused forward at 345.069 ms before these cache-overhead changes and 344.109 ms after them (−0.960 ms, −0.28%). "Default portable PCP" means the Triton fused norm/RoPE + PCP gather + cache-insert path.

Relationship to existing work

This PR uses a different design from #52863/#49517: NVLS multicast into transient symmetric staging followed by a receiver-local paged-cache combine. It keeps cache tensors, allocation, and ownership ordinary and receiver-local, supports PCP2/4/8, and integrates with the common DeepSeek-V3.2 PCP path.

Assisted-by: OpenAI Codex
Signed-off-by: Summer Yang <girasoleyang@gmail.com>
Signed-off-by: Summer Yang <girasoleyang@gmail.com>
@mergify mergify Bot added ci/build deepseek Related to DeepSeek models labels Aug 20, 2026
@GirasoleY GirasoleY changed the title [Kernel][PCP] Fuse cache dispatch with NVLS multicast [glm] [pcp] fuse norm rope with pcp cache gather Aug 20, 2026
@mergify mergify Bot added the glm label Aug 20, 2026
@GirasoleY
GirasoleY force-pushed the review/glm52-pcp-fused-norm-rope branch from 5647ce6 to b2e2996 Compare August 21, 2026 01:33
@mergify

mergify Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @GirasoleY.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify Bot added the needs-rebase label Aug 21, 2026
Reduce fused PCP dispatch and combine overhead. Reuse the first indexer reduction barrier while keeping reduction results in a dedicated shared slot, and cap the q=2048 static-E4M3 dispatch specializations at 32 registers on CUDA 12.9 and newer.

Remove unused KV and K-pe output materialization while preserving BF16 rounding, restrict the system fence to the NVLS writer warp, and process four independent cache rows per combine CTA. Update the op schema, callers, and distributed NVLS correctness coverage accordingly.

Signed-off-by: Summer Yang <girasoleyang@gmail.com>
@GirasoleY
GirasoleY force-pushed the review/glm52-pcp-fused-norm-rope branch from 94582a5 to 21575ef Compare August 21, 2026 22:46
LucasWilkinson added a commit to LucasWilkinson/vllm that referenced this pull request Sep 8, 2026
…/RoPE merge

The vllm-project#53173 merge moved the index_k_out allocation into the has_indexer branch
only; layers without an indexer (skip_topk) hit UnboundLocalError in
fused_norm_rope.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant