Skip to content

[BCG][GLM5] perf: BCG support and prefill enhancements - #27053

Merged
Fridge003 merged 83 commits into
sgl-project:mainfrom
mattteochen:bcg-pcg-nsa
Jun 24, 2026
Merged

[BCG][GLM5] perf: BCG support and prefill enhancements#27053
Fridge003 merged 83 commits into
sgl-project:mainfrom
mattteochen:bcg-pcg-nsa

Conversation

@mattteochen

@mattteochen mattteochen commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Motivation

#23351 introduced PCG for GLM5 (nsa_indexer). As the current implementation runs the full indexer path, which is not fully captured by a CUDA graph, we found that running the fast indexer in eager mode yields better performance.

Moreover, the current PCG split creates a CUDA graph island with only 1 kernel, introducing unnecessary overhead during dispatch.

After 2048 tokens, PCG is disabled; the full path is being run eagerly in any case.

Modifications

  • BCG support for NSA models
  • Run the fast nsa idexer (only k) in eager mode, marking it as a split op (opt in with env variable)
  • Registered another custom op with bmm + unified_attention_with_output to avoid single cuda graph with only bmm (opt in with env variable)
  • Updated the breakable cuda graph handler to merge adjacent eager Modules (this optimisation was also implemented for PCG, but then removed to reduce this PR code changes, as PCG is planned to be deprecated)

Accuracy Tests

PCG:

gpqa:
====================
Repeat: 6, mean: 0.863
Scores: ['0.833', '0.854', '0.884', '0.879', '0.869', '0.859']
Mean latency: 14037.766 s
====================

BCG:

====================
Repeat: 6, mean: 0.854
Scores: ['0.848', '0.854', '0.869', '0.869', '0.838', '0.848']
Mean latency: 15453.834 s
====================

Speed Tests and Profiling

Tested on B200 and INTEL(R) XEON(R) PLATINUM 8570.

1k1k

Pareto for 1k1k on B200 TP8: ~4% speedup over the PCG solution at max concurrency:
1k1k

Median TTFT for 1k1k sees 10% improvement at max conc (not in pareto).

Benchmark table (pcg is main, bcg is current branch with the proposed optimizations):

series                            conc       TPS/user (sp)    out TPS/GPU (sp)  total TPS/GPU (sp)        TTFT ms (sp)
----------------------------------------------------------------------------------------------------------------------
pcg                                  4      77.98 ( 1.00x)      37.83 ( 1.00x)      76.18 ( 1.00x)      88.74 ( 1.00x)
pcg                                  8      67.71 ( 1.00x)      65.51 ( 1.00x)     130.69 ( 1.00x)      90.34 ( 1.00x)
pcg                                 16      57.05 ( 1.00x)     110.08 ( 1.00x)     219.48 ( 1.00x)      94.93 ( 1.00x)
pcg                                 32      46.23 ( 1.00x)     180.36 ( 1.00x)     360.19 ( 1.00x)     101.57 ( 1.00x)
pcg                                 64      35.38 ( 1.00x)     277.00 ( 1.00x)     554.86 ( 1.00x)     110.96 ( 1.00x)
pcg                                128      26.14 ( 1.00x)     411.39 ( 1.00x)     822.61 ( 1.00x)     150.53 ( 1.00x)
pcg                                256      18.53 ( 1.00x)     568.99 ( 1.00x)    1137.42 ( 1.00x)     173.76 ( 1.00x)
bcg                                  4      77.96 ( 1.00x)      37.85 ( 1.00x)      76.21 ( 1.00x)      78.18 ( 1.14x)
bcg                                  8      67.65 ( 1.00x)      65.23 ( 1.00x)     130.14 ( 1.00x)      81.20 ( 1.11x)
bcg                                 16      57.30 ( 1.00x)     110.26 ( 1.00x)     219.83 ( 1.00x)      85.73 ( 1.11x)
bcg                                 32      46.88 ( 1.01x)     182.45 ( 1.01x)     364.37 ( 1.01x)      92.28 ( 1.10x)
bcg                                 64      36.07 ( 1.02x)     282.54 ( 1.02x)     565.94 ( 1.02x)     101.96 ( 1.09x)
bcg                                128      26.88 ( 1.03x)     422.05 ( 1.03x)     843.92 ( 1.03x)     133.58 ( 1.13x)
bcg                                256      19.16 ( 1.03x)     592.42 ( 1.04x)    1184.26 ( 1.04x)     157.74 ( 1.10x)

8k1k

