Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/cookbook/autoregressive/GLM/GLM-5.3-Flash.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ Choose your hardware, then choose the operating point that matches your workload

Every listed hardware platform exposes both strategies. A **Verified** badge means that exact hardware and command were tested. **Final Verification In Progress** means the recipe runs and is queued for measurement on the final weights. **Not Verified** means the command is a supported starting point that still needs workload validation. A choice is disabled only when the underlying runtime combination is known to be unsupported.

The recommended selection is only a starting point. The same panel also lets you override the KV/DSA pairing, multimodal feature transport, and HiCache tiers. Changing an option that was not part of the measured command changes the badge to **Not Verified** without hiding the option.
The recommended selection is only a starting point. The same panel also lets you override the KV/DSA pairing, multimodal feature transport, Breakable Cuda Graph, and HiCache tiers. Changing an option that was not part of the measured command changes the badge to **Not Verified** without hiding the option.

**Breakable Cuda Graph** defaults to **Off**. Select **On** to add `--cuda-graph-backend-prefill breakable` to the generated command. This requires a build that includes [PR #38522](https://github.com/sgl-project/sglang/pull/38522).

import { Deployment } from "/src/snippets/_deployment.jsx";
import { config } from "/src/snippets/configs/zai-org/glm-5.3-flash.jsx";
Expand Down
31 changes: 29 additions & 2 deletions docs/src/snippets/configs/zai-org/glm-5.3-flash.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const config = {
s.kvDsaPair === pairing &&
s.mmTransport === "auto" &&
s.hicache === "off" &&
s.bcg === "off" &&
s.dcp === "off"
);
},
Expand Down Expand Up @@ -68,6 +69,20 @@ export const config = {
},
],
},
{
id: "bcg",
title: "Breakable Cuda Graph",
default: "off",
options: [
{ id: "off", label: "Off" },
{
id: "on",
label: "On",
flags: ["--cuda-graph-backend-prefill breakable"],
hints: ["Enables breakable prefill CUDA graphs; requires a build with PR #38522."],
},
],
},
{
id: "mmTransport",
title: "VLM Transport",
Expand Down Expand Up @@ -331,6 +346,7 @@ sgl-eval run gsm8k \\
nnodes: 1,
verified: true,
verificationStatus: (s) =>
s.bcg !== "off" ? "unverified" :
["bf16-tilelang", "fp8-trtllm"].includes(s.kvDsaPair) &&
s.mmTransport === "auto" &&
s.hicache === "off" &&
Expand Down Expand Up @@ -362,6 +378,7 @@ sgl-eval run gsm8k \\
nnodes: 1,
verified: true,
verificationStatus: (s) =>
s.bcg !== "off" ? "unverified" :
["bf16-tilelang", "fp8-trtllm"].includes(s.kvDsaPair) &&
s.mmTransport === "auto" &&
s.hicache === "off" &&
Expand Down Expand Up @@ -393,6 +410,7 @@ sgl-eval run gsm8k \\
nnodes: 1,
verified: true,
verificationStatus: (s) =>
s.bcg !== "off" ? "unverified" :
["bf16-tilelang", "fp8-trtllm"].includes(s.kvDsaPair) &&
s.mmTransport === "auto" &&
s.hicache === "off" &&
Expand Down Expand Up @@ -426,6 +444,7 @@ sgl-eval run gsm8k \\
nnodes: 1,
verified: true,
verificationStatus: (s) =>
s.bcg !== "off" ? "unverified" :
["bf16-tilelang", "fp8-trtllm"].includes(s.kvDsaPair) &&
s.mmTransport === "auto" &&
s.hicache === "off" &&
Expand Down Expand Up @@ -594,6 +613,7 @@ sgl-eval run gsm8k \\
nnodes: 1,
verified: true,
verificationStatus: (s) =>
s.bcg !== "off" ? "unverified" :
s.mmTransport === "auto" && s.hicache === "off"
? "verified"
: "unverified",
Expand Down Expand Up @@ -623,6 +643,7 @@ sgl-eval run gsm8k \\
nnodes: 1,
verified: true,
verificationStatus: (s) =>
s.bcg !== "off" ? "unverified" :
["off", "l2"].includes(s.hicache) ? "verified" : "unverified",
env: [],
flags: [
Expand All @@ -645,6 +666,7 @@ sgl-eval run gsm8k \\
nnodes: 1,
verified: true,
verificationStatus: (s) =>
s.bcg !== "off" ? "unverified" :
s.mmTransport === "auto" && s.hicache === "off"
? "verified"
: "unverified",
Expand Down Expand Up @@ -674,6 +696,7 @@ sgl-eval run gsm8k \\
nnodes: 1,
verified: true,
verificationStatus: (s) =>
s.bcg !== "off" ? "unverified" :
["off", "l2"].includes(s.hicache) ? "verified" : "unverified",
env: [],
flags: [
Expand All @@ -694,7 +717,8 @@ sgl-eval run gsm8k \\
match: { hw: "b200", strategy: "low-latency", quant: "fp8" },
nnodes: 1,
verified: true,
verificationStatus: (s) => (s.hicache === "off" ? "verified" : "unverified"),
verificationStatus: (s) =>
s.bcg !== "off" ? "unverified" : (s.hicache === "off" ? "verified" : "unverified"),
env: [],
flags: [
"--model-path {{MODEL_NAME}}",
Expand All @@ -720,6 +744,7 @@ sgl-eval run gsm8k \\
nnodes: 1,
verified: true,
verificationStatus: (s) =>
s.bcg !== "off" ? "unverified" :
["off", "l2"].includes(s.hicache) ? "verified" : "unverified",
env: [],
flags: [
Expand All @@ -740,7 +765,8 @@ sgl-eval run gsm8k \\
match: { hw: "b300", strategy: "low-latency", quant: "fp8" },
nnodes: 1,
verified: true,
verificationStatus: (s) => (s.hicache === "off" ? "verified" : "unverified"),
verificationStatus: (s) =>
s.bcg !== "off" ? "unverified" : (s.hicache === "off" ? "verified" : "unverified"),
env: [],
flags: [
"--model-path {{MODEL_NAME}}",
Expand All @@ -766,6 +792,7 @@ sgl-eval run gsm8k \\
nnodes: 1,
verified: true,
verificationStatus: (s) =>
s.bcg !== "off" ? "unverified" :
["off", "l2"].includes(s.hicache) ? "verified" : "unverified",
env: [],
flags: [
Expand Down
47 changes: 47 additions & 0 deletions python/sglang/srt/arg_groups/cuda_graph_hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ def disable_breakable_cudagraph_if_incompatible(server_args: Any):
rules = [
(
"KDA hybrid linear attention",
# GLM-5.3 Flash supports explicit BCG opt-in, but stays off by
# default like other KDA models. Explicit backends skip these rules.
lambda: uses_kda_attention(model_config_of(server_args).hf_config),
),
# DSV4 is BCG-compatible but introduces heavy memory pressure: the
Expand Down Expand Up @@ -399,6 +401,51 @@ def disable_prefill_cuda_graph_for_deepseek_trtllm_mla(server_args: Any):
)


def apply_glm5_chunked_prefill_default(server_args: Any):
"""Set the opted-in GLM BCG chunk default before memory budgeting."""
cfg = resolving_view(server_args)
if (
get_platform().is_cuda
and (Phase.PREFILL, "backend") in server_args._cuda_graph_config_locked
and cfg.cuda_graph_config.prefill.backend == Backend.BREAKABLE
and cfg.chunked_prefill_size is None
and "Glm5NextForConditionalGeneration"
in model_config_of(server_args).hf_config.architectures
):
declare_resolution(
server_args,
"_apply_glm5_chunked_prefill_default",
chunked_prefill_size=4096,
)


def apply_glm5_prefill_cuda_graph_policy(server_args: Any):
"""Set capture sizes for explicitly enabled GLM breakable prefill graphs."""
cfg = resolving_view(server_args)
if (
cfg.cuda_graph_config.prefill.backend != Backend.BREAKABLE
or "Glm5NextForConditionalGeneration"
not in model_config_of(server_args).hf_config.architectures
):
return
locked = server_args._cuda_graph_config_locked
if any((Phase.PREFILL, key) in locked for key in ("max_bs", "bs")):
return
# Capacity defaults have already populated buckets. Replace the unlocked
# ceiling and its buckets together.
declare_resolution(
server_args,
"_apply_glm5_prefill_cuda_graph_policy",
cuda_graph_config=with_phase(
cfg.cuda_graph_config,
Phase.PREFILL,
max_bs=4096,
bs=generate_prefill_cuda_graph_batch_sizes(4096),
),
)
apply_deepep_adjustments(server_args)


def apply_deepep_adjustments(server_args: Any):
"""Config adjustments required by the DeepEP a2a backend."""
cfg = resolving_view(server_args)
Expand Down
6 changes: 6 additions & 0 deletions python/sglang/srt/arg_groups/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ def run_resolution_pipeline(server_args: Any) -> None:
# resolution (the declarative registry materializes too late to affect
# it). Inkling opts into full-graph prefill capture here.
from sglang.srt.arg_groups.cuda_graph_hook import (
apply_glm5_chunked_prefill_default,
apply_glm5_prefill_cuda_graph_policy,
apply_inkling_prefill_cuda_graph_default,
apply_muse_glimmer_prefill_cuda_graph_max_bs_default,
disable_prefill_cuda_graph_for_deepseek_trtllm_mla,
Expand All @@ -190,6 +192,9 @@ def run_resolution_pipeline(server_args: Any) -> None:
handle_dwdp(server_args)

handle_cuda_graph_config(server_args)
# Requires the parsed backend and explicit-input locks, and must precede
# handle_gpu_memory_settings so the chunk size feeds memory budgeting.
apply_glm5_chunked_prefill_default(server_args)

# Handle device-specific backends.
from sglang.srt.arg_groups.platform_hook import (
Expand Down Expand Up @@ -261,6 +266,7 @@ def run_resolution_pipeline(server_args: Any) -> None:
handle_mamba_backend(server_args)
handle_int8_mamba_checkpoint(server_args)
handle_linear_attn_backend(server_args)
apply_glm5_prefill_cuda_graph_policy(server_args)
handle_kv4_compatibility(server_args)
handle_mxfp8_kv_cache_compatibility(server_args)
run_post_process_pass(server_args, _page_size_default)
Expand Down
3 changes: 2 additions & 1 deletion python/sglang/srt/configs/model_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2132,13 +2132,14 @@ def is_generation_model(model_architectures: List[str], is_embedding: bool = Fal
]

# Multimodal archs whose LM prefill is validated under breakable CUDA graph;
# embed-carrying batches are rejected at replay (can_run_graph) and run eager.
# replay eligibility for embed-carrying batches is checked by can_run_graph.
# The Kimi archs are structurally multimodal -- their configs always carry a
# vision_config, so is_multimodal is True even for text-only serving -- and the
# generic multimodal rule disabled prefill CG for them despite the LM prefill
# capturing cleanly.
multimodal_breakable_cuda_graph_supported_model_archs = [
"Cohere2VisionForConditionalGeneration",
"Glm5NextForConditionalGeneration",
"InternS2MobiusForConditionalGeneration",
"PaddleOCRVLForConditionalGeneration",
"Qwen3_5ForConditionalGeneration",
Expand Down
45 changes: 45 additions & 0 deletions python/sglang/srt/layers/attention/dsa/dsa_indexer_kpool.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
get_token_to_kv_pool,
)
from sglang.srt.model_executor.runner import get_is_capture_mode
from sglang.srt.model_executor.runner_backend_utils.breakable_cuda_graph import (
is_in_breakable_cuda_graph,
)
from sglang.srt.runtime_context import get_device

if TYPE_CHECKING:
Expand Down Expand Up @@ -1356,6 +1359,42 @@ def forward_cuda(
forward_batch: ForwardBatch,
layer_id: int,
return_indices: bool = True,
) -> Optional[torch.Tensor]:
if (
is_in_breakable_cuda_graph()
and forward_batch.forward_mode.is_extend_without_speculative()
):
from sglang.srt.layers.attention.dsa.kpool_prefill_cuda_graph import (
bcg_kpool_indexer_prefill_with_output,
)

# K-pool prefill plans contain request-specific tensors and launch
# counts. Like the ordinary DSA indexer, execute them eagerly and
# bridge the result into a stable buffer for captured attention.
output = torch.empty(
(
x.shape[0] if return_indices else 0,
self.index_topk + self.index_kpool - 1,
),
dtype=torch.int32,
device=x.device,
)
bcg_kpool_indexer_prefill_with_output(
self, x, q_lora, positions, output, layer_id
)
return output if return_indices else None
return self._forward_cuda_impl(
x, q_lora, positions, forward_batch, layer_id, return_indices
)

def _forward_cuda_impl(
self,
x: torch.Tensor,
q_lora: torch.Tensor,
positions: torch.Tensor,
forward_batch: ForwardBatch,
layer_id: int,
return_indices: bool = True,
) -> Optional[torch.Tensor]:
if is_hip():
from sglang.kernels.ops.attention.dsa.tilelang_kernel import act_quant
Expand All @@ -1372,6 +1411,12 @@ def forward_cuda(
and get_is_capture_mode()
and q_lora.shape[0] > 0
and q_lora.shape[0] <= DUAL_STREAM_TOKEN_THRESHOLD
# The BCG eager break must finish its indexer work before starting
# the next capture segment; keep its projections on one stream.
and not (
is_in_breakable_cuda_graph()
and forward_batch.forward_mode.is_extend_without_speculative()
)
)

# Skip DSA if the attention backend chooses to skip this batch.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""Breakable prefill bridge for the request-dependent pooled-key indexer."""

import torch

from sglang.srt.model_executor.runner_backend_utils.breakable_cuda_graph import (
eager_on_graph,
)
from sglang.srt.model_executor.runner_backend_utils.tc_piecewise_cuda_graph import (
get_tc_piecewise_forward_context,
)


def _kpool_indexer_prefill_with_output(
indexer,
x: torch.Tensor,
q_lora: torch.Tensor,
positions: torch.Tensor,
output: torch.Tensor,
layer_id: int,
) -> None:
# Metadata, write counts and cache destinations change between requests.
# Resolve the live batch inside the eager break, never from capture args.
forward_batch = get_tc_piecewise_forward_context().forward_batch
n = forward_batch.extend_num_tokens
if n is None or not 0 <= n <= x.shape[0]:
raise ValueError(f"Invalid pooled-indexer prefill token count: {n}")
if n > q_lora.shape[0] or n > positions.shape[0]:
raise ValueError("Pooled-indexer prefill inputs have inconsistent rows")
return_indices = output.shape[0] != 0
result = indexer._forward_cuda_impl(
x=x[:n],
q_lora=q_lora[:n],
positions=positions[:n],
forward_batch=forward_batch,
layer_id=layer_id,
return_indices=return_indices,
)
if not return_indices:
return
if result is None or result.shape != (n, output.shape[1]):
raise ValueError("Pooled-indexer prefill returned an unexpected top-k shape")
# The following captured attention segment reads this stable padded buffer.
output[:n].copy_(result)
output[n:].fill_(-1)


def _kpool_indexer_prefill_capture_stub(
indexer,
x: torch.Tensor,
q_lora: torch.Tensor,
positions: torch.Tensor,
output: torch.Tensor,
layer_id: int,
) -> None:
output.fill_(-1)


bcg_kpool_indexer_prefill_with_output = eager_on_graph(
True, capture_stub=_kpool_indexer_prefill_capture_stub
)(_kpool_indexer_prefill_with_output)
Loading
Loading