
CS336 Lecture 6 讲义截图。传统结论:FP32 → FP16,Arithmetic Intensity 翻倍,工作负载更 compute-bound。
1. 背景
想起之前的cs336还没看完,起来复习的时候看到了这个地方,但总觉得哪里不对。课程中计算强度的定义是:
AIstd=Bytes movedFLOPs
含义:每从内存搬运 1 byte 数据,可以执行多少次抽象浮点运算。
例如矩阵乘 C=AB,其中 A∈RM×K,B∈RK×N,C∈RM×N。传统 FLOP count 近似为 2MKN。
若数据类型从 FP32 换成 FP16,同一个矩阵乘的 FLOP count 不变,但每个元素从 32 bit 变成 16 bit,数据搬运量约减半:
AIstd,fp16≈2AIstd,fp32
这就是开头那页 slide 的结论:低精度提高 arithmetic intensity,更容易跨越 memory-bound 的限制。
但这个结论依赖一个抽象假设:一次 FP16 浮点操作和一次 FP32 浮点操作都被记为同一个 FLOP。
如果我们关心的是更底层的电路代价、能耗、面积和物理计算量,这个抽象就会泄漏。FP16 加法 ≠ FP32 加法,FP16 乘法 ≠ FP32 乘法——它们在门电路规模、晶体管翻转、电容充放电和可并行数量上都不同。
FLOP count 是算法层面的操作计数,不是物理层面的计算代价。
2. 标准 FLOP/Byte 指标的抽象假设
传统 AI_std 隐含地把一次浮点加法、一次浮点乘法或一次 FMA 看作同等单位的”操作”。因此对同一个 GEMM C=AB,无论 FP32 还是 FP16,FLOP count 都是 2MKN。变化只发生在分母的搬运量上:
AIstd,fp32AIstd,fp16=Bfp16Bfp32≈2
这个结论在 FLOP count 的抽象层面是正确的。但它忽略了一个事实:FP16 的单次运算本身比 FP32 便宜。
3. 电路级计算量模型
引入一个粗略但有解释力的 bit-level compute model。
设 b 为浮点数总 bit 宽度,p 为有效尾数位数:
- FP32:sign 1 bit,exponent 8 bit,fraction 23 bit → p32=24(含 hidden leading bit)
- FP16:sign 1 bit,exponent 5 bit,fraction 10 bit → p16=11
加法
p-bit 加法器的代价近似线性增长:Cadd(p)∼O(p)。浮点加法还需要 exponent compare、mantissa alignment、shift、normalize、rounding,但主导直觉仍是位宽越大代价越高。
乘法
p-bit 乘法器需要生成和规约大量 partial products,代价近似按二次增长:Cmul(p)∼O(p2)。这是最关键的一点——低精度乘法的电路代价下降得比数据位宽更快。
FMA
对 a×b+c,使用简化模型:
CFMA(p)≈p2+p
p2 对应乘法部分,p 对应加法/累加部分。这个模型不是精确的硬件能耗模型,但足以表达电路复杂度的缩放规律。
4. 新指标:Bit-Circuit Intensity
定义:
Icircuit=BmovedCcircuit
其中 Ccircuit 是电路级计算工作量(gate-equivalent work、partial-product work),Bmoved 是传输的 bit 数。
比较 FP16 和 FP32:
I32I16=C32C16⋅B16B32
所有数据从 FP32 变为 FP16 时,B32/B16≈2。所以关键在于 C16/C32。
传统 FLOP/Byte 视角等价于假设 C16=C32,所以得到 2×。但电路级视角下 C16 远小于 C32,尤其对乘法/FMA。
5. 加法主导 workload
若只考虑简单加法,用 bit 宽度近似(C∼b):
Cadd,32Cadd,16=3216=21
Iadd,32Iadd,16≈2⋅21=1
加法类操作的 Icircuit 在 FP16 和 FP32 下大致持平,而不是传统视角的 2×。
若用 significand 位数 p 近似(C∼p):
C32C16=2411≈0.458,I32I16≈2⋅0.458≈0.917
两者都不到 2×。
6. 乘法主导 workload
乘法是更重要的情况。若用 significand 位数估计:
Cmul(p)∼p2
Cmul,32Cmul,16=(2411)2=576121≈0.210
乘以传输减半的因子:
Imul,32Imul,16≈2⋅0.210=0.420
在乘法主导的 workload 中:
Icircuit,fp16≈0.42Icircuit,fp32
这和传统结论方向相反。传统视角说 2×,电路级视角说 0.42×。差异来自一个事实:FP16 乘法器面积约为 FP32 乘法器的四分之一。
7. FMA / GEMM 主导 workload
深度学习中更典型的是 FMA。代入 CFMA(p)≈p2+p:
CFMA,32≈242+24=600
CFMA,16≈112+11=132
CFMA,32CFMA,16=600132=0.22
乘以传输减半:
IFMA,32IFMA,16≈2⋅0.22=0.44
因此对于 FMA/GEMM 主导的 workload:
Icircuit,fp16≈0.44Icircuit,fp32
按电路级计算量/传输 bit 定义 intensity,FP16 不是提高 intensity,而是降低 intensity。
8. 矩阵乘中的显式推导
A∈RM×K,B∈RK×N,每个乘加电路工作量 CFMA(p)=p2+p:
Icircuit(b,p)=b(MK+KN+MN)MKN⋅CFMA(p)
FP32:
Icircuit,32=32(MK+KN+MN)MKN⋅600
FP16:
Icircuit,16=16(MK+KN+MN)MKN⋅132
相除:
Icircuit,32Icircuit,16=600/32132/16=18.758.25=0.44
与 §7 的结论一致。
9. Mixed Precision 的情况
现实硬件中常见的不是纯 FP16 FMA,而是 FP16/BF16 multiply + FP32 accumulate:
Cmixed≈p162+p32=112+24=145
相比 FP32 FMA 的 600:
CFMA,32Cmixed=600145≈0.242
若 A、B 为 FP16,C 为 FP32,square GEMM(M=N=K=n):
Bmixed=16n2+16n2+32n2=64n2,B32=96n2
BmixedB32=6496=1.5
I32Imixed≈1.5⋅0.242=0.363
Mixed precision 下的 Icircuit 约在 0.35∼0.50 之间。
10. 实测验证
实验环境:RTX 5060 Laptop GPU(Blackwell),CUDA 12.8,PyTorch cu128。HBM 带宽实测 267.3 GB/s。
峰值吞吐
| dtype | Peak TFLOP/s |
|---|
| FP32 | 9.71 |
| FP16 | 25.00 |
| BF16 | 35.07 |
FP16 峰值是 FP32 的 2.58 倍,BF16 是 3.61 倍。
Compute Utilization:决定性指标
ρ 告诉你工作负载离 ridge 多远。但还有一个更直接的指标:计算单元利用率。
Ud(n)=峰值 TFLOP/sd实测 TFLOP/sd(n)
U 直接衡量:你的 kernel 用到了硬件峰值算力的百分之几。
在不同矩阵大小上测 U:
| n | AI_std | UFP32 | UFP16 | UBF16 | UFP16/UFP32 |
|---|
| 128 | 21 | 8.0% | 3.9% | 2.7% | 0.49 |
| 256 | 43 | 34.1% | 27.8% | 19.0% | 0.81 |
| 512 | 85 | 79.1% | 63.5% | 43.0% | 0.80 |
| 1024 | 171 | 97.4% | 88.8% | 64.7% | 0.91 |
| 2048 | 341 | 99.9% | 105.6% | 91.8% | 1.06 |
| 4096 | 683 | 87.5% | 122.3% | 99.6% | 1.40 |
(n ≥ 2048 时测量噪声较大,两端基本都接近饱和。重点看 n = 256/512/1024。)
在 n = 512 这个关键尺寸上:FP32 用到了 79% 的峰值算力,FP16 只用到 63%。差距 16 个百分点。
在 roofline 上,n=512 时 AI_FP32 = 85 > ridge_32 = 36,AI_FP16 = 171 > ridge_16 = 94。按传统模型,两者都应该接近饱和。但实际上 FP16 差了 16 个百分点。
为什么?因为ridge 右移得比 AI 远。FP32 的 AI 是 ridge 的 2.4 倍,FP16 只有 1.8 倍。margin 越小,越难饱和。
这个 gap 对任何 rP>2 的 GPU 都成立。对于 memory-bound 的 kernel,理论下限是:
UFP32UFP16=rP2
本卡 rP=2.58,理论下限 = 0.78。实测 n=256 时比值为 0.81——接近下限。
结论:对于任何未完全饱和的 kernel size,FP16 的计算利用率始终低于 FP32。低精度没有帮你跨越 memory wall——它只是让你的计算单元更闲。
跨 GPU 代际
| GPU | 代 | rP=PFP16/PFP32 | UFP16/UFP32 下限 |
|---|
| V100 | Volta | 8.0 | 0.25 |
| A100 | Ampere | 16.0 | 0.12 |
| H100 | Hopper | 14.8 | 0.14 |
| RTX 4090 | Ada | 4.0 | 0.50 |
| RTX 5060 | Blackwell | 2.58 | 0.78(实测 0.81) |
Tensor Core 越强的卡,FP16 的计算利用率越低。A100 在 FP16 下的利用率最多只有 FP32 的 12%——不是因为它慢,是因为它太快了,memory 根本喂不饱。
11. 与 memory-bound 的关系
传统 roofline:
Pattainable=min(Ppeak,AIstd⋅Bmem)
FP32 → FP16,两个东西同时变化:
- AIstd ↑(bytes moved 下降)
- Ppeak ↑↑(FP16 计算单元更小、更密集,可用 Tensor Core)
关键:Ppeak 涨得比 AIstd 快。
定义 ρ=AI/R=AI⋅Bmem/Ppeak:
ρFP32ρFP16=rP2,rP=PFP32PFP16
当 rP>2 时,ρ 必然下降,U 也必然下降。
12. 总结
传统 FLOP/Byte 指标下:AIstd,fp16≈2AIstd,fp32。因为 FLOP count 不变,数据搬运量减半。
电路级 Icircuit 指标下,结果完全不同:
| Workload | Icircuit,fp16/Icircuit,fp32 |
|---|
| 加法主导 | ∼0.9∼1.0 |
| 乘法主导 | ∼0.42 |
| FMA/GEMM | ∼0.44 |
| Mixed Precision GEMM | ∼0.35∼0.50 |
两者的区别:
- FLOP/Byte 视角:低精度让数据更小,所以 intensity 增大
- Circuit/Bit 视角:低精度让计算更便宜,而且下降更快,所以 intensity 减小
低精度的系统效果不是简单地跨越 memory-bound,而是:
- 减少数据搬运
- 降低单次算术操作的电路代价
- 提高硬件峰值吞吐
- 使计算更便宜,从而让数据供给、locality、memory hierarchy 更关键
Icircuit=transferred bitsbit-level compute work
实验代码
roofline_dtype_experiment.ipynb — RTX 5060 完整可复现实验