We also see some small improvement (1% at conc 256) in the high throughput region when extending the maximum number of tokens captured --piecewise-cuda-graph-max-tokens 8192:

series                            conc       TPS/user (sp)    out TPS/GPU (sp)  total TPS/GPU (sp)        TTFT ms (sp)
----------------------------------------------------------------------------------------------------------------------
pcg                                  4      69.62 ( 1.00x)      33.04 ( 1.00x)     303.93 ( 1.00x)     363.94 ( 1.00x)
pcg                                  8      56.69 ( 1.00x)      54.55 ( 1.00x)     496.91 ( 1.00x)     372.52 ( 1.00x)
pcg                                 16      44.28 ( 1.00x)      85.08 ( 1.00x)     768.08 ( 1.00x)     375.85 ( 1.00x)
pcg                                 32      32.30 ( 1.00x)     124.63 ( 1.00x)    1123.67 ( 1.00x)     382.08 ( 1.00x)
pcg                                 64      21.34 ( 1.00x)     164.79 ( 1.00x)    1490.29 ( 1.00x)     393.82 ( 1.00x)
pcg                                128      13.48 ( 1.00x)     208.54 ( 1.00x)    1877.57 ( 1.00x)     628.17 ( 1.00x)
pcg                                256      12.08 ( 1.00x)     216.51 ( 1.00x)    1947.91 ( 1.00x)   61385.06 ( 1.00x)
bcg                                  4      69.40 ( 1.00x)      32.95 ( 1.00x)     303.12 ( 1.00x)     363.75 ( 1.00x)
bcg                                  8      56.57 ( 1.00x)      54.48 ( 1.00x)     496.33 ( 1.00x)     369.62 ( 1.01x)
bcg                                 16      44.20 ( 1.00x)      84.97 ( 1.00x)     767.09 ( 1.00x)     373.38 ( 1.01x)
bcg                                 32      32.17 ( 1.00x)     124.25 ( 1.00x)    1120.26 ( 1.00x)     382.45 ( 1.00x)
bcg                                 64      21.39 ( 1.00x)     166.57 ( 1.01x)    1506.45 ( 1.01x)     392.32 ( 1.00x)
bcg                                128      13.48 ( 1.00x)     209.55 ( 1.00x)    1886.69 ( 1.00x)     634.72 ( 0.99x)
bcg                                256      12.11 ( 1.00x)     218.67 ( 1.01x)    1967.28 ( 1.01x)   61322.18 ( 1.00x)

Although 8k1k Pareto is not host-bound anymore, we can enable PCG/BCG to achieve stream overlap for MoE:
image

This distribution, incorporating the full sweep of concurrency [4, 8, 16, 32, 64, 128, 256] shows how capturing max tokens 8192 is enough to trigger the BCG path for most of the prefill batches (memory overhead between default value 2k and 8k is 2.71 GB/GPU):
image

BCG empty cuda grah removal

Removing empty cuda graph islands brings very little perf gain, at least for GLM5 where there's only 1 of such case:
A single batch 1k1k benchmark:

Prefill. latency: 0.05460 --> Prefill. latency: 0.0540

It's useful to hide empty graph warnings:

/usr/local/lib/python3.12/dist-packages/torch/cuda/graphs.py:126: UserWarning: The CUDA Graph is empty. This usually means that the graph was attempted to be captured on wrong device or stream. (Triggered internally at /pytorch/aten/src/ATen/cuda/CUDAGraph.cpp:147.)
  super().capture_end()

Checklist

Review and Merge Process

  1. Ping Merge Oncalls to start the process. See the PR Merge Process.
  2. Get approvals from CODEOWNERS and other reviewers.
  3. Trigger CI tests with comments or contact authorized users to do so.
    • Common commands include /tag-and-rerun-ci, /tag-run-ci-label, /rerun-failed-ci
  4. After green CI and required approvals, ask Merge Oncalls or people with Write permission to merge the PR.

CI States

Latest PR Test (Base): ✅ Run #28023999062
Latest PR Test (Extra): ✅ Run #28126718160

github-actions Bot and others added 30 commits May 1, 2026 13:24
…ed eager submodules, improving performance. Added a new composite operation `mla_bmm_then_unified_attention`.
…ved documentation and assertions for `num_tokens`.
… ensuring correct handling of adjacent split operations and their interactions with non-split nodes. Introduced helper functions for graph creation and target extraction.
…_ENABLE_PCG_DSA_EAGER_FUSION` environment variable. Updated `dsa_indexer` and `mla_bmm_then_unified_attention` to conditionally register split operations based on this setting, enhancing performance for CUDA environments.
…LE_PCG_DSA_EAGER_FUSION` environment variable to the test configuration
This reverts commit cefa693.
This reverts commit 7576d1f.
# Conflicts:
#	python/sglang/srt/layers/attention/dsa/dsa_indexer.py
- split op reuses compiled _get_logits_head_gate instead of an inlined copy
- rename dsa_indexer_pcg_dispatch -> dsa_indexer_graph_dispatch (serves PCG + BCG)
@mattteochen

