news 2026/7/11 2:14:51

别把 CUDA Graph 当成万能加速:它在 LLM Serving 里的真实收益边界与工程落地

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
别把 CUDA Graph 当成万能加速:它在 LLM Serving 里的真实收益边界与工程落地

很多团队一看到CUDA Graph,第一反应都是“减少 kernel launch 开销,吞吐和延迟都会更好”。这句话不算错,但如果把它直接搬到 LLM Serving,往往会高估收益、低估代价。因为大模型推理不是单个 kernel 的 benchmark,而是一个同时受Prefill/Decode、动态 shape、continuous batching、KV Cache、显存水位、调度公平性影响的在线系统。
真正的问题不是“CUDA Graph 有没有用”,而是:它到底优化了哪一段开销?在什么负载下收益明显?为什么有的场景能稳定降延迟,有的场景几乎没变化,甚至因为显存预留、shape 爆炸和调试复杂度把系统搞得更重?
这篇文章从 AI Infra 和 LLM Serving 的工程视角,系统拆解 CUDA Graph 在推理系统中的位置:先讲清楚 launch overhead 到底是什么,再讲它和 Prefill/Decode 的关系、和 vLLM / TensorRT-LLM 这类 runtime 的关系、和 continuous batching / 动态 batch / 动态长度之间的冲突,最后给出一套更实用的落地判断框架、监控指标和面试表达方式。

导读

前几篇系列文章,我们已经分别拆过:

  • Chunked Prefill如何缓解长 prompt 对 TTFT 和 decode 连续性的冲击
  • Admission Control / Goodput如何保护线上系统不被异质请求拖进过载区
  • MoE 路由抖动KV Cache 页管理SGLang vs vLLMCUDA Roofline等不同层面的工程问题

如果说这些文章主要回答的是:

  • 请求怎么调度
  • KV 怎么管理
  • 框架怎么选
  • 瓶颈怎么分析

那么这一篇要回答的是另一个经常被误用的问题:

当你已经知道系统瓶颈不只是算子本身,而是“运行时执行路径”时,CUDA Graph 到底值不值得上?

它属于 AI Infra 体系里一个很典型的话题:
既靠近 CUDA 和 runtime,又必须放回 LLM Serving 的整体系统里理解。


目录

  1. 先讲清楚:CUDA Graph 到底在解决什么问题
  2. 为什么很多人会高估它在 LLM Serving 里的收益
  3. Kernel launch overhead 到底是什么
  4. CUDA Graph 的基本工作方式
  5. 它在训练和推理里的价值为什么不一样
  6. 放到 LLM Serving 里,先把 Prefill 和 Decode 分开看
  7. 为什么 Decode 往往比 Prefill 更适合 Graph 化
  8. 动态 shape 是 CUDA Graph 最大的工程约束之一
  9. Continuous Batching 为什么会和 Graph 产生天然张力
  10. 它到底能优化哪些开销,不能优化哪些开销
  11. 为什么有的压测收益明显,线上却不稳定
  12. 常见落地方式一:固定 shape 池 + 多图复用
  13. 常见落地方式二:Prefill 保守,Decode 激进
  14. 常见落地方式三:和 TensorRT-LLM / 编译型 runtime 结合
  15. 显存为什么会变重:Graph 不是白送优化
  16. 监控和压测时应该重点看什么
  17. 线上最容易踩的几个坑
  18. 面试里怎么把 CUDA Graph 讲得像真做过
  19. 总结

1. 先讲清楚:CUDA Graph 到底在解决什么问题

很多文章一上来就说:

  • 它可以减少 CPU 到 GPU 的 launch 开销
  • 它可以把一串 GPU 操作打包后重复执行
  • 它可以降低延迟、提升吞吐

这些都没问题,但还不够工程化。

更准确地说,CUDA Graph 解决的是:

当一段 GPU 执行路径重复出现时,能不能把“每次都重新发射这串工作”的 CPU 侧运行时开销压到更低。

它关注的不是:

  • 模型数学上变了没有
  • 单个 kernel 算法更快了没有
  • HBM 带宽凭空更高了没有

它关注的是:

  • 这一轮会发多少个 kernel
  • 每个 kernel 之间有多少依赖
  • 这些 launch 和同步动作是不是每次都在重复
  • CPU runtime 是否成了显式成本

从这个角度看,CUDA Graph 更像一种执行路径缓存,而不是单纯的算子优化。


2. 为什么很多人会高估它在 LLM Serving 里的收益

原因很简单:大家容易把它想成“任何 GPU 程序都能稳定加速”。

