GPU 架构学习
第九课:GPU 如何执行神经网络卷积
这一课把矩阵乘法继续推进到卷积,重点解释卷积的数据组织、常见映射方式,以及为什么不同卷积在 GPU 上效率差异很大。
目录
一、先理解卷积张量的维度二、卷积到底在计算什么三、一个具体例子四、输出元素之间的并行性五、最简单的线程映射六、卷积中的数据复用1. 输入数据复用2. 权重复用3. 输出部分和复用七、卷积为什么可以转化成矩阵乘法八、什么是 im2col九、im2col 的优点十、im2col 的缺点十一、Implicit GEMM十二、直接卷积 Direct Convolution十三、直接卷积中的 Shared Memory十四、卷积权重放在哪里十五、卷积的数据流类型1. Weight Stationary2. Output Stationary3. Input Stationary4. Row Stationary十六、GPU 卷积中的线程映射十七、为什么一个线程可能计算多个输出通道十八、为什么一个线程可能计算多个空间位置十九、卷积如何使用 Tensor Core二十、为什么通道数会影响 Tensor Core 利用率二十一、1×1 卷积为什么特别像矩阵乘法二十二、3×3 卷积与 1×1 卷积的区别二十三、什么是 Depthwise Convolution二十四、为什么 Depthwise 卷积不一定更适合 GPU二十五、Depthwise Separable Convolution二十六、卷积中的 Padding二十七、Stride 对卷积的影响二十八、Dilation 对卷积的影响二十九、卷积的数据布局NCHWNHWC三十、布局转换也有成本三十一、卷积融合三十二、卷积的完整数据流三十三、卷积为什么可能跑不满 GPU三十四、普通 GPU 与专用卷积加速器的区别三十五、本课核心结论第十课:Tensor Core 与低精度计算导语:这一课把矩阵乘法继续推进到卷积,重点解释卷积的数据组织、常见映射方式,以及为什么不同卷积在 GPU 上效率差异很大。
卷积是 CNN 中最核心的计算之一,例如图像分类、目标检测、超分辨率和语义分割都会大量使用卷积。
卷积看起来与矩阵乘法不同,但从硬件角度看,它的核心仍然是:
GPU 通常会把卷积转化或组织成适合矩阵乘法、Tensor Core 和并行线程执行的形式。
一、先理解卷积张量的维度
一个二维卷积的输入通常写成:
各维度含义是:
| 符号 | 含义 |
|---|---|
| Batch Size,批量大小 | |
| 输入通道数 | |
| 输入特征图高度 | |
| 输入特征图宽度 |
卷积权重为:
其中:
| 符号 | 含义 |
|---|---|
| 输出通道数 | |
| 输入通道数 | |
| 卷积核高度 | |
| 卷积核宽度 |
输出为:
二、卷积到底在计算什么
对于某个输出元素:
需要计算:
其中:
这里:
- 是步长;
- 是填充;
- 是卷积核内部坐标。
可以看出,一个输出元素本质上是:
一组输入数据和一组权重的点积。
三、一个具体例子
假设:
输入特征图:3 个通道
卷积核:3×3
输出通道:64
计算一个输出元素时,需要:
次乘法,以及相应的累加。
如果输入通道是 64,则一个输出元素需要:
次乘法。
因此卷积通常有大量乘加操作,非常适合 GPU。
四、输出元素之间的并行性
不同输出元素通常可以独立计算。
例如:
Y[n, 0, 0, 0]
Y[n, 0, 0, 1]
Y[n, 1, 0, 0]
Y[n, 1, 5, 8]
它们可以分别由不同线程、Warp 或 Block 处理。
卷积的并行维度很多:
Batch 维度 N
输出通道 C_out
输出高度 H_out
输出宽度 W_out
总输出元素数为:
所以卷积通常能产生大量并行任务。
五、最简单的线程映射
最直观的方式是:
一个 Thread 计算一个输出元素。
例如:

