4、计算图与芯片映射:静态图 vs 动态图,如何映射到硬件执行单元。
好,咱们今天聊个硬核话题——计算图怎么跟芯片“对上话”。
我做了这么多年AI系统,发现很多同学把框架和芯片当成两个独立的东西。其实不然。你写的每一行Python代码,最终都要变成芯片上的一条条指令。这中间的桥梁,就是计算图。
说白了,计算图就是AI模型的“施工图纸”。芯片是“施工队”。图纸画得清不清楚,直接影响施工效率。
4.1 静态图 vs 动态图:两种“画图纸”的方式
先看个最简单的例子。假设我们要算 y = (x + 1) * 2。
动态图(比如PyTorch的默认模式):
import torch
x = torch.tensor([3.0])
a = x + 1 # 立刻算,立刻出结果
y = a * 2 # 再算下一步
print(y) # 输出 tensor([8.])
动态图的特点是:写一行,算一行。你执行到 a = x + 1,芯片马上就开始算加法。等算完了,再执行下一行乘法。
这就像你画图纸,画一笔,工人就砌一堵墙。好处是灵活,想改哪里改哪里。坏处是——工人得来回跑,效率不高。
静态图(比如TensorFlow 1.x或PyTorch的torch.compile):
import torch
@torch.jit.script
def compute(x):
a = x + 1
y = a * 2
return y
x = torch.tensor([3.0])
y = compute(x) # 先编译,再执行
print(y)
静态图的做法是:先把整张图画好,再一次性交给芯片执行。你调用 compute(x) 时,框架先扫描整个函数,把 x+1 和 a*2 这两步拼成一张完整的图。然后才让芯片去算。
这就像你先画完整栋楼的图纸,再让工人按图施工。工人不用来回跑,效率自然高。
核心区别一句话总结:动态图是“边想边干”,静态图是“想好了再干”。
4.2 芯片到底在“看”什么?——硬件执行单元
聊完图纸,咱们看看芯片这边。AI芯片里最核心的硬件单元,我列几个:
- ALU(算术逻辑单元):做加减乘除这些基本运算
- MAC(乘累加单元):专门做
a * b + c这种操作,是矩阵计算的主力 - SIMD单元:一条指令同时处理多个数据,比如一次算8个加法
- Tensor Core(张量核心):NVIDIA的专用单元,一次能做4x4矩阵乘法
- DMA(直接内存访问):负责搬数据,不占计算资源
这些单元各有各的脾气。有的擅长并行,有的擅长串行。计算图要做的,就是把图中的每个操作,映射到最合适的单元上。
4.3 静态图如何映射到硬件?——一个完整的例子
咱们拿一个简单的神经网络层来举例:y = ReLU(W * x + b)。
在静态图里,框架会先构建出这张图:
输入 x → 矩阵乘 (W * x) → 加法 (+ b) → ReLU → 输出 y
然后,框架的编译器(比如XLA或TVM)会做三件事:
- 算子融合(Operator Fusion):把
矩阵乘 + 加法 + ReLU合并成一个“大算子”。因为这三个操作是连续的,中间不需要存临时结果。合并后,数据不用来回搬,省时间。 - 内存规划:提前算好每个张量该放哪里。比如
W放显存,x放缓存,b放寄存器。这样执行时不用临时找地方。 - 指令生成:把合并后的大算子,翻译成芯片能懂的指令。比如矩阵乘用Tensor Core,ReLU用SIMD单元。
我个人的习惯:在部署模型到边缘设备时,我几乎只用静态图。因为边缘芯片资源有限,静态图能提前把内存安排得明明白白,不会出现“跑着跑着内存不够”的尴尬。
4.4 动态图如何映射?——灵活但代价高
动态图的映射方式完全不同。还是 y = ReLU(W * x + b),动态图的做法是:
- 遇到
W * x,立刻调用矩阵乘的kernel(内核函数) - 结果出来后,遇到
+ b,再调用加法的kernel - 最后遇到
ReLU,调用ReLU的kernel
每个kernel都是独立的。这意味着:
- 每次调用kernel,都要从显存读数据、写数据
- 无法做算子融合,因为不知道下一步是什么
- 内存分配是动态的,可能产生碎片
但好处也很明显——调试方便。我在研究新模型结构时,一定用动态图。因为可以随时 print 中间结果,看形状对不对。
我曾经踩过的坑:有一次用动态图训练一个超大模型,训练到一半显存爆了。查了半天,发现是动态图每步都分配了新的临时张量,旧的没及时释放。换成静态图后,内存占用直接降了40%。
4.5 核心知识体系:计算图到芯片的映射流程
下面这张图,是我自己总结的映射流程。你看一遍就能明白全貌:
你看,动态图走的是左边这条线——逐算子解释执行,每个算子独立调度到硬件。静态图走右边——先编译优化,再整体调度。两条路都能到硬件,但效率天差地别。
4.6 实际项目中的选择策略
说了这么多,到底该用哪种?我根据经验给个参考:
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 模型研究/快速实验 | 动态图 | 灵活调试,随时改结构 |
| 生产部署 | 静态图 | 性能高,内存可控 |
| 边缘设备/嵌入式 | 静态图 | 资源有限,必须提前规划 |
| 超大模型训练 | 静态图 | 内存优化空间大,支持流水线并行 |
| 动态网络结构(如树/图结构数据) | 动态图 | 静态图难以处理不确定的计算路径 |
我建议:如果你刚开始接触AI框架,先用动态图把模型跑通。等要上线了,再转静态图做优化。别一上来就搞静态图,调试起来能把你逼疯。
4.7 避坑指南
最后分享几个我踩过的坑:
- 别以为静态图一定快:如果模型里有很多if-else分支,静态图会把所有分支都编译进去,反而变慢。这时候动态图更合适。
- 注意控制流:静态图里不能用Python的
if x > 0:这种写法。得用框架提供的torch.where或tf.cond。我第一次用静态图时,光改这个就花了两天。 - 内存碎片问题:动态图频繁分配释放内存,容易产生碎片。长时间运行的推理服务,建议用静态图。
- 算子融合不是万能的:融合后的算子如果太大,可能超出硬件单元的处理能力。我遇到过融合后性能反而下降的情况,后来拆成两个算子才解决。
嗯,关于计算图与芯片映射,今天就聊这么多。记住一句话:动态图让你跑得快(开发效率),静态图让芯片跑得快(执行效率)。两者没有绝对的好坏,关键看你在哪个阶段。
公众号:蓝海资料掘金营,微信deep3321