Copy link
Copy Markdown
Contributor Author

Thanks @Oasis-Git.

Agreed that the previous version made this look more complex than it needed to be.

The dispatch is intentional, but I changed the design so it is no longer hidden behind an env flag. Under PCG/BCG graph capture this split-op path is now the default because it was the best-performing option in benchmarks. In particular, the indexer can make the k-only decision eagerly inside the split op; for small OSL this avoids running the full path and reduces the number of kernels.

So the split op is not an extra eager fast path for the normal execution path. It replaces the in-graph non-CP prefill path only:

  • eager/non-graph forward_cuda still runs the normal sequence;
  • graph non-CP prefill goes through the split op by default;
  • DSA prefill CP remains excluded;
  • the env gate was removed because this is now the default graph-capture policy.

I also cleaned up the integration according to your readability comments: inlined the context helper, moved the PCG/BCG split-op functions to the bottom of the file, and named the BCG wrappers explicitly.

@mattteochen
mattteochen requested a review from Oasis-Git June 17, 2026 13:25
@mattteochen
mattteochen requested a review from rainj-me as a code owner June 19, 2026 08:15

@Oasis-Git Oasis-Git left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general it is good to me now. I suggest to move the bcg part in another pr so we can think about how to fix this gap more gracefully. Thanks for your contribution

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is clean to me. This file should be done.

self.cuda_graph._segments.append(graph)

def _end_current_segment(self) -> None:
def _end_current_segment(self, drop_empty: bool = False) -> bool:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my perspective I think in this pr we do not need to implement this function or at leaset, split it into next pr. I am still thinking about the implementation code style.

@Oasis-Git Oasis-Git left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general it is good to me now. Fix the comments mentioned.

Comment thread python/sglang/srt/models/deepseek_v2.py Outdated
@Oasis-Git

Copy link
Copy Markdown
Collaborator

cc @Fridge003 for mla file check.

@mattteochen

Copy link
Copy Markdown
Contributor Author

/rerun-failed-ci

4 similar comments
@mattteochen

Copy link
Copy Markdown
Contributor Author

/rerun-failed-ci

@mattteochen

Copy link
Copy Markdown
Contributor Author

/rerun-failed-ci

@mattteochen

Copy link
Copy Markdown
Contributor Author

/rerun-failed-ci

@mattteochen

Copy link
Copy Markdown
Contributor Author

/rerun-failed-ci

@Fridge003

Copy link
Copy Markdown
Collaborator

Hi @mattteochen can this work on GLM5.2 model?

@nvpohanh

Copy link
Copy Markdown
Collaborator

/rerun-failed-ci

@mattteochen

mattteochen commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

Hi @mattteochen can this work on GLM5.2 model?

@Fridge003 yes, it works with GLM5.2 by setting --cuda-graph-backend-prefill=breakable and SGLANG_ENABLE_PCG_DSV2_DUAL_STREAM=1.

Perf gain is replicated: 1 batch prefill bench 1k1k 0.06s --> 0.045s (dummy weights).

@Fridge003
Fridge003 merged commit d5e9176 into sgl-project:main Jun 24, 2026
675 of 781 checks passed
@Fridge003 Fridge003 added the release-highlight Candidate PR for release note highlight label Jun 24, 2026
b8zhong pushed a commit that referenced this pull request Jun 24, 2026
Reconcile the DSA indexer dispatch with main's #27053 (BCG + graph
split-op-surface prefill): make pcg/bcg_dsa_indexer_prefill_split and the
_fused_q/_fused_k store helpers fusion-aware (num_tokens/out_cache_loc graph
contract, single-stream under capture), adopt main's logits_head_gate_graph /
in_piecewise_or_breakable_cuda_graph, and drop the now-unused
k_cache_and_topk_result split op. Fusion-off behavior is identical to main.
@nvpohanh

Copy link
Copy Markdown
Collaborator

cc @nvjullin

Chronostasys pushed a commit to MindLab-Research/sglang that referenced this pull request Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

deepseek documentation Improvements or additions to documentation piecewise-cuda-graph release-highlight Candidate PR for release note highlight run-ci run-ci-extra

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants