fix: overlap CUDA graph replay preparation on PRO5000 - #1365
Conversation
…_8bit The kernel read the input twice (absmax pass + quantize pass). For the common case (bf16 + group_size=128 -> 1 vec/lane) cache the loaded vector in registers and reuse it in the quantize pass, eliminating the second HBM read; larger groups fall back to reload. Also emit the 8 fp8 outputs as one packed vec_t<__nv_fp8_e4m3,8> store (if constexpr; int8 path unchanged). Bit-identical output (math unchanged). quant kernel 304.8->130.9 us/step (-57%); PER_BLOCK bs64 3.91->3.43ms. Benefits every per_token_group_quant_8bit user (PER_BLOCK / DeepGEMM). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- transport input embeddings through RPC and the C++ engine pipeline - expose embeddings to PyModel and model descriptors - support multimodal embedding locations and disable token-only reuse
- start_server: add prompt generator multi-process launch with frontend port reuse, guarded by VIT role check and enable_prompt_generator config - Add MPS support gated by enable_prompt_generator_mps config - Normalize and validate prompt generator config at startup with graceful fallback when internal_source is unavailable - model_loader: support loading embedding weights independently - Add normalize_prompt_generator_config unit tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add register_additional_metrics() to MetricReporter for dynamic metric registration with dedup support. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Preserve legacy per-output AuxInfo tensors for rolling upgrades while serializing one stacked tensor for clients that avoid per-output decoding. Rebuilt from the missing RPC portion of 1adf28a.
Bound complete token history by the request output limit, initialize every beam row, delay non-beam return-sequence expansion until sampling, and align logits processor rows. Rebuilt from the develop-missing portions of 28a7fa2.
(cherry picked from commit 9d3f693)
PR #1365 第 1 轮评审 — BLOCKED(1)
阻塞项(P0/P1,support≥3 且过全部门)[P1] FusedRopeKVCacheDecodeOp.forward 新增的 is 身份检查会破坏 XQAImpl(sm_90/sm_100 的 decode 路径)
非阻塞发现(并集报告:单票也保留,降级不丢弃)
本轮 KPI{
"reps": 5,
"agentic_reps": 2,
"diff_truncated": false,
"patch_bytes": 34169,
"coverage": 1.0,
"shards": 1,
"discover_ok": 5,
"discover_fail": 0,
"discover_attempts": 10,
"clusters": 5,
"synth_fail": 0,
"confirm": {
"candidates": 1,
"to_block": 0,
"confirm_fail": 0
},
"blocking_dedup_merged": 0,
"fact_check": {
"checked": 1,
"hold": 1,
"refuted": 0,
"parse_fail": 0
},
"model": "whale/DeepSeek-V4-Pro-0813",
"round_index": 1,
"delta_files": 14,
"full_files": 14,
"new_findings": 5,
"merged": 0,
"recheck": {
"fixed": 0,
"still_open": 0,
"cannot_substantiate": 0,
"fail": 0
},
"blocking": 1,
"blocking_on_unchanged": 0
}本报告由 rtp-llm-agent-platform 自动生成,同一 PR 的后续轮次就地更新同一条评论。 rtp-llm-agent-platform review · 第 1 轮 · head |
siluzhou
left a comment
There was a problem hiding this comment.
AI Code Review - PR #1365
Status: BLOCKING
Summary: P0/0 · P1/1 · P2/0 · P3/0
Reviewed: commit 32497b255b44 · 2026-09-01 11:57 UTC+8
Blocking Issues
P1
- XQA replay 违反新增的 decode RoPE 设备缓冲区所有权契约 @
rtp_llm/ops/fused_rope_kvcache_op.py:339- 建议:让 XQA replay 调用
refresh_sequence_lengths(),或通过prepare(..., forbid_reallocation=True)刷新并保留 op 管理的稳定设备 tensor,删除_captured_seq_lens回写。增加真实 XQA capture/replay 测试,连续改变 batch size 与序列长度,并验证 eager/replay 输出一致、padding 清零及指针稳定。
- 建议:让 XQA replay 调用
Checklist Findings (3 fail / 108 total)
General Principles Checklist
- [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue
XQA replay 违反新增的 decode RoPE 设备缓冲区所有权契约
forward()要求params.sequence_lengths必须是 op 持有的稳定 CUDA buffer;初始化时prepare()返回该 buffer,但XQAImpl.prepare_cuda_graph()仍将参数改回捕获自原始attn_inputs.sequence_lengths的_captured_seq_lens。启用 RoPE 的 XQA replay 随后会因对象身份不符而抛出DecodeRopeContractError;若原 tensor 在 CPU,还会先触发设备检查。 - [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue
XQA replay 违反新增的 decode RoPE 设备缓冲区所有权契约
forward()要求params.sequence_lengths必须是 op 持有的稳定 CUDA buffer;初始化时prepare()返回该 buffer,但XQAImpl.prepare_cuda_graph()仍将参数改回捕获自原始attn_inputs.sequence_lengths的_captured_seq_lens。启用 RoPE 的 XQA replay 随后会因对象身份不符而抛出DecodeRopeContractError;若原 tensor 在 CPU,还会先触发设备检查。 - [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue
XQA replay 违反新增的 decode RoPE 设备缓冲区所有权契约
forward()要求params.sequence_lengths必须是 op 持有的稳定 CUDA buffer;初始化时prepare()返回该 buffer,但XQAImpl.prepare_cuda_graph()仍将参数改回捕获自原始attn_inputs.sequence_lengths的_captured_seq_lens。启用 RoPE 的 XQA replay 随后会因对象身份不符而抛出DecodeRopeContractError;若原 tensor 在 CPU,还会先触发设备检查。
Strengths
sequence_lengths_device已贯通 C++ 构造、CUDA Graph capture/replay、pybind 声明及 Python 消费路径。- CUDA Graph replay 会原位刷新设备侧序列长度镜像,并正确清理 padding lane。
- FlashInfer graph plan 使用图绑定的设备侧 page-index buffer,并通过 CUDA event 保护可复用的 pinned planning buffer。
- 新增 SM120 smoke 覆盖 TP2、MoE FP8、CUDA Graph 与流异步的关键组合,并复用同步用例的输入与 golden。
| raise DecodeRopeContractError( | ||
| "decode RoPE sequence_lengths must be on CUDA" | ||
| ) | ||
| if params.sequence_lengths is not self._sequence_lengths_device: |
There was a problem hiding this comment.
[P1] XQA replay 违反新增的 decode RoPE 设备缓冲区所有权契约
forward() 要求 params.sequence_lengths 必须是 op 持有的稳定 CUDA buffer;初始化时 prepare() 返回该 buffer,但 XQAImpl.prepare_cuda_graph() 仍将参数改回捕获自原始 attn_inputs.sequence_lengths 的 _captured_seq_lens。启用 RoPE 的 XQA replay 随后会因对象身份不符而抛出 DecodeRopeContractError;若原 tensor 在 CPU,还会先触发设备检查。
建议: 让 XQA replay 调用 refresh_sequence_lengths(),或通过 prepare(..., forbid_reallocation=True) 刷新并保留 op 管理的稳定设备 tensor,删除 _captured_seq_lens 回写。增加真实 XQA capture/replay 测试,连续改变 batch size 与序列长度,并验证 eager/replay 输出一致、padding 清零及指针稳定。
Checklist: [6.1] 状态不变量:创建/更新/失败/重试/回滚路径有效;[6.1] 新逻辑有聚焦单测 + 相关集成/smoke 测试;[6.1] 边界 case 覆盖(空、单元素、最大值)
LLLLKKKK
left a comment
There was a problem hiding this comment.
AI Code Review - PR #1365
Status: LGTM
Summary: P0/0 · P1/0 · P2/3 · P3/3
Reviewed: commit 32497b255b44 · 2026-09-01 12:02 UTC+8
lgtm ready to ci
Non-blocking Suggestions
P2
- XQA 未迁移新的 RoPE 缓冲区所有权契约 @
rtp_llm/ops/fused_rope_kvcache_op.py:339- 建议:保留
_captured_seq_lens供 XQA 参数原地刷新,但不要赋给 RoPE 参数;改用refresh_sequence_lengths(..., forbid_reallocation=True),并增加启用 RoPE 的 prepare/forward 与 graph replay 测试。
- 建议:保留
- 直连 runner 未构造新增设备镜像 @
rtp_llm/cpp/cuda_graph/cuda_graph_runner.cc:431- 建议:在测试 runner 中补齐
sequence_lengths_device;decode replay 缺少该镜像时应 fail-fast,并增加连续两次不同 sequence length 的重放测试。
- 建议:在测试 runner 中补齐
- 异步 plan-copy 等待顺序缺少确定性测试 @
rtp_llm/models_py/modules/factory/attention/cuda_impl/test/test_py_flashinfer_mha_decode.py:593- 建议:注入可控 event,断言未完成时在下一次
plan()前同步,并覆盖 event 已完成时不等待的分支。
- 建议:注入可控 event,断言未完成时在下一次
P3
- 非图 decode 重复分配并复制 sequence lengths @
rtp_llm/ops/fused_rope_kvcache_op.py:294- 建议:非图路径直接采用已有且 dtype/shape 匹配的设备镜像,仅为缺少镜像的独立调用保留分配回退。
- page indices 别名契约缺少直接断言 @
rtp_llm/models_py/modules/factory/attention/cuda_impl/test/test_py_flashinfer_mha_decode.py:620- 建议:增加
capture_call.args[1].data_ptr() == fmha_params.page_indice_d.data_ptr()断言。
- 建议:增加
- 必备绑定字段被当作可选属性访问 @
rtp_llm/ops/fused_rope_kvcache_op.py:209- 建议:改为直接访问
attn_inputs.sequence_lengths_device,让缺失绑定明确抛出AttributeError。
- 建议:改为直接访问
Checklist Findings (5 fail / 101 total)
General Principles Checklist
- [6.1] Architecture — 状态不变量:创建/更新/失败/重试/回滚路径有效 → issue
异步 plan-copy 等待顺序缺少确定性测试
新逻辑要求 event 未完成时先synchronize(),再复用 planning 缓冲区;当前测试仅检查 plan 次数、参数设备与指针稳定性。若等待被删除或顺序颠倒,单测不会稳定失败,只能依赖 smoke 时序偶现暴露。 - [6.1] Architecture — 错误语义:fail-fast/retry/fallback/silent 行为显式 → issue
必备绑定字段被当作可选属性访问
sequence_lengths_device已由 pybind 和.pyi定义为必备字段,但代码仍通过字面量getattr静默回退。加载不匹配的旧.so时不会 fail-fast,反而进入不同的缓冲区路径,掩盖 Python 与绑定版本不一致。 - [6.1] Tests — 新逻辑有聚焦单测 + 相关集成/smoke 测试 → issue
page indices 别名契约缺少直接断言
测试只确认传给plan()的 page indices 位于 CUDA,未确认其与fmha_params.page_indice_d同址。若改传另一块 CUDA tensor,测试仍会通过,但预期的内部 no-op 拷贝将退化为真实 D2D 拷贝。 - [6.1] Tests — 边界 case 覆盖(空、单元素、最大值) → issue
异步 plan-copy 等待顺序缺少确定性测试
新逻辑要求 event 未完成时先synchronize(),再复用 planning 缓冲区;当前测试仅检查 plan 次数、参数设备与指针稳定性。若等待被删除或顺序颠倒,单测不会稳定失败,只能依赖 smoke 时序偶现暴露。
Python Static-First Checklist
- [P.A] 静态结构与类型纪律 — 禁止 getattr/setattr literal 访问 → issue
必备绑定字段被当作可选属性访问
sequence_lengths_device已由 pybind 和.pyi定义为必备字段,但代码仍通过字面量getattr静默回退。加载不匹配的旧.so时不会 fail-fast,反而进入不同的缓冲区路径,掩盖 Python 与绑定版本不一致。
Strengths
- 生产链路已将
sequence_lengths_device贯通 C++、pybind、类型声明、捕获内存与 replay 刷新。 - 独立 CUDA event 保护可复用 pinned 规划缓冲区,无需等待整张 graph。
- 活跃 lane 复制与 padding lane 清零语义在 host/device 两侧保持一致。
- 新增 SM120 异步 smoke 复用同步用例 golden,可检测输出漂移。
| raise DecodeRopeContractError( | ||
| "decode RoPE sequence_lengths must be on CUDA" | ||
| ) | ||
| if params.sequence_lengths is not self._sequence_lengths_device: |
There was a problem hiding this comment.
[P2] XQA 未迁移新的 RoPE 缓冲区所有权契约
forward() 要求 sequence_lengths 必须是 op 自持 CUDA tensor,但 XQAImpl.prepare_cuda_graph() 仍将其覆盖为 pinned CPU _captured_seq_lens。生产 replay 不再调用 Python forward(),所以不会形成 P1;然而对象状态已违反自身不变量,任何 prepare 后的直接 forward 都会失败,且现有 XQA 图测试均关闭 RoPE。
建议: 保留 _captured_seq_lens 供 XQA 参数原地刷新,但不要赋给 RoPE 参数;改用 refresh_sequence_lengths(..., forbid_reallocation=True),并增加启用 RoPE 的 prepare/forward 与 graph replay 测试。
|
|
||
| if (!is_prefill_cuda_graph_mode_) { | ||
| // D2D copies — collected for single batched kernel launch | ||
| tryAddD2DCopy(inputs.attention_inputs.sequence_lengths_device, |
There was a problem hiding this comment.
[P2] 直连 runner 未构造新增设备镜像
源 sequence_lengths_device 未定义时复制会静默跳过;测试 runner 仅构造 input/prefix 镜像。已注册的 H20 decode-padding 测试启用 RoPE,因此 replay 会继续使用捕获期长度,而 host 输入已更新。生产 PyWrappedModel 会构造该镜像,影响集中于测试封装和直连 runner。
建议: 在测试 runner 中补齐 sequence_lengths_device;decode replay 缺少该镜像时应 fail-fast,并增加连续两次不同 sequence length 的重放测试。
| @@ -615,7 +615,9 @@ def test_replay_refreshes_plan_metadata(self): | |||
| self.assertEqual(plan_mock.call_count, 1) | |||
There was a problem hiding this comment.
📍 实际位置 rtp_llm/models_py/modules/factory/attention/cuda_impl/test/test_py_flashinfer_mha_decode.py:593(不在 diff 展示范围内,就近挂载)
[P2] 异步 plan-copy 等待顺序缺少确定性测试
新逻辑要求 event 未完成时先 synchronize(),再复用 planning 缓冲区;当前测试仅检查 plan 次数、参数设备与指针稳定性。若等待被删除或顺序颠倒,单测不会稳定失败,只能依赖 smoke 时序偶现暴露。
建议: 注入可控 event,断言未完成时在下一次 plan() 前同步,并覆盖 event 已完成时不等待的分支。
Checklist: [6.1] 状态不变量:创建/更新/失败/重试/回滚路径有效;[6.1] 边界 case 覆盖(空、单元素、最大值)
| tuple(sequence_lengths.shape), | ||
| device, | ||
| ) | ||
| self._sequence_lengths_device = torch.empty( |
There was a problem hiding this comment.
[P3] 非图 decode 重复分配并复制 sequence lengths
PyWrappedModel 已生成 sequence_lengths_device,但非图路径仍执行 torch.empty() 和 copy_();普通 forward 又会重新创建 attention impl,使私有缓存无法跨步复用。每步因此增加一次小型设备分配及一次冗余 H2D/D2D 拷贝,但当前没有基准数据证明显著回归。
建议: 非图路径直接采用已有且 dtype/shape 匹配的设备镜像,仅为缺少镜像的独立调用保留分配回退。
| self.assertFalse(capture_call.args[1].is_cuda) | ||
| # Page indices alias the graph-bound device buffer so FlashInfer's | ||
| # internal copy is a no-op instead of a blocking host-to-device copy. | ||
| self.assertTrue(capture_call.args[1].is_cuda) |
There was a problem hiding this comment.
[P3] page indices 别名契约缺少直接断言
测试只确认传给 plan() 的 page indices 位于 CUDA,未确认其与 fmha_params.page_indice_d 同址。若改传另一块 CUDA tensor,测试仍会通过,但预期的内部 no-op 拷贝将退化为真实 D2D 拷贝。
建议: 增加 capture_call.args[1].data_ptr() == fmha_params.page_indice_d.data_ptr() 断言。
Checklist: [6.1] 新逻辑有聚焦单测 + 相关集成/smoke 测试
| # mirror. Its pointer is capture-stable and CudaGraphRunner refreshes | ||
| # it with the other fused metadata copies before every replay, avoiding | ||
| # a synchronizing torch.copy_ from the pinned host planning mirror. | ||
| graph_sequence_lengths = getattr( |
There was a problem hiding this comment.
[P3] 必备绑定字段被当作可选属性访问
sequence_lengths_device 已由 pybind 和 .pyi 定义为必备字段,但代码仍通过字面量 getattr 静默回退。加载不匹配的旧 .so 时不会 fail-fast,反而进入不同的缓冲区路径,掩盖 Python 与绑定版本不一致。
建议: 改为直接访问 attn_inputs.sequence_lengths_device,让缺失绑定明确抛出 AttributeError。
Checklist: [6.1] 错误语义:fail-fast/retry/fallback/silent 行为显式;[P.A] 禁止 getattr/setattr literal 访问
|
CI dispatcher could not find a native This can happen if the PR was opened before the CI architecture change, or if the original run was deleted. To fix: push any commit (even empty: |
27e6930 to
77e9a20
Compare
Summary
Validation
bazelisk build //rtp_llm:start_server_bin --config=cuda12_9: passed//rtp_llm/test/smoke:moe_fp8pb_cuda_graph_async_tp2_sm120: passed on GPUs 0-1; 2/2 requests matched the existing synchronous golden (compare diff count: 0)Scope
Only production changes, the directly affected unit assertion, and the new SM120 smoke registration are included. Existing managed test/benchmark artifacts are intentionally excluded.