图示:五、最简单的线程映射
每个线程内部执行:
float sum = 0.0f;
for (int ci = 0; ci < C_in; ++ci) {
for (int kh = 0; kh < K_h; ++kh) {
for (int kw = 0; kw < K_w; ++kw) {
sum += input[...] * weight[...];
}
}
}
output[...] = sum;
其中 sum 通常保存在寄存器中。
这种实现功能简单,但可能存在大量重复访存。
六、卷积中的数据复用
卷积有三类重要的数据复用。
1. 输入数据复用
同一个输入像素会被多个相邻输出位置使用。
例如一个输入像素可能同时参与:
左上方输出
上方输出
右上方输出
左方输出
中心输出
右方输出
……
2. 权重复用
同一个卷积核权重会在所有空间位置重复使用。
例如:
会用于整张输出特征图中对应输出通道的计算。
3. 输出部分和复用
一个输出元素需要多次乘加。
中间结果最好一直保存在寄存器中:
partial sum
→ partial sum
→ partial sum
→ final output
七、卷积为什么可以转化成矩阵乘法
卷积中的一个输出元素是一个点积。
可以把卷积核展平:
变成长度为:
的向量。
每个输出位置对应一个输入窗口,也可以展平为长度为 的向量。
于是卷积就可以写成:
其中:
输出:
这就变成了标准矩阵乘法。
八、什么是 im2col
im2col 的意思是:
把输入图像中的每个卷积窗口展开成矩阵的一列。
假设输入是:
a b c
d e f
g h i
使用 卷积核,stride 为 1。
卷积窗口包括:
窗口 0:a b
d e
窗口 1:b c
e f
窗口 2:d e
g h
窗口 3:e f
h i
展平后:
窗口 0:a b d e
窗口 1:b c e f
窗口 2:d e g h
窗口 3:e f h i
组织成矩阵:
再把卷积核展平,就可以使用 GEMM 完成卷积。
九、im2col 的优点
im2col 的最大优点是:
可以直接使用高度优化的矩阵乘法库。
矩阵乘法库通常已经实现:
- Shared Memory 分块;
- Warp Tiling;
- Tensor Core;
- 双缓冲;
- 合并访存;
- 寄存器分块。
因此,把卷积转换成 GEMM 后,可以获得很高性能。
十、im2col 的缺点
im2col 会复制大量输入数据。
同一个输入像素可能出现在多个卷积窗口中,因此会在展开矩阵中重复存储。
例如一个内部像素在 卷积中,最多可能被复制到 9 个窗口。
这会带来:
- 额外内存占用;
- 额外数据搬运;
- 额外预处理时间;
- 更大的显存带宽压力。
所以现代高性能卷积实现不一定真的完整生成一个巨大的 im2col 矩阵。
十一、Implicit GEMM
更常见的做法是:
在计算过程中动态生成矩阵乘法所需的数据,而不显式保存完整 im2col 矩阵。
这种方式称为:
Implicit GEMM
硬件或 Kernel 在读取输入时,根据输出位置和卷积核位置实时计算输入地址。
逻辑上仍然执行矩阵乘法:
但 并没有完整地存储在 Global Memory 中。
数据流程为:

图示:十一、Implicit GEMM
这样可以避免完整 im2col 的中间数据开销。
十二、直接卷积 Direct Convolution
除了 GEMM 转换,还可以直接按照卷积公式计算。
例如一个 Block 负责一块输出区域:

图示:十二、直接卷积 Direct Convolution
为了计算它,需要加载稍大的输入区域:

图示:十二、直接卷积 Direct Convolution
对于 卷积,计算 输出区域时,需要的输入区域可能是:
因为周围还要提供卷积核覆盖的边界数据。
这个额外区域通常称为:
Halo,边缘扩展区域
十三、直接卷积中的 Shared Memory
直接卷积可以让 Block 内线程协作加载输入 Tile。
过程如下:
1. 从 Global Memory 加载输入 Tile
2. 保存到 Shared Memory
3. 同步
4. 不同线程读取自己的卷积窗口
5. 使用权重进行乘加
6. 输出结果
输入 Tile 加载一次后,可以被多个输出线程复用。
这与矩阵乘法 Tiling 的思想一致。
十四、卷积权重放在哪里
卷积权重可以来自:
- Global Memory;
- L2 Cache;
- L1 Cache;
- Shared Memory;
- Constant Cache;
- 寄存器。
具体选择取决于权重大小和复用方式。
对于很小的卷积核,例如 ,某些权重可以在缓存、Shared Memory 或寄存器中高效复用。
权重的典型数据流是:
Global Memory
↓
L2 / L1
↓
Shared Memory 或寄存器
↓
执行单元
因为同一个权重会在多个空间位置使用,所以权重复用率通常很高。
十五、卷积的数据流类型
从加速器和 ASIC 角度,常见数据流包括:
Weight Stationary
Output Stationary
Input Stationary
Row Stationary
它们的核心问题是:
哪一类数据尽量留在本地,不频繁移动?
1. Weight Stationary
让权重尽可能停留在寄存器或处理单元内部。
权重固定
输入不断流过
部分和继续累加
适合权重能够被大量输入位置复用的场景。
优势:
- 减少权重读取;
- 适合卷积核在空间位置上的重复使用。
缺点:
- 输入和部分和可能需要更多移动。
2. Output Stationary
让输出部分和尽量保存在本地。
partial sum 保存在寄存器
输入和权重不断进入
最后一次性写回输出
这与前面矩阵乘法中把 sum 保存在寄存器里的思想相同。
优势:
- 避免中间部分和反复读写;
- 适合较长的累加过程。
GPU 中很多矩阵乘法和卷积 Kernel 都具有明显的 Output Stationary 特征。
3. Input Stationary
让输入数据尽可能留在本地,被多个权重或输出通道重复使用。
输入固定
不同权重依次参与计算
适合输入数据跨多个输出通道复用的情况。
4. Row Stationary
Row Stationary 试图同时利用:
- 输入复用;
- 权重复用;
- 部分和复用。
它常见于专用神经网络加速器的数据流设计中。
GPU 通常不会只固定采用一种抽象数据流,而是通过多级缓存、Shared Memory 和寄存器形成混合的数据复用方式。
十六、GPU 卷积中的线程映射
卷积可以有多种线程映射方式。
一种可能是:
一个 Thread → 一个输出元素
另一种是:
一个 Thread → 多个相邻输出元素
还有:
一个 Warp → 一个输出 Tile
以及:
一个 Block → 多个输出通道 × 多个空间位置
实际高性能实现通常会同时在以下维度分块:
- 输出通道;
- 输出空间;
- 输入通道;
- Batch。
十七、为什么一个线程可能计算多个输出通道
假设同一个输入窗口需要计算多个输出通道:
它们使用相同的输入窗口,但使用不同权重。
如果一个线程或 Warp 同时计算多个输出通道,就可以复用输入数据。
例如:

