2、计算图与算子基础

说实话,很多做模型加速的同学,一上来就盯着算子替换。但我觉得,真正理解计算图和算子,才是加速的根基。今天我们就聊聊这个。

2.1 计算图的概念

计算图,说白了就是描述计算流程的「地图」。你把输入数据放进去,它告诉你每一步怎么算,最后输出什么。

我习惯把计算图看作一个有向无环图(DAG)。每个节点是一个算子,每条边是数据的流向。举个例子:

输入 → Conv → ReLU → Pool → FC → Softmax → 输出

这就是一个最简单的计算图。你想想看,模型推理时,数据就是沿着这条路径流动的。

核心要点:计算图的价值在于,它把复杂的神经网络拆解成了一个个可优化的步骤。每个步骤(算子)都可以单独分析、替换、融合。

我在项目中遇到过,有些同学直接拿 PyTorch 的模型就跑推理,完全不管计算图长什么样。结果性能上不去,也不知道瓶颈在哪。嗯,这就是没理解计算图。

2.2 算子的定义与分类

算子,就是计算图里的「操作」。每个算子完成一个特定的数学运算。

我个人习惯把算子分成三类:

  • 计算密集型算子:比如 Conv、MatMul。这类算子主要瓶颈在算力(FLOPS)。
  • 访存密集型算子:比如 ReLU、Softmax、Element-wise 操作。这类算子主要瓶颈在带宽。
  • 控制流算子:比如 Reshape、Split、Concat。这类算子不涉及太多计算,但会影响数据排布。

小技巧:分析算子性能时,先判断它是计算密集型还是访存密集型。方向错了,优化就白费。

我曾经在一个项目中,花了两周优化一个 Conv 算子,结果发现整个模型的瓶颈其实在 Softmax 的访存上。方向错了,白忙活一场。

2.3 常见算子详解

Conv(卷积)

卷积是 CNN 的核心算子。它的计算模式是:输入特征图与卷积核做滑动窗口点积。

为什么它容易成为瓶颈?因为计算量太大了。举个例子:

输入: 1x3x224x224
卷积核: 64x3x3x3
输出: 1x64x224x224

计算量 ≈ 64 × 3 × 3 × 3 × 224 × 224 ≈ 86M FLOPS

你看,一个卷积层就接近一亿次运算。模型里通常有几十层,总计算量可想而知。

注意:卷积的优化方向主要是减少计算量(如分组卷积、深度可分离卷积)或提高计算效率(如 Im2Col + GEMM、Winograd)。

MatMul(矩阵乘法)

MatMul 是 Transformer 和全连接层的核心。它的计算模式是:两个矩阵相乘。

我遇到过很多同学,觉得 MatMul 就是简单的乘法。其实不然。MatMul 的访存模式非常关键:

  • 如果数据排布不对,缓存命中率会很低
  • 如果矩阵尺寸不是对齐的,计算效率会下降

说白了,MatMul 优化的核心就是「让数据尽量待在缓存里」。

ReLU(线性整流单元)

ReLU 很简单:y = max(0, x)。但它有个特点:访存密集。

为什么?因为 ReLU 的计算量几乎为零(一个比较操作),但需要读取输入、写入输出。所以它的瓶颈在内存带宽。

优化思路:ReLU 通常可以和前面的 Conv 或 MatMul 融合。这样就不用单独读写一次数据了。我习惯叫它「算子融合」。

Softmax

Softmax 用在分类层的输出。它的计算分三步:

  1. 对每个元素求指数:e^x
  2. 求和
  3. 每个元素除以和

你想想看,这里有个问题:指数运算很慢,而且需要两次遍历数据(一次求指数,一次求和)。

我曾经优化过一个模型,发现 Softmax 占了 15% 的时间。后来用在线 Softmax(一次遍历完成)替换,时间降到了 5%。

2.4 算子性能瓶颈分析

分析算子瓶颈,我一般看三个指标:

指标 含义 瓶颈类型
计算时间 算子实际执行时间 计算密集型
访存时间 数据加载/存储时间 访存密集型
启动开销 算子调度、内核启动时间 控制流密集型

怎么判断瓶颈?我有个简单方法:

  • 如果算子的计算时间远大于访存时间 → 计算瓶颈
  • 如果访存时间远大于计算时间 → 访存瓶颈
  • 如果两者差不多 → 平衡型,需要综合考虑

实战经验:我建议你用 profiling 工具(如 NVIDIA Nsight、PyTorch Profiler)跑一遍模型。看看每个算子的耗时占比。通常前 3-5 个算子占了 80% 以上的时间。优先优化它们。

嗯,这里要注意:不要只看算子本身的耗时,还要看它和前后算子的交互。比如 Conv 后面跟 ReLU,如果分开执行,会有两次访存。如果融合,就只需要一次。

下面是我画的一张计算图与算子分类的框架图,帮你理清思路:

计算图与算子分类框架 输入数据 计算图 (DAG) 计算密集型 访存密集型 控制流密集型 Conv, MatMul ReLU, Softmax Reshape, Concat 瓶颈:算力 (FLOPS) 优化:减少计算量、提高并行度 瓶颈:带宽 (Memory) 优化:算子融合、减少访存 瓶颈:启动开销 优化:算子合并、减少调度

这张图把计算图、算子分类和瓶颈分析串起来了。你想想看,优化一个模型,其实就是沿着这张图,找到最耗时的算子,然后针对它的瓶颈类型做优化。

好了,这一章的内容就到这里。记住:理解计算图和算子,是算子替换加速的第一步。后面我们会深入每个算子的优化技巧。

想要更多资料,联系可可