GPU 架构学习
第十课:Tensor Core 与低精度计算
这一课讲 Tensor Core 和低精度计算:先理解不同数值格式,再看 MMA 指令、混合精度累加,以及 Blackwell 中 Transformer Engine 的作用。
目录
一、浮点数由什么组成二、常见数值格式三、动态范围和精度有什么区别四、为什么低精度可以提高性能1. 减少存储空间2. 提高有效带宽3. 一个计算单元可以处理更多数据4. 寄存器和片上存储利用率提高五、低精度为什么会产生误差六、必须区分四种“精度”七、为什么累加精度通常要更高八、Tensor Core 的 MMA 是什么九、Warp 如何协作执行 MMA十、Tensor Core Kernel 的典型数据流十一、Tensor Core 和 CUDA Core 的区别十二、FP16 和 BF16 应该怎么选择FP16BF16十三、什么是 Loss Scaling十四、TF32 是什么十五、FP8 为什么有两种主要格式E4M3E5M2十六、FP8 为什么需要缩放因子十七、Per-Tensor Scaling 的局限十八、Block Scaling 和 Micro-Tensor Scaling十九、Blackwell 的 NVFP4二十、Blackwell Transformer Engine 是什么二十一、INT8 量化与浮点低精度有什么区别二十二、INT8 和 FP8 的直观区别INT8FP8二十三、Per-Tensor、Per-Channel 和 Per-Block 量化Per-TensorPer-ChannelPer-Block二十四、训练和推理的低精度要求不同推理训练二十五、什么是混合精度训练二十六、哪些操作通常更敏感二十七、量化感知训练与训练后量化PTQ:Post-Training QuantizationQAT:Quantization-Aware Training二十八、低精度为什么不一定真的更快二十九、低精度与 Roofline 的关系提高峰值计算能力减少数据传输量三十、从 ASIC 角度看 Tensor Core本课核心结论第十一课:GPU 的缓存、显存控制器与内存事务导语:这一课讲 Tensor Core 和低精度计算:先理解不同数值格式,再看 MMA 指令、混合精度累加,以及 Blackwell 中 Transformer Engine 的作用。
前面讲到,普通 CUDA Core 主要执行标量运算:
Tensor Core 则面向矩阵乘加:
Tensor Core 的高性能不仅来自“矩阵并行”,还来自大量采用 FP16、BF16、FP8、INT8、FP4 等低精度数据。
这一课需要解决三个问题:
- 低精度到底降低了什么?
- 为什么位数更少会更快?
- Tensor Core 如何在提高吞吐率的同时控制误差?
一、浮点数由什么组成
一个二进制浮点数通常包含三部分:
符号位 Sign
指数位 Exponent
尾数位 Mantissa/Fraction
可以近似写成:
其中:
- 符号位决定正负;
- 指数决定能够表示多大或多小的数;
- 尾数决定数值表示得有多精细。
因此:
指数位越多 → 动态范围越大
尾数位越多 → 有效精度越高
例如两个格式都占 16 bit,但指数和尾数的分配不同,它们的数值特性也会完全不同。
二、常见数值格式
| 格式 | 总位宽 | 指数位 | 尾数位 | 主要特点 |
|---|---|---|---|---|
| FP32 | 32 | 8 | 23 | 范围和精度较高 |
| FP16 | 16 | 5 | 10 | 精度较好,但范围较小 |
| BF16 | 16 | 8 | 7 | 范围接近 FP32,但精度较低 |
| TF32 | 32位接口格式 | 8 | 约10 | FP32 范围、较低乘法精度 |
| FP8 E4M3 | 8 | 4 | 3 | 精度较高、范围较小 |
| FP8 E5M2 | 8 | 5 | 2 | 范围较大、精度较低 |
| FP4 E2M1 | 4 | 2 | 1 | 必须配合细粒度缩放使用 |
FP16 使用5位指数和10位尾数;BF16使用8位指数和7位尾数,因此 BF16 保留了接近 FP32 的动态范围,而 FP16 在有限范围内提供了更细的数值间隔。TF32 主要用于 Tensor Core 矩阵运算,不应简单理解为一种普通的32位存储格式。
三、动态范围和精度有什么区别
假设要表示:
0.00000001
1.23456789
100000000
动态范围决定:
能不能表示非常大或非常小的数。
尾数精度决定:
能不能区分两个非常接近的数。
例如 BF16 的指数位与 FP32 一样多,因此不容易因为数值太大而溢出,但它的尾数只有7位,所以:
1.234567
可能只能近似表示成:
1.234375
FP16 尾数更多,在相同数量级下通常更精细,但它的指数位较少,更容易发生:
- Overflow:数值过大,溢出;
- Underflow:数值过小,变成0或次正规数。
因此不能简单说:
BF16 一定比 FP16 准确
更准确的是:
BF16:范围更大
FP16:有限范围内精度更高
四、为什么低精度可以提高性能
低精度的优势来自多个层面。
1. 减少存储空间
同样有十亿个参数:
FP32:约 4 GB
FP16:约 2 GB
INT8:约 1 GB
FP4:约 0.5 GB
这意味着:
- 显存可以放入更大的模型;
- Cache 可以容纳更多数据;
- 数据从显存搬到芯片内所需流量减少。
2. 提高有效带宽
假设显存带宽固定为:
理论上每秒可传输的元素数为:
FP32:约 2500 亿个
FP16:约 5000 亿个
INT8:约 1 万亿个
FP4:约 2 万亿个
位宽减半,理想情况下同样带宽可以传输约两倍元素。
对于带宽受限程序,低精度可能直接提高性能。
3. 一个计算单元可以处理更多数据
低位宽乘法器通常:
- 面积更小;
- 功耗更低;
- 数据通路更窄;
- 更容易在相同芯片面积内布置更多并行计算单元。
可以简化理解为:
一个较大的 FP32 乘法器占用的面积
可能容纳多个更低精度乘法器
具体比例取决于架构,不能简单按位宽线性换算,但总体趋势成立。
4. 寄存器和片上存储利用率提高
假设一个寄存器或数据通路宽度为32位:
可放 1 个 FP32
可放 2 个 FP16
可放 4 个 INT8
可放 8 个 4-bit 数据
硬件可以采用打包方式,在一次读取或一条指令中处理多个低位宽元素。
五、低精度为什么会产生误差
低精度格式能够表示的数值数量更少。
假设真实值是:
而某种格式附近只能表示:
1.125
1.250
那么它可能被舍入为:
产生量化误差:
主要误差包括:
舍入误差
截断误差
溢出
下溢
饱和
多个运算中的误差累积
所以低精度的核心矛盾是:
更高性能、更低存储开销
与
更低的数值表示能力
六、必须区分四种“精度”
讨论 Tensor Core 时,不能只说“使用 FP16”。
需要区分:
输入存储精度
乘法输入精度
累加精度
输出存储精度
例如一种常见的矩阵计算可以是:
A:FP16
B:FP16
乘法:FP16 输入
累加器:FP32
输出:FP16 或 FP32
表示为:
输入较低精度可以提高乘法吞吐率和降低带宽,而使用较高精度累加器可以减少长时间累加导致的误差。
七、为什么累加精度通常要更高
考虑点积:
需要进行1024次乘法和累加。
如果每次累加都舍入为很低的精度:
第1次累加 → 舍入
第2次累加 → 再舍入
第3次累加 → 再舍入
……
误差可能不断积累。
而使用高精度累加器:
低精度输入
↓
完成乘法
↓
在 FP32 累加器中累积
↓
最后再转换为目标格式
可以显著降低重复舍入的影响。
这就是混合精度计算的重要思想:
不需要让所有数据都保持高精度,但应让数值敏感的环节使用更高精度。
CUDA 的 WMMA 接口支持例如 BF16 输入配合 FP32 累加器,TF32 矩阵运算也使用浮点累加器。
八、Tensor Core 的 MMA 是什么
MMA 是:
Matrix Multiply-Accumulate,矩阵乘加。
数学表达式为:
它不是简单执行一个标量:
而是一次处理矩阵小块。
例如概念上:
A Tile × B Tile + C Tile → D Tile
不同架构支持的矩阵形状、数据格式和线程组织不同,因此不能认为所有 Tensor Core 都固定执行某一种大小的矩阵。
九、Warp 如何协作执行 MMA
在经典 WMMA 编程模型中,一个 Warp 共同完成一次矩阵操作。
矩阵小块并不是完整地存放在某一个线程中,而是分散在 Warp 的多个线程中:

图示:九、Warp 如何协作执行 MMA
这些局部数据称为 Fragment。
CUDA 文档明确指出,Fragment 中的矩阵元素分散在整个 Warp 的线程之间,而且具体元素到线程的映射属于架构实现细节。mma_sync 要求参与 Warp 的线程共同到达,然后执行:
所以不应理解成:
一个 Thread 独占一个 Tensor Core
更合理的理解是:

图示:九、Warp 如何协作执行 MMA
十、Tensor Core Kernel 的典型数据流
一个高性能 Tensor Core 矩阵乘法通常为:

图示:十、Tensor Core Kernel 的典型数据流
这里仍然需要前面学过的:
- Tiling;
- 合并访存;
- Shared Memory;
- 双缓冲;
- 异步搬运;
- 寄存器累加。
Tensor Core 只解决“算得快”,并不会自动解决“数据送不进来”的问题。
十一、Tensor Core 和 CUDA Core 的区别
| 对比 | CUDA Core | Tensor Core |
|---|---|---|
| 典型操作 | 标量加法、乘法、FMA | 矩阵块 MMA |
| 数据组织 | 每线程标量或向量 | 线程组协作的矩阵片段 |
| 适用任务 | 通用计算 | 矩阵、卷积、Transformer |
| 支持精度 | FP32、整数等 | 多种混合低精度格式 |
| 灵活性 | 较高 | 对形状和布局要求较高 |
| 峰值吞吐率 | 相对较低 | 矩阵运算吞吐率很高 |
Tensor Core 不是用于替代所有 CUDA Core。
例如以下工作仍需要普通管线:
地址计算
循环控制
分支判断
数据布局变换
特殊函数
索引计算
非矩阵算子
一个 Tensor Core Kernel 通常是多种硬件单元共同工作的结果。
十二、FP16 和 BF16 应该怎么选择
FP16
特点:
指数较少
尾数较多
范围较小
同一数量级下精度较高
适合数值已经得到良好缩放、范围较可控的场景。
训练时可能需要 Loss Scaling,防止较小梯度下溢。
BF16
特点:
指数范围接近 FP32
尾数较少
动态范围大
转换和训练通常更稳定
BF16 可以表示非常大和非常小的数量级,但每个数量级内的数值间隔更粗。
因此很多训练任务偏好 BF16,而某些对局部精度要求更高、范围可控的任务可能适合 FP16。
十三、什么是 Loss Scaling
假设 FP16 无法表示非常小的梯度:
转换成 FP16 后可能变为0。
可以在反向传播前把损失乘以一个较大系数:
梯度也会相应放大:
更新参数前再除以 。
流程为:
原始梯度太小
↓
乘以较大的 Scale
↓
在 FP16 可表示范围内计算
↓
更新前恢复原来的尺度
动态 Loss Scaling 会根据是否出现溢出自动调整缩放系数。
十四、TF32 是什么
TF32 是 Ampere 及后续架构中面向 Tensor Core 的一种计算精度。
它的核心特点是:
指数范围接近 FP32
尾数精度低于 FP32
面向矩阵乘法
因此,程序仍可能以 FP32 形式存放和传递数据,但 Tensor Core 在乘法路径中采用较低尾数精度,再使用较高精度累加。
可以把它理解成:
FP32 的动态范围
+
接近较低精度格式的乘法吞吐优势
但 TF32 不适合要求严格 FP32 逐位结果一致的场景。CUDA 文档将 TF32 定义为 Tensor Core 专用的特殊格式,具有与 FP32 相近的范围和降低的有效精度。
十五、FP8 为什么有两种主要格式
FP8 常见格式包括:
E4M3
E5M2
E4M3
1 个符号位
4 个指数位
3 个尾数位
尾数较多,因此精度相对更好,但动态范围较小。
E5M2
1 个符号位
5 个指数位
2 个尾数位
指数更多,动态范围更大,但精度更低。
传统的 FP8 混合方案中,前向传播常偏向精度更高的 E4M3,而梯度可能需要动态范围更大的 E5M2。
十六、FP8 为什么需要缩放因子
FP8 的表示范围比 FP16、BF16 小得多。
假设一个张量包含:
0.001
0.1
1
100
10000
直接转换为同一种 FP8 格式时:
- 小数可能下溢;
- 大数可能溢出;
- 中间值也可能损失较多精度。
因此先选择缩放因子 :
恢复时:
缩放的目标是让张量中的主要数据尽量落入 FP8 的有效表示范围。
Transformer Engine 可以维护张量的最大绝对值和缩放因子,减少用户手工管理低精度数值范围的负担。
十七、Per-Tensor Scaling 的局限
假设整个张量共享一个缩放因子。
张量的一部分数值在:
另一部分在:
为了避免大数溢出,缩放因子必须适配大数。
结果是小数部分被压缩到很窄的范围,可能大量舍入为0。
少数异常大值
↓
决定整个张量的缩放因子
↓
大量普通小值精度下降
这就是低精度量化中的 Outlier 问题。
十八、Block Scaling 和 Micro-Tensor Scaling
解决办法是把张量划分为多个小块,每一块使用自己的缩放因子。