图示:十七、为什么一个线程可能计算多个输出通道
代价是需要更多输出累加寄存器。
十八、为什么一个线程可能计算多个空间位置
相邻输出位置会使用高度重叠的输入窗口。
例如 卷积:
输出位置 0 使用输入列 0、1、2
输出位置 1 使用输入列 1、2、3
两者共享输入列 1、2。
如果同一个线程或 Warp 同时计算相邻输出位置,就可以提高输入数据复用。
但同样会增加:
- 寄存器数量;
- 地址计算复杂度;
- 每线程工作量。
十九、卷积如何使用 Tensor Core
要使用 Tensor Core,需要把卷积组织成矩阵乘加。
核心是把归约维度:
看成矩阵乘法中的 K 维。
输出通道 可以映射为 M 维。
输出空间和 Batch:
可以映射为 N 维。
于是:
乘以:
得到:
这与标准 GEMM 完全对应。
二十、为什么通道数会影响 Tensor Core 利用率
Tensor Core 通常以固定大小的小矩阵块工作。
如果通道维度较大且能与矩阵块良好对齐,例如:
C_in = 64
C_out = 128
通常更容易充分使用 Tensor Core。
如果通道数很小或尺寸不规则,例如:
C_in = 3
C_out = 7
就可能出现:
- 矩阵块填充不足;
- 部分执行通道空闲;
- Padding 计算;
- Tensor Core 利用率较低。
所以网络层的张量形状会直接影响硬件效率。
二十一、1×1 卷积为什么特别像矩阵乘法
对于 卷积:
输出公式变为:
它不需要访问邻域,只在通道维度上做点积。
可以直接组织为:
其中:
因此 1×1 卷积几乎就是标准矩阵乘法,通常非常适合 Tensor Core。
二十二、3×3 卷积与 1×1 卷积的区别
1×1 卷积:
- 没有空间邻域;
- 地址模式简单;
- 易于转换成 GEMM;
- 通常计算效率较高。
3×3 卷积:
- 需要访问邻域窗口;
- 输入数据复用更复杂;
- 需要处理 Padding 和边界;
- 地址计算更多;
- 但每个输出元素计算量也更大。
在很多 CNN 中,1×1 卷积负责通道变换,3×3 卷积负责空间特征提取。
二十三、什么是 Depthwise Convolution
普通卷积中,每个输出通道都会对所有输入通道进行累加。
Depthwise Convolution 中,每个输入通道单独使用自己的卷积核:
并且通常:
输入通道 0 → 输出通道 0
输入通道 1 → 输出通道 1
输入通道 2 → 输出通道 2
不进行跨通道累加。
其计算量大幅下降。
普通卷积计算量约为:
Depthwise 卷积约为:
少了 与 之间的大规模通道组合。
二十四、为什么 Depthwise 卷积不一定更适合 GPU
Depthwise 卷积计算量少,但数据搬运量未必同比例减少。
每个输入数据参与的计算较少,权重复用和通道复用也降低。
因此其计算强度往往较低。
普通卷积:
大量通道乘加
→ 计算密集
Depthwise 卷积:
每个通道独立
→ 计算量少
→ 更容易受内存带宽限制
所以 Depthwise 卷积虽然 FLOPs 少,但实际运行时间不一定按 FLOPs 比例下降。
这也是移动端网络中常见的现象:
理论计算量很低,不代表 GPU 延迟一定很低。
二十五、Depthwise Separable Convolution
Depthwise Separable Convolution 通常分为两步:
第一步:Depthwise 3×3
第二步:Pointwise 1×1
Depthwise 负责空间特征提取。
Pointwise 负责通道混合。
虽然计算量比普通卷积低很多,但两个阶段可能产生中间特征图:
输入
↓
Depthwise 输出写回显存
↓
Pointwise 再次读取
↓
最终输出
如果能进行 Kernel Fusion,可以减少中间结果的读写。
但融合会增加寄存器、Shared Memory 和调度复杂度。
二十六、卷积中的 Padding
Padding 是在输入边界外补充数据,通常补 0。
例如:
原始输入:
a b c
d e f
g h i
Padding 后:
0 0 0 0 0
0 a b c 0
0 d e f 0
0 g h i 0
0 0 0 0 0
硬件实现时不一定真的创建一个更大的补零矩阵。
更常见的是地址判断:
if (input_h >= 0 &&
input_h < H &&
input_w >= 0 &&
input_w < W) {
value = input[...];
} else {
value = 0;
}
边界线程可能产生分支,但通常只影响特征图边缘。
二十七、Stride 对卷积的影响
Stride 决定卷积窗口移动的步长。
Stride 为 1:
窗口每次移动一个像素
Stride 为 2:
窗口每次移动两个像素
Stride 增大后:
- 输出特征图变小;
- 总计算量下降;
- 输入窗口重叠减少;
- 输入数据复用率可能下降;
- 地址访问间隔发生变化。
因此 stride 会影响数据复用和访存模式。
二十八、Dilation 对卷积的影响
Dilation,也就是空洞卷积,会让卷积核采样点之间出现间隔。
普通 :
x x x
x x x
x x x
Dilation 为 2:
x . x . x
. . . . .
x . x . x
. . . . .
x . x . x
它扩大感受野,但会带来:
- 更分散的输入访问;
- 更复杂的地址计算;
- 更低的空间局部性;
- 合并访存难度增加。
所以空洞卷积在 GPU 上的效率可能低于规则的密集卷积。
二十九、卷积的数据布局
常见布局有:
NCHW
Batch
→ Channel
→ Height
→ Width
内存中 W 维通常最连续。
NHWC
Batch
→ Height
→ Width
→ Channel
内存中 Channel 维通常最连续。
不同布局适合不同访问方式。
例如 Tensor Core 计算经常希望通道数据连续,以便高效加载向量化数据,因此某些实现更偏好 NHWC 或特殊打包格式。
不过最佳布局取决于:
- GPU 架构;
- 数据类型;
- 卷积类型;
- Tensor Core 指令;
- 上下层算子布局;
- 是否需要额外转置。
三十、布局转换也有成本
假设某个卷积在 NHWC 下更快,但输入当前是 NCHW。
如果需要先执行:
NCHW → NHWC
卷积结束后再执行:
NHWC → NCHW
布局转换需要完整读写张量。
所以即使卷积本身加速,也可能被转换开销抵消。
深度学习框架通常会综合考虑整个计算图,而不仅仅优化某一层。
三十一、卷积融合
卷积后常跟随:
Bias
BatchNorm
ReLU
Pooling
如果分别执行:
Conv → 写显存
Bias → 读写显存
BatchNorm → 读写显存
ReLU → 读写显存
会产生大量中间数据搬运。
融合后可以:
卷积累加完成
↓
寄存器中加 Bias
↓
执行缩放和偏移
↓
执行 ReLU
↓
一次性写回输出
这可以显著减少 Global Memory 流量。
这类过程常称为:
Epilogue Fusion
三十二、卷积的完整数据流
一个高性能卷积可以粗略表示为:

