Files
qwen38-flash-next-dgx-spark/docs/cuda-graph-profile.md
T

8.6 KiB
Raw Blame History

CUDA Graph 性能剖析:图已生效,主要收益受 GPU 工作占比限制

历史实验记录:保留当时的配置和恢复状态。当前部署已改为 128K eager 基线,见 当前状态

2026-09-18Asia/Shanghai。方案与脚本见 profile128k

CUDA Graph 确实被重放,并非参数失效。本轮短回答生成速度中位数由 29.954 → 31.316 tokens/s(约 +4.5%, 与此前约 +4.4% 的复测一致。32K 首次输入首 token 则为 15.416 → 15.376 秒,基本不变。

原因有两层:当前小尺寸图没有覆盖实际的长输入预填充分块;同时,GPU 时间线上本来就没有很多空隙可消除。 不能据此认定 SSD、PLE mmap 或 CPU 调度是主要瓶颈,也不能把 CUDA Graph 的小收益解释为 CUDA/GPU 没有发挥作用。 eager 和 graph 两组都在使用 GPU 执行 CUDA 内核;本次比较的是是否通过 CUDA Graph 提交这些工作。

正常计时与正确性

  • 固定 nightly 0bfc7a15、相同模型 revision、BF16 KV、MTP=2、前缀缓存、两处 Mamba 修正、GPU memory=0.80。
  • 实验上下文上限 131072max_num_seqs=4,但测试串行;max_num_batched_tokens=2048。
  • 小尺寸图为 breakable PIECEWISEcapture sizes [1,2,4,8,12]
  • 短回答预算 512,长检索预算 2048,思考与正文共用;temperature=0、seed=42、reasoning_effort=low。
  • 两组各 19 次请求:1 次算术预热、三轮各 5 次计时请求、3 次 trace 请求。38 次均通过各自判据。 长检索要求精确答案和正常 stop,短说明仅检查非空及正常 stop,不代表进行了语言质量评估。
  • 19 个对应请求的输入 SHA256 全部一致;服务返回 prompt_tokens 与预检逐条一致。
  • 每轮首次输入使用不同前缀,避免跨轮缓存命中;重复输入紧随首次输入。
  • 下表为三轮中位数,不含预热及 trace 请求。短回答每次均输出 214 tokens;长检索输出约 6274 tokens,有少量差异。
指标 eager 小尺寸 graph
短回答解码速度(近似 tokens/s) 29.954 31.316
短回答首 token(秒,含思考) 0.2627 0.2624
短回答完整耗时(秒) 7.374 7.064
8K 首次首 token(秒) 3.788 3.770
8K 复用首 token(秒) 0.880 0.874
32K 首次首 token(秒) 15.416 15.376
32K 复用首 token(秒) 1.368 1.327

原始请求记录和自动汇总见 results/profile128k。 计时阶段没有开启 capture,但 Nsight launcher 和 NVTX 包装仍存在;不是完全卸载 profiler 的独立测速。 执行顺序 eager → graph,未随机化、未清 OS 页缓存,小样本不代表统计显著性或长期稳定性。

时间线证据

Nsight Systems 2025.3.2CUDA profiler API 控制三个短采样窗口,node 级 graph trace。 NVTX 临时标记 PLE 同步、CPU 去重、CPU gather、lookup,以及图调度描述符。 计时阶段和 trace 阶段分开;下表不用于跨模式的端到端性能排名。

“GPU 活动占比”是 kernel、memcpy、memset 时间区间并集,除以首次至末次 GPU 活动的跨度。 它不是 SM 利用率、内存带宽利用率,也不包含完整 HTTP 请求前后的所有工作。

采样请求 eager 活动占比 graph 活动占比 graph 中来自图节点的 kernel 数量占比
短回答 93.01% 95.22% 82.43%
32K 首次输入及回答 97.38% 97.50% 44.25%
32K 复用及回答 95.22% 95.29% 77.15%

图生效,但覆盖范围有限且分成多段

  • eager 的三个窗口都没有 graph-node kernel。
  • graph 短回答窗口记录 4,664 次 cudaGraphLaunch160,160 / 194,303 个 kernel 来自图节点
  • 调度记录显示解码 actual=3 被 padding 到 4,模式为 PIECEWISE;MTP 路径也存在图重放。 同时保留部分 NONE 路径,不能说整次请求被捕获成一张图。
  • 该窗口有 88 次 actual=3 的图调度,累计 graph launch 约为其 53 倍。 这是目标模型与辅助路径合计的实测比值,说明提交仍分成多段;不是每生成一个 token 就固定调用 53 次。
  • 32K 首次请求中的 1600 和 742-token 预填充调度均为 NONE;后续解码才命中图。
  • 日志确认 attention/KV block size 为 1600。虽然批处理上限是 2048,实际主要分块是 1600, 因此不能仅凭配置里的 2048 推断捕获或预填充形状。

PLE 的“等待时间”不是磁盘读表时间

32K 首次输入 trace

时间项 eager graph
首次至末次 GPU 活动跨度 17.458 s 17.614 s
GPU 活动区间并集 17.000 s 17.173 s
PLE CPU gather 0.275 s 0.292 s
PLE CPU 去重(标记中的 np.unique 0.019 s 0.021 s
PLE 等待 GPU 6.389 s 7.292 s
其中与 GPU 活动重叠 6.341 s 7.233 s

CPU wait 与 GPU 工作几乎完全重叠,不是额外叠加在 GPU 计算之后的同等开销。 不能把所有 API/NVTX/GPU 时长相加,也不能依据同步函数耗时最长就优先优化同步本身。 图版本 wait 更长不证明 PLE 退化:提交时序和输出长度都不同。

本轮 gather 包括页缓存或实际存储读取,未测物理 NVMe I/O;没有证据说明每次都从 SSD 读取, 也没有证据支持将整张 47.7 GiB 表强制驻留能带来显著收益。 短回答中 CPU gather 约 0.20 秒,相比约 7 秒 GPU 活动跨度亦非主导项。

决策与后续方向

本轮不扩大图捕获尺寸。长输入 GPU 活动已接近连续,消除提交间隙的空间有限; 扩大图还可能增加 padding、捕获内存和启动成本。当前证据不足以让它成为稳定基线的优先改动。 这不等于证明大图一定没有收益;该候选未实测,不报告虚构加速或稳定性结果。 按方案,只有新候选通过才追加 128K 验证,因此本轮未重跑 128K,也未再次触碰 260K。

稳定基线建议仍是:eager + 前缀缓存 + MTP=2,实际使用限制在 128K 以内。 愿意维护实验补丁时,小尺寸图可作为约 4–5% 短回答收益的可选项;目前证据不足以要求默认切换。 下一个有依据的性能工作应聚焦耗时 GPU 内核及其 GB10 后端:32K trace 的主要内核包含 CUTLASS 分组量化 GEMM、稀疏注意力和归一化组合;短回答还有显著 BF16 GEMM 时间。 要判定算力受限还是带宽受限,需要另做针对性内核分析,不能用本轮活动占比替代结论。

资源与实验修正

实验采样最低 MemAvailable 15.748 GiB,主机 oom_kill 从 25 到 25,没有新增 OOM。 监控每秒采样,低于 1 GiB 立即停止,持续低于 2 GiB 或主机 OOM 增加则停止。 监控覆盖两组实验,恢复阶段另做最终系统检查;没有为了验证保护阈值主动制造内存压力。

首次诊断启动因临时包装函数缺少 PyTorch schema 所需类型注解而失败,尚未加载完成或执行请求。 自动恢复触发后,修复注解并验证 torch.library.infer_schema,随后才运行完整两组。 该失败属于诊断脚本问题,不是模型/vLLM 的新 bug;失败日志保留在 Spark 的 attempt1 目录。

原始 trace 和 SQLite 只保留在 Spark optimization-results/profile128k/{eager,graph},父目录权限 0700。 它们可能包含进程参数,不提交 Git。仓库的 证据清单 记录文件大小、SHA256、Nsight 版本、配置要点;仅提交 CUDA/NVTX 摘要、请求结果及资源记录。

恢复状态

2026-09-18 01:21Asia/Shanghai)已恢复原 eager 镜像,容器 healthy、重启策略 unless-stopped。 算术、中文、工具调用 smoke test 全部通过;原 Compose 与测试前逐字一致,临时 profiler 参数与挂载均已移除。 可用内存约 18.85 GiB,主机 OOM 计数仍为 25,系统及用户 failed units 均为 0。 恢复证据见 recovery.jsonsmoke log

默认配置仍保留原来的 262144 上限,本轮未修改服务容量;该配置值不代表已验证的稳定容量。 实际使用仍建议限制在 128K 以内,接近 260K 的既有 OOM 结论不变。

参考