但 LLM Serving 和传统离线 benchmark 很不一样,它的执行路径有几个特殊点:

  • 请求长度高度动态
  • batch 形态持续变化
  • Prefill 和 Decode 的计算特征完全不同
  • continuous batching 会让每一轮参与执行的请求集合不断变化
  • KV Cache 水位、分页状态、prefix 复用、量化分支都会影响实际路径

这意味着:

如果执行图不稳定,Graph 的可复用性就会很差。

于是你会看到两种完全不同的结论:

  • 在固定 batch、固定序列长度的压测里,CUDA Graph 收益很漂亮
  • 在线上异质流量里,收益变得不稳定,甚至维护成本高于实际价值

这不是 Graph 无效,而是系统条件变了。


3. Kernel launch overhead 到底是什么

想判断 CUDA Graph 有没有价值,先得理解 launch overhead 是什么。

很多工程师会把 GPU 延迟理解成“kernel 执行时间”,但端到端执行通常还包括:

  • CPU 侧准备 launch 参数
  • 调用 runtime / driver 发射 kernel
  • 建立 stream 上的依赖关系
  • 插入同步点或事件
  • 多个小 kernel 串起来形成一次完整前向

如果单个 kernel 很大,这些开销可能不显眼。
但如果一次推理步骤里包含很多细碎 kernel,而且每个 kernel 执行时间不长,那么:

  • CPU launch
  • runtime bookkeeping
  • 调度和同步

就可能逐渐变成显性成本。

这在以下场景里尤其明显:

  • 小 batch
  • 小 shape
  • 高频重复执行
  • 一个 token 一步一步生成的 decode

这也是为什么很多人一聊 CUDA Graph,都会自然想到推理阶段,尤其是 decode。


4. CUDA Graph 的基本工作方式

它的核心思路可以粗略理解为两步:

4.1 先捕获一段执行路径

把一串稳定的 GPU 操作记录成一个图,里面包含:

  • 哪些 kernel 会被执行
  • 执行顺序是什么
  • 依赖关系是什么
  • 相关内存地址和参数布局如何组织

4.2 后续重复回放

下一次遇到“同一类执行路径”时,不再逐个 launch,而是直接 replay 这张图。

这样能减少:

  • 多次 runtime 调用
  • 多次 CPU 发射动作
  • 一部分调度与依赖建立成本

但它有个前提:

你回放的执行路径必须足够稳定。

如果 shape、地址、kernel 序列、控制流分支经常变,图就很难复用。


5. 它在训练和推理里的价值为什么不一样

这是一个很容易被忽略的点。

5.1 训练阶段

训练往往有更稳定的 mini-batch 形态:

  • 固定 batch size
  • 固定 sequence length
  • 固定前向/反向结构

如果数据整理得比较规整,Graph 复用条件更容易满足。

5.2 推理阶段

在线推理的异质性强得多:

  • prompt 长度不一样
  • 输出长度不一样
  • 请求到达时间不一样
  • batch 是动态拼出来的
  • Prefill 和 Decode 混在一起时路径差异更大

所以推理里不是“能不能用 CUDA Graph”,而是:

要在多大程度上牺牲动态性,去换取图复用。

这本质上是 runtime 设计 trade-off。


6. 放到 LLM Serving 里,先把 Prefill 和 Decode 分开看

这是理解收益边界的关键。

6.1 Prefill

Prefill 的特点是:

  • 一次处理大量输入 token
  • 更偏大矩阵计算
  • 计算规模随 prompt 长度变化很大
  • shape 波动通常更明显

它更像“每次进来一段规模变化很大的工作”。

6.2 Decode

Decode 的特点是:

  • 每步通常只处理每个请求的少量新 token
  • 操作反复执行
  • kernel 数量多、步数多
  • 单步执行形态相对更稳定

它更像“同一段小工作重复很多次”。

从 Graph 友好性看,后者通常更适合。


7. 为什么 Decode 往往比 Prefill 更适合 Graph 化

这背后有三个核心原因。

7.1 重复度更高

Decode 是 token-by-token 的迭代过程。
如果每一步执行路径相似,那么 graph replay 的复用价值会被多次放大。

7.2 CPU launch 成本更容易显性化

因为 decode 单步通常更小、更碎。
当真正的 GPU 计算时间没那么长时,CPU launch 占比更容易上升。

7.3 shape 更容易被约束

尽管 decode 也有 batch 波动,但相比长 prompt prefill:

  • 单步 token 数更小
  • 更容易做 bucket 化
  • 更容易通过固定 slot / token budget 设计出较稳定执行形态

所以很多系统会优先在 decode 路径上做更激进的 graph 化,而对 prefill 更保守。