图示:三十二、卷积的完整数据流
核心目标仍然是:
少搬数据
多做计算
尽量复用
最后写回
三十三、卷积为什么可能跑不满 GPU
即使卷积理论 FLOPs 很高,也可能受到以下限制:
- 输入或输出尺寸太小;
- Batch 太小;
- 通道数太少;
- 通道数无法与 Tensor Core 对齐;
- Depthwise 导致计算强度低;
- Padding 或 Dilation 造成不规则访存;
- Shared Memory 使用过多;
- 寄存器使用过多;
- Kernel 启动并行度不足;
- 布局转换开销;
- 中间结果反复读写;
- Tensor Core 数据供应不足。
因此不能只根据“这是卷积”就判断一定是计算受限。
三十四、普通 GPU 与专用卷积加速器的区别
GPU 追求通用性:
- 可以执行各种卷积;
- 支持不同张量尺寸;
- 支持不同数据类型;
- 支持图形、科学计算和 AI;
- 使用可编程线程和存储层次。
专用 ASIC 加速器则可以固定:
- 计算阵列;
- 数据流;
- 片上 SRAM 结构;
- NoC;
- 精度;
- 稀疏机制;
- 卷积循环映射。
专用加速器可以减少通用控制开销,但灵活性较低。
从 ASIC 角度,卷积可以拆成多层循环:
for n
for co
for ho
for wo
for ci
for kh
for kw
Y[n][co][ho][wo]
+= X[n][ci][...]
* W[co][ci][kh][kw];
硬件架构设计的核心问题,就是决定:
- 哪些循环并行展开;
- 哪些循环按时间执行;
- 哪些数据保存在寄存器;
- 哪些数据保存在 SRAM;
- 数据如何在 PE 阵列中移动。
三十五、本课核心结论
第一,卷积的每个输出元素本质上是输入窗口与卷积核之间的点积。
第二,卷积具有 Batch、输出通道和输出空间等多个并行维度。
第三,卷积中的输入、权重和输出部分和都存在数据复用。
第四,卷积可以通过 im2col 或 Implicit GEMM 转化为矩阵乘法。
第五,显式 im2col 简单但会复制大量输入数据。
第六,Implicit GEMM 不显式保存完整展开矩阵,而是在计算过程中动态生成数据。
第七,直接卷积可以把输入 Tile 放入 Shared Memory,供多个输出线程复用。
第八,Weight Stationary、Output Stationary 和 Input Stationary 描述了不同的数据保持策略。
第九,1×1 卷积非常接近标准 GEMM,通常适合 Tensor Core。
第十,Depthwise 卷积 FLOPs 很少,但计算强度也较低,可能受内存带宽限制。
第十一,NCHW、NHWC 等数据布局会直接影响合并访存和 Tensor Core 使用效率。
第十二,卷积与 Bias、BatchNorm、激活函数融合,可以减少中间结果读写。
第十三,卷积性能不仅取决于计算量,还取决于通道数、尺寸、布局、数据复用和并行度。
第十四,从 ASIC 角度看,卷积架构设计本质上是循环展开和数据流映射问题。
下一课进入 GPU 中更现代、更重要的计算结构:
第十课:Tensor Core 与低精度计算
将重点讲解:
FP32、TF32、FP16、BF16、FP8、INT8、FP4
为什么低精度速度更快
Tensor Core 的 MMA 指令
Warp 如何协作完成矩阵运算
累加精度为什么通常高于乘法精度
量化、缩放因子和数值范围
Blackwell Transformer Engine 的基本思想