在 GPU/CPU 性能分析与 AI 算子优化中,Arithmetic Intensity(算术强度)与Roofline Model(屋顶图模型)是用来定量评估算子瓶颈(瓶颈究竟在计算能力还是在内存带宽)的最核心理论工具。
1. 算术强度 (Arithmetic Intensity)
算术强度(又称计算密度,单位为FLOP/Byte)用来衡量一个算法/算子单位内存访存所做的数据计算量。
计算公式:
Arithmetic Intensity (I)=总浮点计算量 (FLOPs)总内存访存量 (Bytes)\text{Arithmetic Intensity } (I) = \frac{\text{总浮点计算量 (FLOPs)}}{\text{总内存访存量 (Bytes)}}Arithmetic Intensity(I)=总内存访存量(Bytes)总浮点计算量(FLOPs)
- FLOPs (Floating-Point Operations):算法执行的总浮点运算次数(加、乘、FMA 等)。
- Bytes:算法从全局内存(Global Memory / DRAM)读取和写入的总数据字节数。
示例:
矢量加法C=A+BC = A + BC=A+B(长度NNN,FP16 数据):
计算量:NNN次加法→N\rightarrow N→NFLOPs
访存量:读A,BA, BA,B(各2N2N2N字节),写CCC(2N2N2N字节)→6N\rightarrow 6N→6NBytes
算术强度I=N6N≈0.167 FLOP/ByteI = \frac{N}{6N} \approx 0.167 \text{ FLOP/Byte}I=6NN≈0.167FLOP/Byte(极低,严重访存受限)。
大矩阵乘法C=A⋅BC = A \cdot BC=A⋅B(N×NN \times NN×N矩阵,FP16 数据):
计算量:2N32N^32N3FLOPs
访存量(假设无复用):3N2×2=6N23N^2 \times 2 = 6N^23N2×2=6N2Bytes
算术强度I=2N36N2=N3 FLOP/ByteI = \frac{2N^3}{6N^2} = \frac{N}{3} \text{ FLOP/Byte}I=6N22N3=3NFLOP/Byte(随着NNN增大而变高,高计算密度)。
2. Roofline Model(屋顶图模型)
Roofline Model 是由 UC Berkeley 提出的一种直观的可视化性能分析模型。它把硬件的峰值算力和峰值带宽画在一张双对数坐标图上,形成一个形如“屋顶”的上限曲线。
计算性能 (TFLOP/s) ^ | Roofline (上限) | +---------------------+ <-- 硬件理论峰值算力 (Compute Bound) | / | / | / | / <-- 内存带宽上限 (Memory Bound) | / | / +-----------+-----------------------------> 算术强度 (FLOP/Byte) ^ 临界拐点 (I_knee)屋顶图的上限公式:
Achieved Performance (TFLOP/s)=min(Peak Compute Performance,Peak Memory Bandwidth×I)\text{Achieved Performance (TFLOP/s)} = \min\left(\text{Peak Compute Performance}, \text{Peak Memory Bandwidth} \times I\right)Achieved Performance (TFLOP/s)=min(Peak Compute Performance,Peak Memory Bandwidth×I)
核心三要素:
- 访存受限区(Memory-Bound Region,斜线部分):
- 当算子的算术强度I<IkneeI < I_{\text{knee}}I<Iknee时,性能上限由内存带宽决定。
- 此时硬件的计算单元(Tensor Core / CUDA Core)大部分时间在等数据,算力被严重浪费。
- 计算受限区(Compute-Bound Region,平线部分):
- 当算子的算术强度I>IkneeI > I_{\text{knee}}I>Iknee时,性能上限由硬件峰值算力决定。
- 此时片上缓存复用良好,内存带宽不再是瓶颈,硬件计算资源被跑满。
- 临界拐点(Knee Point,IkneeI_{\text{knee}}Iknee):
- 区分算子是 Memory-Bound 还是 Compute-Bound 的硬件物理临界值:
Iknee=硬件峰值算力 (TFLOP/s)硬件峰值内存带宽 (TB/s)I_{\text{knee}} = \frac{\text{硬件峰值算力 (TFLOP/s)}}{\text{硬件峰值内存带宽 (TB/s)}}Iknee=硬件峰值内存带宽(TB/s)硬件峰值算力(TFLOP/s)
硬件实例(NVIDIA A100 SXM 80GB FP16 Tensor Core):
- 峰值算力≈312 TFLOP/s\approx 312 \text{ TFLOP/s}≈312TFLOP/s
- 峰值 HBM 带宽≈2.0 TB/s\approx 2.0 \text{ TB/s}≈2.0TB/s
- 临界拐点Iknee=3122.0=156 FLOP/ByteI_{\text{knee}} = \frac{312}{2.0} = 156 \text{ FLOP/Byte}Iknee=2.0312=156FLOP/Byte
- 结论:在 A100 上,一个 FP16 算子每读取 1 字节数据,必须执行至少156 次浮点计算,才能把 GPU 的 Tensor Core 算力跑满!
3. AI 算子分类与优化指导
根据 Roofline 模型,大模型与深度学习中的常见算子可以分为两大类,对应的优化策略截然不同:
| 算子类型 | 典型代表 | 算术强度特征 | 瓶颈所在 | 核心优化方向 |
|---|---|---|---|---|
| Memory-Bound(访存受限) | Elementwise, Softmax, LayerNorm/RMSNorm, LLMDecode 阶段Attention | I≪IkneeI \ll I_{\text{knee}}I≪Iknee(极低) | 全局内存 (HBM/DRAM) 带宽 | 1.算子融合 (Kernel Fusion):减少 Global Memory 读写; |
2.量化 (INT8/FP4):减少读取字节数;
3.FlashAttention:避免写入中间N×NN \times NN×N注意力矩阵 |
|Compute-Bound(计算受限) | 大 GEMM, Conv2D, LLMPrefill 阶段Attention |I≫IkneeI \gg I_{\text{knee}}I≫Iknee(高) | Tensor Core / ALU 算力 | 1.Tiling (分块复用):充分利用 Shared Memory / Register / TMEM;
2.指令对齐:使用 Tensor Core MMA 原语;
3.流水线重叠:使用cp.async/ TMA 隐藏剩余延迟 |
总结
- Arithmetic Intensity是算子自身的属性(取决于算法逻辑与输入 Shape)。
- Roofline Model将算子属性与硬件物理极限相结合,直接回答了:“这个算子性能跑不上高位,到底是因为读写显存太慢,还是算得不够快?”
- 在进行算子优化(如使用 TileLang 或 CUDA)前,先绘制或推算 Roofline 指标,能够精准指引优化方向,避免做无效工作。