8. 动态 shape 是 CUDA Graph 最大的工程约束之一

如果你只记一个限制,记这个就够了。

CUDA Graph 最怕的不是“模型大”,而是:

执行形态不稳定。

在 LLM Serving 中,导致 shape 不稳定的来源很多:

  • 输入 prompt 长度变化
  • 每轮 active sequence 数变化
  • 每轮 decode 参与请求数变化
  • continuous batching 不断插入和移除请求
  • 不同请求的 KV 长度不同
  • 不同采样策略或功能分支带来不同路径

这会直接引出一个现实问题:

8.1 不能指望一张图吃掉所有流量

真正落地时,通常不是“一张万能图”,而是:

  • 多个 shape bucket
  • 每个 bucket 一组图
  • 不命中 bucket 时 fallback 到 eager

也就是说,Graph 本身往往不是替代普通执行,而是和普通执行并存。

8.2 bucket 设计本身就是工程题

bucket 太细:

  • 图数量暴涨
  • 捕获成本高
  • 显存和管理复杂度上升

bucket 太粗:

  • padding 和空转增加
  • 复用看起来多了,真实效率可能下降

这不是单纯 CUDA 技术问题,而是 runtime 选型问题。


9. Continuous Batching 为什么会和 Graph 产生天然张力

continuous batching 的目标是让 GPU 尽量持续吃到工作,提升吞吐和资源利用率。
但它的副作用是:

  • 每一轮 batch 形态都可能变化
  • 请求会动态进出 batch
  • active sequence 数持续波动

这和 CUDA Graph 希望看到的“稳定重复路径”天然不完全一致。

所以它们之间并不是简单叠加关系,而是存在张力:

9.1 batching 越灵活,graph 越难稳定

你越想动态地插入新请求、越想追求极致调度自由度,执行路径就越不稳定。

9.2 graph 越激进,batching 自由度越可能下降

如果为了复用图而强行把 shape 固定得更死:

  • 调度器的自由度会下降
  • 请求异质性处理会变差
  • 可能损害公平性或长尾表现

所以成熟系统通常会折中:

  • 在一部分稳定形态上 Graph 化
  • 在另一部分动态形态上保持 eager

10. 它到底能优化哪些开销,不能优化哪些开销

这部分最容易被讲错。

10.1 它更可能优化的

  • CPU 侧 launch overhead
  • 多 kernel 串联的运行时管理开销
  • 一部分小 shape 场景下的端到端延迟
  • 重复执行路径上的稳定性

10.2 它不能直接解决的

  • HBM 带宽不够
  • TP / EP 通信过慢
  • KV Cache 容量不足
  • PagedAttention block 访问模式本身的访存瓶颈
  • 长 prompt 导致的 prefill 计算量爆炸
  • 调度策略不合理造成的 TTFT / TPOT 恶化

如果系统真正瓶颈是:

  • memory-bound decode
  • KV page miss / block 管理
  • 多卡同步
  • 长 prompt 压住 decode

那么上 CUDA Graph 的收益可能远没有想象中大。

换句话说:

它优化的是“怎么发射这段工作”,不是“这段工作本身为什么慢”。


11. 为什么有的压测收益明显,线上却不稳定

因为很多压测默认做了三件对 Graph 很友好的事:

  • 固定输入长度
  • 固定输出长度
  • 固定 batch 形态

这等于人为构造了一个高度稳定的执行路径。

在线上则完全不是这样:

  • 请求长短分布离散
  • 高峰时 active sequence 波动很大
  • 有人很快结束,有人持续长输出
  • 某些请求还带 prefix reuse、工具调用或结构化输出约束

于是你会看到:

  • 实验室里 latency 降了很多
  • 线上平均值也许有提升
  • 但 P95 / P99 不一定同步变好
  • fallback 比例一高,收益就被吃掉

所以评估 CUDA Graph 不能只看平均 tokens/s 或平均 latency,必须看:

  • graph hit rate
  • fallback rate
  • 不同请求类型的收益分布
  • 长尾延迟是否改善

12. 常见落地方式一:固定 shape 池 + 多图复用

这是最典型也最现实的方案。

12.1 基本思路

把常见执行形态离散成少量 bucket,例如按:

  • batch size 区间
  • active sequence 数
  • token 数上限
  • 某些关键维度的 shape

为每个 bucket 预热或懒加载对应图。

12.2 优点

  • 实现路径清晰
  • 对热门 shape 能拿到稳定收益
  • 不需要把所有请求都硬塞进统一图

