执行摘要
- 一句话:DSV4 目标预填充提前发布共享读完成事件以提升重叠调度
- 推荐动作:值得精读。该 PR 是投机解码调度重叠的重要一环,展示了如何通过“提前快照共享读 + 声明 PRE_REPLAY 边界”来安全地放宽 WAR 屏障,对理解 DSV4 的 sparse prefill 数据流和 CUDA graph 元数据生命周期有直接帮助。建议结合 #34816 和 #34515 一起阅读,把握完整的调度重叠演进路径。但需关注合并后 CI 回归的修复情况,确认
token_to_kv_pool 问题已解决后再合入主线。
功能与动机
DSV4 sparse prefill 此前在第一个 attention 层才懒构建 request-to-token 与 Full-to-SWA 快照,PR body 指出“The overlap scheduler therefore could not safely release or mutate those shared inputs at the metadata boundary and had to retain them through a coarser forward boundary.” 由于 DFLASH/DSPARK 没有单独的 prefill draft-extend 读取器,对非 CP 目标 prefill,在发布 PRE_REPLAY 边界前物化快照可以避免与 sparse-prefill 读取器竞争,同时让后续调度工作与目标模型计算重叠。本 PR 是 #34816 的 target-prefill 对应物,从 #34515 的集成性能工作中提取,最终为移除所有阻塞后的 40% E2E 吞吐提升做出贡献。
实现拆解
按以下 4 步拆解实现:
- 后端扩展(deepseek_v4_backend.py):在
DSV4Metadata 中新增 prefill_shared_reads_snapshotted 布尔标志,并在 copy_ / refresh_for_breakable_cuda_graph_replay_ 中重置,避免 replay 残留污染;新增 prepare_prefill_shared_read_snapshot 方法,在满足环境变量开关、EXTEND 模式、spec_algorithm.is_dflash_family() 且非 CP-v2 时,若属 sparse prefill(num_qo_tokens > _LARGE_INDEXER_QUERY_THRESHOLD 或显式开启 SGLANG_OPT_FLASHMLA_SPARSE_PREFILL)则调用新增的 _build_sparse_prefill_chunk_cache 构建 chunk cache,并将 prefill_shared_reads_snapshotted 置为 True;shared_read_ends 在 EXTEND 且已快照时返回 SharedReadEnds.PRE_REPLAY。
- Runner 接入(eager_runner.py、prefill_cuda_graph_runner.py):在
init_forward_metadata 之后立即调用 prepare_prefill_shared_read_snapshot,并随后调用 maybe_publish_prefill_shared_read_done;prefill CUDA graph runner 使用 padding 后的 token 数作为 num_qo_tokens,确保与 graph replay 的几何尺寸一致。
- 事件门控(shared_read_event.py、base_attn_backend.py):
maybe_publish_prefill_shared_read_done 的投机算法判断从“仅无投机”放宽为“无投机或 DFLASH 家族”,其余投机算法(如 EAGLE/MTP)因存在后续 draft-extend 读取器而保持保守粗屏障;base_attn_backend.py 新增默认 no-op 的 prepare_prefill_shared_read_snapshot 钩子,保证未实现该语义的后端不会误声明 PRE_REPLAY。
- 测试配套:在
test_deepseek_v4.py 新增 test_prefill_snapshot_declares_pre_replay_boundary、test_snapshot_builds_cache_only_for_sparse_prefill、test_sparse_prefill_snapshot_marks_success_only_after_build 三个单元测试,覆盖快照声明、dense/sparse 分支、构建成功后才标记;在 test_prefill_cuda_graph_padding.py 新增 test_replay_snapshot_uses_padded_token_count,验证 replay 使用 padding 后的 token 数调用快照;在 test_prefill_shared_read_done.py 新增 test_dflash_family_target_prefill_publishes,验证 DFLASH 家族事件发布路径。
关键文件:
python/sglang/srt/layers/attention/deepseek_v4_backend.py(模块 注意力后端;类别 source;类型 core-logic;符号 prepare_prefill_shared_read_snapshot, _build_sparse_prefill_chunk_cache, shared_read_ends, DSV4Metadata): 核心实现文件:新增 prepare_prefill_shared_read_snapshot 与 _build_sparse_prefill_chunk_cache,扩展 DSV4Metadata 状态并修改 shared_read_ends 以声明 PRE_REPLAY 边界,是本次优化最关键的行为变更点。
python/sglang/srt/model_executor/runner/eager_runner.py(模块 运行器;类别 source;类型 data-contract;符号 _execute_extend): 在 eager 执行的元数据初始化之后接入快照与事件发布,是快速路径在 eager 模式下的入口。
python/sglang/srt/model_executor/runner_utils/shared_read_event.py(模块 事件发布;类别 source;类型 data-contract;符号 maybe_publish_prefill_shared_read_done): 事件发布门控逻辑的核心文件:将投机算法条件放宽到 DFLASH 家族,保留 EAGLE/MTP 的粗屏障,决定 PRE_REPLAY 事件是否最终发布。
python/sglang/srt/layers/attention/base_attn_backend.py(模块 注意基类;类别 source;类型 core-logic;符号 prepare_prefill_shared_read_snapshot): 为所有 attention 后端定义默认 no-op 的 prepare_prefill_shared_read_snapshot 钩子,保证未实现该语义的后端不会误声明 PRE_REPLAY。
python/sglang/srt/model_executor/runner/prefill_cuda_graph_runner.py(模块 图谱运行器;类别 source;类型 data-contract): CUDA graph 模式下同样接入快照与事件发布,确保 replay 路径保持一致的 PRE_REPLAY 语义。
test/registered/attention/unittests/dsv4/test_deepseek_v4.py(模块 DSV4测试;类别 test;类型 test-coverage;符号 test_prefill_snapshot_declares_pre_replay_boundary, test_snapshot_builds_cache_only_for_sparse_prefill, test_sparse_prefill_snapshot_marks_success_only_after_build): 新增三个单元测试,覆盖快照声明、dense/sparse 分支、构建成功后才标记,是本次核心语义的回归防线。
test/registered/unit/model_executor/runner/test_prefill_cuda_graph_padding.py(模块 图谱填充测试;类别 test;类型 test-coverage;符号 test_replay_snapshot_uses_padded_token_count): 验证 CUDA graph replay 使用 padding 后的 token 数调用快照,保证 replay 几何与捕获一致。
test/registered/unit/model_executor/runner/test_prefill_shared_read_done.py(模块 共享读测试;类别 test;类型 test-coverage;符号 test_dflash_family_target_prefill_publishes): 验证 DFLASH 家族在 target prefill 场景能正确发布共享读完成事件,补充事件门控的测试覆盖。
关键符号:prepare_prefill_shared_read_snapshot, _build_sparse_prefill_chunk_cache, maybe_publish_prefill_shared_read_done, shared_read_ends, test_prefill_snapshot_declares_pre_replay_boundary, test_snapshot_builds_cache_only_for_sparse_prefill, test_sparse_prefill_snapshot_marks_success_only_after_build, test_replay_snapshot_uses_padded_token_count, test_dflash_family_target_prefill_publishes
关键源码片段
python/sglang/srt/layers/attention/deepseek_v4_backend.py
核心实现文件:新增 prepare_prefill_shared_read_snapshot 与 _build_sparse_prefill_chunk_cache,扩展 DSV4Metadata 状态并修改 shared_read_ends 以声明 PRE_REPLAY 边界,是本次优化最关键的行为变更点。
def prepare_prefill_shared_read_snapshot(
self, forward_batch: ForwardBatch, *, num_qo_tokens: int
) -> None:
# 先重置快照标记,避免上次 replay 残留影响本次判定。
metadata = self.forward_metadata
if isinstance(metadata, DSV4Metadata):
metadata.prefill_shared_reads_snapshotted = False
# 仅当启用 WAR 优化、处于 EXTEND 模式、DFLASH 家族且非 CP-v2 时才快照。
snapshot_shared_prefill_reads = (
envs.SGLANG_ENABLE_PREFILL_WAR_READ_DONE.get()
and forward_batch.forward_mode == ForwardMode.EXTEND
and self.model_runner.spec_algorithm.is_dflash_family()
and not is_cp_v2_active(forward_batch)
)
if not snapshot_shared_prefill_reads:
return
assert isinstance(metadata, DSV4Metadata)
# 稀疏 prefill 需要额外构建 chunk cache,否则只声明已快照。
use_sparse_prefill = not _is_sm120 and (
num_qo_tokens > _LARGE_INDEXER_QUERY_THRESHOLD
or envs.SGLANG_OPT_FLASHMLA_SPARSE_PREFILL.get()
)
if use_sparse_prefill:
metadata.sparse_prefill_cache = self._build_sparse_prefill_chunk_cache(
forward_batch, num_qo_tokens=num_qo_tokens
)
# 无论 dense 还是 sparse,都标记共享读已完成快照,从而允许
# shared_read_ends 向调度器声明 PRE_REPLAY 边界。
metadata.prefill_shared_reads_snapshotted = True
python/sglang/srt/model_executor/runner_utils/shared_read_event.py
事件发布门控逻辑的核心文件:将投机算法条件放宽到 DFLASH 家族,保留 EAGLE/MTP 的粗屏障,决定 PRE_REPLAY 事件是否最终发布。
def maybe_publish_prefill_shared_read_done(
model_runner, forward_batch, device_module
) -> None:
"""在合规的元数据初始化后发布 prefill 读完成事件。"""
if not envs.SGLANG_ENABLE_PREFILL_WAR_READ_DONE.get():
return
if forward_batch.forward_mode != ForwardMode.EXTEND:
return
# 除 DFLASH 家族外,其他投机算法可能还有后续 draft-extend 读取器,
# 保持保守屏障,避免提前释放共享输入导致数据竞争。
if (
not model_runner.spec_algorithm.is_none()
and not model_runner.spec_algorithm.is_dflash_family()
):
return
# 记录点位于 replay 准备之后,因此只支持 PRE_REPLAY 边界。
declared = model_runner.attn_backend.shared_read_ends(
forward_batch.forward_mode
)
if declared is not SharedReadEnds.PRE_REPLAY:
return
logger.info_once(
"Prefill shared-read-done fastpath active (%s)",
type(model_runner.attn_backend).__name__,
)
read_done = device_module.Event()
read_done.record()
model_runner.shared_read_done_event = read_done
评论区精华
审查中无 inline review 评论,但 Issue 评论中有三条关键讨论:
- nvpohanh 报告 CI 回归:本 PR 合并后导致
test_q8kv8_sparse_prefill_backend.py 中四个测试失败,报错 AttributeError: 'DeepseekV4AttnBackend' object has no attribute 'token_to_kv_pool',并留言“This broke CI 😢 ... I am fixing it.”。这提示该 PR 或 main 合并带入了对 DeepseekV4AttnBackend 接口的破坏,需要后续修复。
- YAMY1234 提供隔离验证:在 Qwen3.5 AgentX 工作负载上,Total TPS/GPU 为 116382.57 vs 117919.87 baseline(-1.304%),P90 TPS/User 为 108.631 vs 108.131(+0.462%),均在 3% 阈值内;但明确说明 Qwen3.5 使用 trtllm_mha,本 PR 修改的 DSV4 DFLASH 路径未被激活,因此该验证只确认隔离 cherry-pick 无回归,不验证 DSV4 主动路径性能。
-
hnyls2002 触发 rerun:请求 rerun test_deepseek_v4.py、test_prefill_shared_read_done.py、test_prefill_cuda_graph_padding.py,相应 CI 在 4-gpu-b200 与 1-gpu-h100 上全部通过。
-
合并后 CI 回归:DeepseekV4AttnBackend 缺失 token_to_kv_pool (correctness): 本 PR 或 main 合并引入了对 DeepseekV4AttnBackend 接口的破坏,需要额外修复;截至 PR 合并时仍未解决。
- Qwen3.5 AgentX 无回归验证 (performance): 未观察到可测量的性能回归,但未验证 DSV4 主动路径性能。
- 测试 rerun 请求与结果 (testing): 相关针对性测试通过,未发现本 PR 引入的测试失败。
风险与影响
关联脉络
- PR #34816 Publish DSPARK decode-verify WAR boundary: PR body 明确指出本 PR 是其 target-prefill 对应物,二者共同完成 DSV4 投机解码的 prefill 与 decode-verify 共享读完成事件发布。
- PR #34515 DSV4 prefill overlap integration and performance work: 本 PR 从 #34515 中提取,该集成工作最终实现移除所有阻塞后约 40% 的 E2E 吞吐提升;本 PR 是其中 prefill 侧的关键拆分子项。
参与讨论