如果把AI系统比作一座大型物流园,CPU仓库、GPU仓库和各类加速器仓库各占一方。模型推理时,一份“张量货箱”可能先在CPU仓库完成预处理,又必须运到GPU仓库做矩阵计算,最后再把结果送回CPU仓库交给用户。掌管这一切的调度员,就像物流调度台的老张,最关心的是:货怎么搬、走哪条路、什么时候搬,才能让整个流水线不停工。 数据迁移,就是这些跨设备“货箱搬运”的总称。它不像核爆般的计算那样光鲜,却很实在:如果搬运规划得差,推理服务会卡在等待拷贝上,吞吐量骤降;如果规划得好,计算与通信可以像流水线一样同时开动,让GPU几乎一刻不停。本篇我们将跟着老张,从物流视角理解数据迁移的成本、异步通信、调度策略,以及在推理部署中的落地。
对AI从业者而言,掌握数据迁移等于掌握推理性能的“隐形杠杆”。在跨设备部署越来越普遍的今天,能通过调度与重叠技术把通信成本藏起来,是把理论跑成吞吐量的核心能力。
可视化演示
展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。
本节配有交互动效(进入视口后自动加载,约 3KB)。
系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T
迁移的根源
老张管理的物流园不断扩张,CPU仓库擅长快速处理杂活儿,GPU仓库擅长并行重活,NPU仓库则专门处理特定算子。一张计算图画出来后,不同步骤落在不同仓库。货箱在CPU仓库加工完,接下来只能在GPU仓库继续,这时候就必须跨仓搬运。
AI Runtime系统里的“货箱”就是张量。每个张量都活在设备自己的内存里:CPU内存与GPU显存彼此隔离,地址空间不互通。GPU不能直接读CPU上的数组,CPU也不能直接访问显存指针。计算图只要跨设备,调度器就必须插入一次数据迁移。
迁移不是免费的。搬运时间可以写成一个非常朴素的公式:
老张很清楚:如果每箱货都要等卡车到了再开工,整个园区就会僵住。所以,了解搬运成本只是第一步,更关键的课题是——如何让搬运不再让人等车。
- 分子 \(D\)这是要搬运的数据量,单位是字节。张量越大、批越大,D就越大。
- 分母 \(B\)这是有效带宽,单位是字节每秒。它代表CPU与GPU之间的实际搬运速度,受总线、驱动和内存类型影响。
- 整体 \(T_{\text{传输}}\)数据量除以带宽得到搬运秒数。调度器看到这个数字,就能判断迁移是否值得。
张量搬运基本功
物流园里的货位不是千篇一律的。老张的园子里有几类仓库:普通货架可以随意堆放,但货车装货时还得先人工翻找;锁页货架则像固定泊位,货车可以直接靠上去装货;GPU仓库里的货架则是高速通道,进出非常快。
内存类型也是这个道理。“普通内存”叫可分页内存,操作系统可能把页面换到磁盘,DMA设备不能直接碰,于是拷贝前必须先复制到一块锁页内存,多走一步路。“锁页内存”也叫固定内存,地址稳定,支持设备直接访问,是从CPU搬运到GPU的重要中转站。GPU显存是设备侧的高速存储,而“GPU与GPU之间”则可能走NVLink等高速互联,比CPU到GPU更快。
想要估算一次迁移的数据体积,可以用张量占用的字节数:
老张明白了:同样的箱子,选错货位会让装卸多绕几道弯。于是他在规划迁移前,会先确认货从哪里出、路上走哪条链路,才不至于白费力气。
- 批量大小 \(B\)一次送入多少个样本。批量越大,货箱越大。
- 通道数 \(C\)特征图的通道数量,通常对应卷积核的数量。
- 高与宽 \(H, W\)特征图的空间尺寸,高宽越大,搬运重量越高。
- 元素字节数 \(d\)每个数值占几个字节,例如FP32占4字节。
开销从哪来
单看公式,搬运似乎只有“重量除以速度”。可老张经验丰富,他知道还有绑绳、开门、签收这些固定动作。即使货物只有一小箱,卡车启动也要花时间。
实际拷贝开销由两部分组成:
更可怕的是同步等待:调度器如果让计算单元等待拷贝结束再干活,那么这段迁移时间就成了纯空闲。老张看到这一幕直摇头:“卡车装卸时工人不干活,这园区效率太低了。”于是,真正的优化思路浮出水面:能不能让搬运和加工同时进行?
- \(D / B_{\text{eff}}\)把真实数据量除以有效带宽。有效带宽可能达不到理论峰值,所以这里用的是实际观察值。
- \(T_{\text{overhead}}\)调一次拷贝接口、建立传输通道、同步收尾的固定开销。即使搬1字节也要付这笔“起步价”。
- 总和 \(T_{\text{copy}}\)实际一次数据迁移至少花这么多时间。如果中途还会经过中间缓冲,开销会更高。
计算通信重叠
老张的物流园引进了连续流水线:这边货车还在卸货,那边的加工台已经开始处理上一批货。虽然单次卸货时间没变,但整个园区不再干等。
在AI Runtime里,这个思想叫“计算通信重叠”。它利用异步拷贝接口,把数据搬运提交给独立的拷贝引擎,CPU和GPU上的计算不用阻塞;再用双缓冲,让当前张量参与计算的同时,下一个张量已经提前搬到设备上。如果通信时间短于计算时间,通信可以被完全藏进计算的“影子”里。
对于一次搬运和一段计算,重叠后的关键路径是:
老张乐了:以前“先卸货再做工”要等整车,现在只要加工时间比卸货长,整车搬运就像不存在一样。这正是异步流水线、多流并发能提升吞吐的底层原因。
- \(T_{\text{comm}}\)数据迁移需要的时间。
- \(T_{\text{calc}}\)计算算子需要的时间。
- \(\max\) 取较大值因为二者在同一时间窗口并行,谁慢谁决定最终耗时。
调度器决策
老张不会满足于只优化一条路线。他会看全局:哪个仓库的活儿可以挪到一起?哪个货箱其实不需要出仓,直接在本地加工?要不要多租几辆货车同时跑?
调度器决策的本质,是一个规划问题。它要决定每个算子放在哪个设备上,决定执行顺序,还会尝试消除不必要的数据拷贝。比如某个中间张量只被下一个算子使用,那调度器可以让算子就地融合,省掉一次搬运。再比如内存池复用:已经分配的显存空间可以反复装新货,避免反复申请和释放。
调度器判断一个新方案是否值得,可以看收益:
老张在调度台上做决策时,从不在乎“看着忙不忙”,只在乎“货有没有及时到位”。所以在真实系统中,调度器也会根据收益函数,在所有可迁移点之间寻找最佳组合。
- \(T_{\text{old}}\)当前方案的计算与执行总耗时。
- \(T_{\text{copy}}\)新方案带来的数据迁移开销,可能是新增的,也可能是消除后剩下的。
- \(T_{\text{new}}\)新方案中计算本身的耗时。
- \(\text{Gain}\)新旧方案的时间差,收益大于0才值得调整。
性能衡量工具
优化做得好不好,不能靠感觉,得用秒表和磅秤说话。老张会在园区门口记录每批货物从进来到完成加工的总时间,再对比不同调度方案之间的差值。
衡量数据迁移优化最直观的指标是加速比,也就是优化前后总耗时之比:
除了加速比,老张还会看吞吐量,也就是单位时间能完成多少推理请求;还会看带宽利用率,判断硬件链路是不是被充分占满。如果带宽利用率高但延迟没降,可能是固定开销主导;如果吞吐量上去了但某台设备长期空转,则说明调度顺序仍有优化空间。
- \(T_{\text{serial}}\)没有重叠时代码串行跑完的总耗时。
- \(T_{\text{overlap}}\)采用异步通信或流水线后的总耗时。
- 比值 \(S\)加速比。数值大于1说明重叠优化有效。
推理落地实践
最后,老张把一整套物流经验用在了最忙碌的岗位上:在线推理服务。推理请求到了之后,不能先等CPU把数据预处理完、再传给GPU、再等结果传回来,那样每一次请求都会累加延迟。
一个朴素的推理延迟可以拆成三段:
在实际部署中,工程师会用预取流水线改造这段延迟:处理当前请求时,异步拷贝下一个请求的输入;返回当前结果时,让输出回传与后续计算重叠。最终暴露给用户的关键路径几乎只剩下计算时间和极短的尾部传输。
老张站在园区门口,看着货车和加工台如同齿轮般咬合,终于明白:数据迁移并不是拖后腿的杂活,而是一块可以被巧妙藏起来的拼图。谁能在搬运上多花心思,谁的推理服务就能跑得更快。
- \(T_{\text{input}}\)请求数据从CPU迁移到GPU的时间。
- \(T_{\text{exec}}\)模型在GPU或加速器上执行计算的时间。
- \(T_{\text{output}}\)结果张量从设备侧传回CPU侧的时间。
AI 应用场景
在线推理服务的输入预取流水线
阶段:部署与在线推理
解决的问题:传统服务端串行执行“预处理→拷贝到GPU→推理→拷回CPU”,每次请求都要等待两次完整的数据迁移,延迟高且吞吐低。AI Runtime通过异步流和双缓冲,将输入迁移与上一请求的计算重叠,让GPU尽量不空闲。
具体案例:视觉质检服务每天处理海量工业图片。CPU先完成缩放和归一化,GPU同时推理前一批图片;当GPU忙时,下一张图片已经在锁页内存中准备就绪,等待拷贝引擎悄悄送达。
多卡并行训练中的梯度通信重叠
阶段:分布式训练调度
解决的问题:模型并行或数据并行训练时,各GPU之间要同步梯度。若等反向传播全部结束再做AllReduce,通信会暴露在关键路径上。调度器可以把梯度切分,让一部分梯度先出发,剩余反向计算继续运行。
具体案例:大语言模型训练时,调度器把梯度分成多个切片,在反向传播后半段就启动跨卡通信。通信时间被藏在计算里,整体训练吞吐显著提升。
边缘与云端协同推理
阶段:跨端云资源调度
解决的问题:边缘设备算力有限,云端有大模型但网络带宽不稳定。数据迁移调度器会根据网络状态选择传输压缩中间特征,而不是原始输入;同时用异步通道让上行传输与本地推理重叠。
具体案例:手机端先运行轻量特征提取模型,把特征搬到云端继续跑大模型。上传过程与本地前几层计算同时进行,用户几乎感觉不到远端等待。
Python 演示
import asyncioimport time async def copy_tensor(duration): # 模拟从CPU内存拷贝张量到GPU显存 await asyncio.sleep(duration) async def compute(duration): # 模拟在GPU上执行一段算子 await asyncio.sleep(duration) async def sync_schedule(iterations, copy_s, calc_s): # 同步调度:先搬完数据,再执行计算 start = time.perf_counter() for i in range(iterations): await copy_tensor(copy_s) await compute(calc_s) return time.perf_counter() - start async def pipeline_schedule(iterations, copy_s, calc_s): # 流水线调度:下一次搬运与本次计算重叠 start = time.perf_counter() await copy_tensor(copy_s) next_copy = None for i in range(iterations): if i < iterations - 1: next_copy = asyncio.create_task(copy_tensor(copy_s)) await compute(calc_s) if next_copy: await next_copy return time.perf_counter() - start async def main(): sync_time = await sync_schedule(4, 0.2, 0.3) pipe_time = await pipeline_schedule(4, 0.2, 0.3) print(f"sync={sync_time:.2f}s pipeline={pipe_time:.2f}s") if __name__ == "__main__": asyncio.run(main())
await copy_tensor(copy_s)阻塞等待本次搬运完成,模拟同步调度的串行等待。next_copy = asyncio.create_task(copy_tensor(copy_s))提前发起下一次张量拷贝,模拟异步预取。await compute(calc_s)计算当前张量,同时下一次搬运正在后台进行。if next_copy: await next_copy确保下一轮的开始数据已经就绪,相当于流水线尾部汇合。sync_time` 与 `pipe_time对比结果会显示流水线方式明显更快,因为搬运时间被藏进了计算时间。
常见误区
误区我总以为把模型和输入一次性搬到GPU显存,后面所有算子都自动在GPU上执行,不会再有任何迁移成本。
正解只要计算图中的算子被分配到不同设备,或者中间结果需要从显存拷回CPU,数据迁移就会再次出现。老张的物流园里,货箱不会只搬一次;调度器必须持续关注每一次跨仓动作。
为何常见:因为很多框架封装的API让用户感知不到底层拷贝,初学者容易把“一次显式迁移”误当成“所有迁移”。
误区既然迁移贵,那应该拼命减少拷贝次数,最好一次都不拷。
正解减少拷贝是重要方向,但“少拷贝”不等于“快”。如果一次大拷贝把计算完全阻塞,反而比把拷贝拆小、再藏进计算缝隙更慢。关键在于不要暴露等待时间。
为何常见:人们天然觉得数量少就代价小,却忽略了时间轴上“是否占用关键路径”才是重点。
误区我用上异步拷贝接口后,单个张量的拷贝时间应该也会缩短。
正解异步拷贝不改变单次传输的字节数和带宽,它只是让调用立即返回,让计算不必等待它完成。单次搬运还是那么多时间,但可以被其他工作覆盖。
为何常见:把“接口不阻塞”误解成“硬件跑得更快”,混淆了任务调度与硬件速度。
误区只要数据迁移把PCIe或NVLink带宽打满,就说明优化做到位了。
正解高带宽利用率可能只是说明搬运任务足够多,却未必换来更低延迟或更高吞吐。固定启动开销、依赖链和尾部传输仍可能拖慢整体,所以还要看加速比和端到端延迟。
为何常见:因为带宽看得见摸得着,而延迟收益需要从整体时间线观察,容易被忽略。
面试题
Q1. 数据迁移耗时最简单的估算公式是?
- A.数据量除以带宽← 正确答案
- B.算力乘以数据量
- C.带宽乘以数据量
- D.延迟除以算力
答案:A。数据迁移类似货车运货,时间等于货物量除以搬运速度,即数据量除以有效带宽。其他选项混淆了算力、带宽与延迟的关系。
Q2. 同步调度中,通信阶段计算单元通常处于?
- A.正常运行继续计算
- B.空闲等待数据到达← 正确答案
- C.提前预取下一批数据
- D.自动降低核心电压
答案:B。同步调度里,计算和通信串行执行,搬运期间计算单元只能空转等待,对应物流园里卡车卸货时加工台停工的状态。
Q3. 将内存从可分页改为锁页,直接收益是?
- A.增加显存容量上限
- B.减少二次拷贝开销← 正确答案
- C.提高GPU核心频率
- D.自动消除全部传输
答案:B。可分页内存需要先复制到锁页内存才能被DMA设备访问,锁页内存省去这道中转,让CPU到GPU的拷贝路径更直接。
Q4. 异步流水线加速的核心原因是?
- A.减少实际传输字节
- B.等待时间被计算填充← 正确答案
- C.提升数据总线主频
- D.压缩张量数据格式
答案:B。异步流水线没有让单次搬运变快,而是让搬运与计算重叠,把原来干等的红色空闲段变成绿色通信段,从而缩短关键路径。
Q5. 下面哪项不属于调度器减少迁移开销的手段?
- A.消除不必要的数据拷贝
- B.将设备放置重新编排
- C.用更大批量压缩模型← 正确答案
- D.复用已分配显存池
答案:C。调度器可以通过算子的设备放置、执行顺序、内存池复用和拷贝消除来优化迁移。加大批量并不属于迁移开销优化手段,反而可能让搬运数据量增大。
Q6. 一次拷贝耗时0.2秒,计算耗时0.3秒,重叠后单批最短耗时是?
- A.0.5秒
- B.0.3秒← 正确答案
- C.0.1秒
- D.0.2秒
答案:B。当通信与计算可以并行执行时,关键路径是两者的最大值,即0.3秒。如果串行执行则是0.5秒,这就是重叠优化的价值。
Q7. 推理返回结果时,若输出拷贝未与后续请求计算重叠,主要影响是?
- A.模型精度明显下降
- B.端到端延迟变长← 正确答案
- C.训练收敛速度变慢
- D.显存空间自动翻倍
答案:B。输出回传处在请求链路的尾部,若不与下一请求的计算重叠,它会直接累加到端到端延迟上,类似外卖小哥送完一单才接下一单,整体效率降低。
Q8. 数据迁移的开销来源包含哪些?
- A.传输数据体积← 正确答案
- B.链路有效带宽← 正确答案
- C.固定启动开销← 正确答案
- D.模型训练轮数
答案:ABC。数据体积和有效带宽决定传输耗时,固定启动开销则是每次拷贝都要付的“起步价”。训练轮数与单次数迁移开销无直接关系。
Q9. 调度器可以采用的通信隐藏手段有哪些?
- A.使用异步拷贝接口← 正确答案
- B.使用双缓冲预取数据← 正确答案
- C.调整计算与传输顺序← 正确答案
- D.缩小物理链路带宽
答案:ABC。异步拷贝、双缓冲和顺序调整都能让通信不阻塞计算。缩小物理链路带宽只会让迁移更慢,不是隐藏手段。
Q10. 在推理流水线中,数据预取的目标包括哪些?
- A.让输入迁移与计算重叠← 正确答案
- B.让输出回传与后续计算重叠← 正确答案
- C.提高单位时间请求数← 正确答案
- D.降低模型存储占用
答案:ABC。预取的核心是让输入和输出迁移都不暴露在关键路径上,从而降低延迟、提高吞吐。模型存储占用属于模型压缩话题,与预取目标无关。