首页优化地图方法索引基础概念
返回技术汇总表 ↗

M019 · 独立详解

执行图捕获与重放:少发指令,仍然重新计算

从主机下发与设备执行的时间线开始,解释CUDA Graphs、固定地址、新输入、形状分档和训练捕获。

调度与并行详解已编写
视频讲解待制作

这篇文章的小视频尚未制作;可以先阅读下方文字详解。

本页目录

01 · 小内核跑得快,CPU却可能发不够快

模型的一步可能包含许多短设备任务。主机需要准备参数、经过框架与驱动,把它们交给设备。若提交太慢,设备会出现等待;每个内核自身很快,却没有形成紧凑的执行。

执行图把可重复的任务和依赖组织成记录,提前完成部分准备,后续通过重放再次执行这些任务。主要目标是减少重复任务下发成本,而非修改Attention数学或减少模型参数。

依据:NVIDIA · CUDA Graphs任务图、捕获与运行 ↗

02 · 为什么不能把所有CPU开销简单相加

自设单队列例子:每个任务的CPU提交需4 μs,设备执行需1 μs,连续10个任务。CPU准备下一任务可以与设备工作重叠;第一任务到4 μs才提交,第十个到40 μs提交,最后在41 μs结束。不是10×(4+1)=50 μs。

路径简化排程结束时间
逐个提交提交时刻4、8…40;每任务在收到且前任务完成后执行1 μs41 μs
图重放假设一次提交4 μs,图内10个串行任务各1 μs14 μs

若每任务设备时间变为10 μs,CPU能够提前准备后续任务,两种路径在同一简化模型下都于104 μs结束。它说明收益取决于是否暴露提交开销,不能把每次提交时间直接加到设备总计算时间上。

03 · 一次准备,后续多次运行

步骤做什么需要注意
预热正常执行,让相关路径与资源准备好预热在实际运行,不是只记录。
定义/捕获记录任务、参数和依赖stream capture中的设备工作通常被记录,不能把捕获当作已产出新结果。
实例化/准备得到可重复执行的运行对象不同API封装程度不同,不一定显式暴露所有步骤。
重放再次调度图中的设备工作每次仍执行数学计算。

CUDA可以显式构建任务图,也可以从stream捕获。PyTorch的CUDAGraph包装了常用路径。这里讨论普通固定任务图;底层CUDA还有更新节点等高级接口,其约束与框架包装不能混为一谈。

依据:NVIDIA · CUDA Graphs任务图、捕获与运行 ↗ · PyTorch 2.14 · CUDA Graphs与使用约束 ↗

04 · 输入内容可以变,捕获的地址仍要有效

普通PyTorch CUDA Graph重放使用捕获时的输入/输出内存地址。可以把新数据复制到原输入缓冲,但只让Python变量指向另一块Tensor,不会自动修改捕获的指针。

同一个执行图,怎样读到新输入

教学固定缓冲模型,图引用原缓冲A并执行Y=2X;不同地址以A/B代称。不运行CUDA/ACL,不模拟节点更新API。
教学示意 · 不代表设备实测
static_input = allocate_fixed_buffer()
graph = capture(lambda: compute(static_input))

static_input.copy_(new_data)   # 把内容写进原地址
graph.replay()                 # 对新内容重新计算

static_input = new_data        # 仅改Python变量指向
# graph仍引用捕获的原缓冲;不会因变量改名自动改地址。

输出也要管理生命周期:后一次重放可能覆盖同一输出缓冲。若要保留两次结果,需在覆盖前复制或按引擎支持的缓冲方案处理。执行期间不能让另一请求改写正在被读取的输入。

依据:PyTorch 2.14 · CUDA Graphs与使用约束 ↗

05 · Decode批量一直变,怎样重放

服务可能某一轮有3个活跃请求,下一轮变成5个;Attention还读取不断增长的历史KV。一个普通固定形状图不能直接适配任意batch、控制流和布局变化。

策略例子代价与条件
分档捕获容量4、8分别建图,实际3个请求走4档额外计算与每档缓冲;无效槽必须屏蔽。
局部捕获只捕获稳定的计算片段边界仍有CPU调度与衔接成本。
动态部分走普通路径不兼容输入回退到非重放执行保证正确,但收益和延迟会随路径变化。
使用支持的更新机制按底层API条件更新部分节点参数不是任意改shape都可更新,框架支持另查。

KV长度变化有时可通过固定布局中的元数据内容表达,让支持的内核读取本轮有效长度或块表。这依赖具体内核和引擎支持,不能推导出“图能支持任意动态shape”。档位也要覆盖适配器、布局和其他会改变执行路径的条件。

依据:NVIDIA · CUDA Graphs任务图、捕获与运行 ↗