12.3 代价

  • 需要管理多张图
  • 预热时间更长
  • 显存与元数据开销更高
  • 长尾 shape 很可能仍要 eager fallback

很多时候,这个方案的真实收益,不取决于 Graph 本身,而取决于:

你的线上流量是不是足够“有主形态”。


13. 常见落地方式二:Prefill 保守,Decode 激进

这是 LLM Serving 里很常见的一种思路。

13.1 Prefill 更保守

因为 prefill:

  • shape 波动大
  • 长 prompt 差异大
  • 经常伴随 chunking、prefix reuse、长上下文场景

这使得它 graph 化的复用率未必高。

13.2 Decode 更激进

因为 decode:

  • 单步重复多
  • CPU launch 更容易成为成本
  • 更容易按 active token / batch slot 做稳定化设计

于是常见做法是:

  • prefill 主要保持 eager 或有限 bucket graph
  • decode 路径重点 graph 化

这种分层思路比“全链路一把梭”更符合工程现实。


14. 常见落地方式三:和 TensorRT-LLM / 编译型 runtime 结合

如果说 vLLM 这类 runtime 更强调动态调度、KV 管理和通用服务能力,
那么 TensorRT-LLM 这类方案通常更强调:

  • 编译优化
  • 静态或半静态执行路径
  • kernel / engine 层面更激进的优化

这类系统和 CUDA Graph 的关系往往更自然,因为它们本来就在追求:

  • 更稳定的执行计划
  • 更强的 shape 约束
  • 更低的运行时调度开销

但代价也很明确:

  • 灵活性下降
  • 形态管理更复杂
  • 对动态请求分布的包容度可能不如更通用的 runtime

所以这不是“谁更先进”,而是:

你的业务更偏稳定高吞吐,还是更偏异质在线交互。


15. 显存为什么会变重:Graph 不是白送优化

很多人第一次用 Graph,都会疑惑为什么显存变大了。

原因通常包括:

  • 需要为图捕获保留稳定的内存布局
  • 可能要维护多组 shape bucket 图
  • 某些中间 buffer 生命周期会被拉长
  • 图相关元数据和预留空间会增加

放到 LLM Serving 里,这个问题更敏感,因为显存本来就被三类东西挤压:

  • 模型权重
  • KV Cache
  • 运行时 buffer

Graph 一旦再吃掉一部分显存,系统可能会出现新的 trade-off:

  • 图是复用了
  • launch overhead 是降了
  • 但 KV Cache 预算变小了
  • 最终并发能力或长上下文容量反而下降

这就是典型的局部优化和系统优化冲突。

所以判断值不值,必须看端到端:

  • TTFT
  • TPOT
  • 总吞吐
  • Goodput
  • 可容纳活跃会话数

而不是只看某段执行更快了。


16. 监控和压测时应该重点看什么

如果你真的在做这件事,建议至少看下面几组指标。

16.1 Graph 本身的命中情况

  • graph replay hit rate
  • eager fallback rate
  • 不同 bucket 的命中分布
  • 新图捕获频率

16.2 用户体验指标

  • TTFT P50 / P95 / P99
  • TPOT 或 per-token latency
  • 流式输出卡顿比例
  • 请求超时率、取消率

16.3 系统资源指标

  • GPU 利用率
  • CPU 利用率
  • runtime 线程占用
  • 显存总占用
  • KV Cache 可用容量

16.4 结构化收益指标

  • 短请求 vs 长请求收益差异
  • Prefill vs Decode 收益差异
  • 低并发 vs 高并发收益差异
  • 命中 graph 的请求 vs fallback 请求差异

如果只看平均 latency,很容易被掩盖。


17. 线上最容易踩的几个坑

17.1 只在理想 benchmark 上评估

固定 shape 跑出来很漂亮,不代表线上就稳定收益。

17.2 图 bucket 设计过细

结果是:

  • 图太多
  • 捕获复杂
  • 预热慢
  • 显存爆炸

17.3 图 bucket 设计过粗

结果是:

  • padding 浪费严重
  • graph 命中了,但真实效率不高
  • batch 里无效工作增多

17.4 忽略 fallback 的系统影响

fallback 不是简单“性能稍差一点”,而是可能导致:

  • 延迟分布双峰化
  • 某些请求类型体验明显不稳定
  • 线上尾延迟变差

17.5 只盯 GPU,不看 CPU

CUDA Graph 的一部分价值恰恰在 CPU runtime 路径。
如果不看 CPU 开销和 launch 路径,很难解释为什么它有时有效、有时无效。

17.6 只看单请求,不看 Goodput

有时单步更快,但因为显存更紧、图管理更重,系统整体可承载会话数下降,最终 Goodput 不升反降。