图示:十八、Block Scaling 和 Micro-Tensor Scaling
这样每个缩放因子只需要适应局部数据范围。
例如 MXFP8 可对连续的小数据块分别缩放,而不是整个张量只使用一个 FP32 缩放因子。NVIDIA 当前文档中的 MXFP8 方案以32个连续元素为一个缩放块。
这种方法的本质是:
增加少量缩放元数据
↓
换取更精细的局部动态范围
↓
减少溢出、下溢和精度损失
十九、Blackwell 的 NVFP4
Blackwell 支持用于 AI 计算的 FP4 路径。
NVFP4 的基本数据元素采用 E2M1:
1 位符号
2 位指数
1 位尾数
仅靠4位数据本身,可表示的范围和精度都非常有限,因此 NVFP4 使用分层缩放:
其中:
- :4位数据;
- :局部块缩放因子;
- :整个张量的全局缩放因子。
当前 Transformer Engine 文档描述的 NVFP4 中,连续16个元素共享一个 E4M3 局部缩放因子,并额外使用一个张量级 FP32 缩放因子。
可以简单理解为:
FP4 数据负责低成本保存主要数值
局部 Scale 适应小范围变化
全局 Scale 适应整个 Tensor 的数量级
二十、Blackwell Transformer Engine 是什么
Transformer Engine 不应简单理解成一个单独的“Transformer 专用 ALU”。
它更像一套硬件和软件协同机制,包括:
支持低精度的 Tensor Core
缩放因子和动态范围管理
低精度矩阵乘法 Kernel
张量格式转换
融合算子
框架和运行库支持
Blackwell 的第二代 Transformer Engine 引入了更细粒度的缩放机制,并支持面向 AI 的 FP4 计算,以提高大模型训练和推理中的计算吞吐率及模型容量。
其核心逻辑仍然是:

图示:二十、Blackwell Transformer Engine 是什么
二十一、INT8 量化与浮点低精度有什么区别
INT8 没有指数位。
它需要通过缩放因子,把浮点数映射到整数区间。
常见对称量化为:
恢复为:
非对称量化还会加入 Zero Point:
其中:
- 是 Scale;
- 是 Zero Point。
二十二、INT8 和 FP8 的直观区别
INT8
在一个固定缩放范围内,量化级别通常均匀分布:
... -3S, -2S, -S, 0, S, 2S, 3S ...
优点:
- 硬件简单;
- 整数乘加效率高;
- 在已校准范围内精度稳定。
缺点:
- 同一个 Scale 难以同时适应很大和很小的数;
- 对动态范围变化敏感。
FP8
浮点数的间隔随数量级变化:
靠近 0:间隔较小
数值变大:间隔也变大
优点:
- 能覆盖更宽的动态范围;
- 更适合数值范围随层和训练阶段变化的场景。
缺点:
- 尾数很少;
- 仍然需要良好的缩放管理。
二十三、Per-Tensor、Per-Channel 和 Per-Block 量化
Per-Tensor
整个张量一个 Scale:
Tensor → 一个 Scale
硬件和存储开销小,但精度可能较差。
Per-Channel
每个输出通道一个 Scale:
Channel 0 → Scale 0
Channel 1 → Scale 1
Channel 2 → Scale 2
常用于神经网络权重量化,因为不同输出通道的权重范围可能差异较大。
Per-Block
每个小数据块一个 Scale:
每16、32或更多元素 → 一个 Scale
精度通常更高,但需要:
- 保存更多 Scale;
- 计算地址和缩放关系;
- 硬件支持相应数据布局。
粒度越细,通常越容易适应局部数据分布,但元数据和控制开销也越高。
二十四、训练和推理的低精度要求不同
推理
模型参数已经固定。
可以提前:
- 统计激活范围;
- 校准 Scale;
- 量化权重;
- 测试精度下降;
- 对异常层保留高精度。
因此 INT8、FP8、FP4 更容易用于推理。
训练
训练过程中:
- 权重持续变化;
- 激活分布变化;
- 梯度范围变化;
- 很小的更新可能影响最终收敛。
所以训练通常需要更复杂的混合精度策略,例如:
低精度执行主要矩阵乘法
高精度保存关键状态
高精度执行归约或参数更新
动态调整缩放因子
二十五、什么是混合精度训练
一种简化的混合精度训练流程是:

图示:二十五、什么是混合精度训练
实际框架中的具体精度组合会因模型、硬件和训练方案而不同。
核心原则是:
把大量规则矩阵计算放到低精度 Tensor Core 上,把数值敏感操作保留在较高精度。
二十六、哪些操作通常更敏感
以下操作往往比普通矩阵乘法更容易受到低精度影响:
Softmax
归一化
方差计算
长距离归约
Loss 计算
很小的梯度更新
概率和指数运算
优化器状态更新
原因包括:
- 需要计算很小的差值;
- 存在大量累加;
- 包含指数和除法;
- 对极值敏感;
- 误差可能被后续操作放大。
因此一个模型使用 FP8 或 FP4,不代表所有操作都以 FP8 或 FP4 执行。
二十七、量化感知训练与训练后量化
PTQ:Post-Training Quantization
训练完成后进行量化:
训练好的浮点模型
↓
校准数据统计范围
↓
转换为 INT8 / FP8 / FP4
优点是实现简单、无需重新完整训练。
缺点是低位宽下精度可能下降明显。
QAT:Quantization-Aware Training
训练时模拟量化过程:
浮点权重
↓
模拟量化和反量化
↓
前向传播感受到量化误差
↓
通过训练适应误差
QAT 通常更适合:
- INT8 以下精度;
- 对精度敏感的模型;
- 激活分布复杂的模型;
- 极低位宽部署。
二十八、低精度为什么不一定真的更快
即使理论吞吐率更高,也可能因为以下原因得不到加速:
矩阵尺寸太小
维度没有良好对齐
需要频繁格式转换
Scale 计算开销较大
数据布局不适合 Tensor Core
Kernel 启动开销占比高
访存或同步成为瓶颈
Tensor Core 供数不足
某些算子仍使用高精度
例如一个很小的矩阵:
8×8 × 8×8
转换、加载和启动开销可能比计算本身还大。
所以低精度峰值算力高,不代表所有程序都能按相同比例加速。
二十九、低精度与 Roofline 的关系
低精度同时可能改变 Roofline 模型中的两个量。
提高峰值计算能力
Tensor Core 对低精度格式通常提供更高吞吐率:
减少数据传输量
数据位宽降低:
因此计算强度:
可能提高。
但是峰值算力提高后,新的 Ridge Point 也会变化:
这意味着 Tensor Core 越快,越需要更高的数据复用,否则更容易受内存系统限制。
三十、从 ASIC 角度看 Tensor Core
一个高度简化的 Tensor Core 可以理解成:

图示:三十、从 ASIC 角度看 Tensor Core
与普通标量 ALU 相比,它的特点是:
- 固定面向矩阵数据流;
- 大量乘法并行;
- 共享输入数据;
- 使用规则的归约结构;
- 部分和保持在累加器中;
- 牺牲通用性换取密度和能效。
从硬件设计角度,这与神经网络 ASIC 中的 MAC Array 或 Systolic Array 有相似之处,但 NVIDIA 并未公开所有内部实现细节,不能把 Tensor Core 简单等同于某一种固定脉动阵列。
本课核心结论
第一,浮点数的指数决定动态范围,尾数决定有效精度。
第二,FP16 尾数更多但范围较小,BF16 范围接近 FP32 但尾数更少。
第三,低精度可以减少存储、降低带宽需求,并提高矩阵计算吞吐率。
第四,低精度会产生舍入、溢出、下溢和误差累积问题。
第五,必须区分输入精度、乘法精度、累加精度和输出精度。
第六,低精度输入配合较高精度累加,是混合精度计算的核心。
第七,Tensor Core 执行矩阵乘加:
第八,经典 WMMA 模型由一个 Warp 共同持有 Fragment 并执行矩阵运算,而不是一个线程独占 Tensor Core。
第九,FP8 E4M3 更偏向精度,E5M2 更偏向动态范围。
第十,FP8、FP4 通常需要缩放因子来管理数值范围。
第十一,Per-Block 或 Micro-Tensor Scaling 可以降低异常值对整个张量的影响。
第十二,Blackwell 的 NVFP4 采用4位 E2M1 数据,并配合局部和全局两级缩放。
第十三,Transformer Engine 是 Tensor Core、低精度格式、动态范围管理和软件 Kernel 的协同系统。
第十四,INT8 使用固定缩放映射,FP8 使用浮点指数并通常配合缩放。
第十五,低精度不一定自动加速,仍需满足矩阵尺寸、数据布局和数据供应要求。
下一课将学习:
第十一课:GPU 的缓存、显存控制器与内存事务
主要包括:
显存请求如何从 Warp 到达 HBM/GDDR
Cache Line 和 Memory Transaction
L1、L2 Cache 如何组织
显存分区与内存控制器
地址交织
为什么对齐很重要
为什么跨 Cache Line 会增加事务
HBM 和 GDDR 的结构差异
ECC 和显存容量开销