06 · 捕获区域里,什么容易出问题

  • CPU读取设备标量并据此做Python分支:会引入同步或不可重复控制路径。
  • 普通Python日志、计数或队列更新:不会因设备图重放自动再执行。
  • 输入输出地址失效、形状或布局不兼容:可能违反固定任务图约束。
  • 随机操作与状态更新:需要使用框架支持的图安全机制,不能无依据假定每次完全相同或自动正确。
  • 多stream或集合通信:需要匹配依赖、rank调用顺序与后端支持。

上述是普通固定图的入门边界。CUDA底层支持一些条件节点等更高级能力,PyTorch包装的允许范围要另查;不要把“CPU工作不自动重放”误写为“所有CUDA图都禁止Host节点”。

依据:PyTorch 2.14 · CUDA Graphs与使用约束 ↗ · NVIDIA · CUDA Graphs任务图、捕获与运行 ↗

07 · 训练能不能把反向也捕获

可以按框架支持捕获稳定的前后向部分,有些配方还能纳入优化器更新。但必须处理梯度缓冲、参数状态、RNG、Loss Scaling和学习率等状态变化。普通forward捕获不会自动让loss.backward和optimizer.step都随它重放。

预热若包含optimizer.step就真实改变了参数和优化器状态,应把它当作实际训练或正确恢复状态。不能用随机假数据更新几次,再宣称训练起点没有改变。

初学实现可以先捕获固定推理函数,再验证新输入;之后按官方的autograd-aware局部捕获或整步示例扩展训练。动态CPU逻辑保留在边界外。

依据:PyTorch 2.14 · CUDA Graphs与使用约束 ↗

08 · 一个最小PyTorch推理接入片段

CUDA推理演示 · 需要支持的GPU与PyTorch;本地未执行此设备片段
import torch
x = torch.tensor([-1., 2.], device="cuda")
s = torch.cuda.Stream()
s.wait_stream(torch.cuda.current_stream())
with torch.cuda.stream(s):
    for _ in range(3):
        y = x * 2
torch.cuda.current_stream().wait_stream(s)

g = torch.cuda.CUDAGraph()
with torch.cuda.graph(g):
    y = x * 2
x.copy_(torch.tensor([3., 4.], device="cuda"))
g.replay()
torch.cuda.synchronize()
# y内容应为[6,8];对照普通执行,并保留x/y引用。

下方标准库CPU例子模拟固定缓冲引用,验证就地改内容与变量重新绑定的差别,也实现主机提交和设备队列的教学时间线。它不调用CUDA或ACL,不测真实下发开销。

昇腾CANN运行时有aclmdlRICaptureBegin/End记录Stream任务、aclmdlRIExecuteAsync执行运行实例的接口,目标同样包含减少Host下发。该文档列有产品支持表与任务更新机制,使用时核对CANN版本、产品和框架入口;这不是torch.cuda接口可原样替换的保证。

依据:PyTorch 2.14 · CUDA Graphs与使用约束 ↗ · CANN Community 9.1.0 · aclmdlRICaptureBegin ↗

09 · 执行图、编译图和结果缓存

对象复用的是什么输入变了会怎样
编译缓存根据条件生成的执行方案兼容时用同一方案重新计算。
执行图重放设备任务与依赖及准备好的运行对象向兼容缓冲写新内容,再执行任务。
结果缓存此前某输入得到的输出只有满足缓存语义时才直接返回旧结果。

因此图重放不是缓存生成答案,也不会跳过自回归每一步的计算依赖。它可以与编译、融合和连续批处理组合;每次组合都要核对固定部分与动态部分在哪里衔接。

10 · 测量与自查

  • 分别报告预热/捕获成本、稳态重放、输入复制和完整请求延迟。
  • 固定工作量比较CPU提交时间、设备时间和端到端时间,明确同步位置。
  • 统计各档位命中、padding浪费、图私有池/工作区容量与回退路径。
  • 连续写入不同输入,验证确实得到不同正确结果,并检查输出覆盖。
  • 自查:为什么改变量绑定不等于改捕获指针?为什么重放仍可能有10个kernel?为什么GPU计算已经很长时,减少CPU提交未必明显提速?

流程位置与关联方法

这个方法可能出现在多个位置;计算线包含模型计算与算子实现,执行线包含运行时、内存和通信。

资料与核查范围

原理依据以原始论文和官方文档为主;算例与时间线是教学推导。文档核查日期:2026-10-10。引用 main / latest 文档时,实际接口以安装版本为准。

代码为接入片段或教学伪代码;本页没有声称完成 NVIDIA / 昇腾硬件实测。

← 返回总表中的 M019继续阅读:长度分组、去 Padding 与 Packing →
AI Infra 知识库 · 总览 → 独立讲解网站更新 2026-10-08
搜索知识库

搜索方法与基础概念 · Esc 关闭