18. 面试里怎么把 CUDA Graph 讲得像真做过

这是个很适合拉开差距的话题,因为它天然带系统味道。

18.1 不要只背定义

如果你只说:

  • CUDA Graph 可以减少 kernel launch overhead

这太浅了。

18.2 更像实战的表达方式

你可以这样讲:

我理解 CUDA Graph 在 LLM Serving 里不是“默认必开”的通用优化,而是一个对执行路径稳定性要求很高的 runtime 优化手段。它主要减少的是 CPU 侧 launch 和一串 kernel 的重复发射成本,所以最适合那种重复度高、单步较碎、shape 相对稳定的路径,典型就是 decode。
但线上推理有 continuous batching、动态长度、KV Cache 管理、prefix 复用等因素,执行形态并不稳定,所以很难一张图吃掉所有请求。实际落地一般会做 bucket 化、多图复用,并保留 eager fallback。
评估它值不值,不能只看固定 shape benchmark 的平均延迟,还要看 graph hit rate、fallback rate、显存额外占用,以及 TTFT / TPOT / Goodput 在真实异质流量下是否真的变好。很多时候它优化的是运行时发射路径,而不是 memory-bound decode、通信或 KV 容量这些更根本的瓶颈。

18.3 面试官继续追问时能展开什么

可以继续讲:

  • 为什么 decode 比 prefill 更适合
  • 为什么 dynamic shape 会限制图复用
  • 为什么显存可能上升
  • 为什么线上收益和离线 benchmark 不一致
  • 为什么要和调度器、bucket、fallback 一起设计

只要能把这些因果链讲顺,基本就不像只看过概念。


19. 总结

CUDA Graph 是个很典型的 AI Infra 话题:
它既不是纯 CUDA 面试题,也不是简单开关项,而是一个必须放进 LLM Serving 系统上下文里理解的 runtime 优化点。

如果只看局部,它的故事很简单:

  • 减少 launch overhead
  • 提高重复路径执行效率

但如果放进真实线上系统,它马上会变成一组 trade-off:

  • 复用率 vs 动态性
  • launch 开销 vs bucket 复杂度
  • 局部延迟优化 vs 显存预算
  • 图命中收益 vs fallback 抖动
  • runtime 优化 vs 系统 Goodput

所以更准确的结论不是:

CUDA Graph 能不能让 LLM Serving 变快?

而是:

当你的执行路径足够稳定、CPU launch 成本已经显性化、并且你愿意用一部分动态性和显存去换运行时效率时,CUDA Graph 才会在 LLM Serving 里体现出稳定价值。

一句话总结这篇文章:

CUDA Graph 优化的是“同一段 GPU 工作怎样更省地重复发射”,而 LLM Serving 真正难的是“哪些工作值得这样发射、哪些路径根本不稳定、哪些系统瓶颈并不在这里”。

如果你正在做 AI Infra / LLM Serving,这个话题很值得掌握,因为它刚好站在 CUDA、runtime、调度和线上落地的交叉点上。也正因为如此,它很适合用来区分“知道概念”和“理解系统”的差别。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/11 2:14:48

codex自创skill

先和codex聊天把要做事情跑通了,然后让他做成sikll,后面就可以直接用

作者头像 李华
网站建设 2026/7/11 2:13:45

CocosCreator UI开发:Mask遮罩与Clipping裁剪的实战选型指南

1. 项目概述:从“傻傻分不清”到“精准选择”刚接触CocosCreator做UI时,很多新手朋友都会在Mask(遮罩)和Clipping(裁剪)这两个功能上卡壳。界面上看起来都能“切掉”一部分内容,但官方文档里又各…

作者头像 李华
网站建设 2026/7/11 2:12:48

信捷TG765-ET触摸屏 1屏连5PLC实战:485总线组网与站号配置5要点

信捷TG765-ET触摸屏一控五PLC实战:485总线组网与站号配置全解析工业自动化现场中,人机界面(HMI)与多台PLC的稳定通讯是系统可靠运行的基础。信捷TG765-ET作为一款支持多种通讯协议的工业触摸屏,其485总线组网能力在实际…

作者头像 李华
网站建设 2026/7/11 2:11:00

OpenPLC Editor v4之技术栈

OpenPLC 编辑器是一款跨平台的桌面 IDE,基于 Electron 运行时构建,结合了 React 驱动的渲染进程与 Node.js 主进程,提供专业的 PLC 编程能力。该技术栈中的每一项技术选择都有其明确目的:从 IEC 61131-3 语言编辑器到实时工业协议…

作者头像 李华