性能优化,量化意识为王。
阅读大纲
目标:系统掌握 CUDA 优化知识,服务于 tiled 矩阵乘 + FlashAttention 实现
策略:读一层、做一层,交替进行,不要先读完再动手
Layer 1:GPU 执行心智模型 ✅
章节:Ch3 --- Heterogeneous Computing
核心问题
- GPU 为什么需要数万个线程?
- 什么代码适合放 GPU?
- 延迟隐藏(Latency Hiding)是什么,依赖什么条件?
Layer 2:学会用数字衡量性能
章节:Ch9 --- Performance Metrics
核心问题
- 如何计算 Theoretical Bandwidth(理论带宽上限)?
- 如何计算 Effective Bandwidth(kernel 实际使用的带宽)?
- 两者之比说明了什么?
动手:对 naive 矩阵乘 kernel 测量实际带宽,和理论上限对比
Layer 3:内存优化(最重要)
章节:Ch10 --- Memory Optimizations
阅读顺序
Host ↔ Device 数据传输(PCIe 瓶颈)
↓
Global Memory Coalescing(合并访问) ← 重点
↓
Shared Memory + Tiling ← 重点,FlashAttention 核心
↓
L1/L2 Cache、Texture Memory ← 了解即可
动手
- 实现 naive 矩阵乘,观察 global memory 访问模式
- 实现 tiled 矩阵乘,对比带宽和性能
- 用 Ch9 的方法量化优化效果
Layer 4:线程执行模型
章节:Ch6-8(Execution Configuration)+ Ch13 --- Control Flow
核心问题
- Warp 是什么?为什么是 32 个线程?
- Branch Divergence 如何导致性能损失?
- Occupancy 是什么,如何影响延迟隐藏?
- 如何配置 block/grid 大小让 SM 充分利用?
动手:为 softmax 实现做准备,理解 warp 内 reduction 的写法
Layer 5:指令级优化(按需)
章节:Ch12 --- Instruction Optimization
内容
- 浮点精度与性能权衡
- 快速数学函数(
__float2int、-use_fast_math) - 编译器 flag
有具体性能瓶颈时再读,不需要提前啃。
暂时跳过
| 章节 | 原因 |
|---|---|
| Ch4 Application Profiling | 有具体项目再看 |
| Ch16 多 GPU | 先打好单 GPU 基础 |
| Ch17-19 数字精度 | 按需查阅 |
| Ch20 编译器选项 | 参考手册,用到再看 |
与 FlashAttention 实现的对应关系
| FlashAttention 组件 | 依赖的 CUDA 知识 | 对应章节 |
|---|---|---|
| Tiling(分块计算) | Shared Memory 使用 | Ch10 |
| 高效矩阵乘 | Coalescing + Tiling | Ch10 |
| Online Softmax | Warp Reduction | Ch13 |
| 避免 HBM 读写 | 内存层次理解 | Ch9 + Ch10 |
| 线程配置 | Occupancy 调优 | Ch6-8 |
Layer 1: GPU 执行心智模型(Ch3)
核心结论
CPU 设计目标:最小化延迟 (少数线程,跑得快)
GPU 设计目标:最大化吞吐量(海量线程,用并发掩盖延迟)
关键机制:Latency Hiding(延迟隐藏)
GPU 有成千上万的线程,分成若干 warp(每个 warp 32 个线程)。
当某个 warp 等待内存数据时(内存延迟约 400-800 个时钟周期),GPU 立刻切换到其他就绪的活跃 warp 继续执行,等数据回来再切回来。
为什么切换没有代价?
每个线程有独立分配的寄存器,切换时不需要保存/恢复任何状态,直接换。
延迟隐藏失效的条件:
活跃 warp 数量不足------当所有 warp 都在等内存时,没有就绪的 warp 可以切换,GPU 空转。
运算/传输比(Operation-to-Transfer Ratio)
判断一个计算是否值得放在 GPU 上,核心指标是:
ratio=运算次数传输元素数\text{ratio} = \frac{\text{运算次数}}{\text{传输元素数}}ratio=传输元素数运算次数
比值越高,GPU 越划算(传输代价被大量计算摊薄)。
矩阵加法(N×N)
| 项目 | 数量 |
|---|---|
| 运算次数 | N²(每个元素一次加法) |
| 传输元素数 | 3N²(A、B、C 各一个矩阵) |
| 比值 | O(1) |
结论:不值得放 GPU。 传输代价无法被计算收益覆盖。
矩阵乘法(N×N)
| 项目 | 数量 |
|---|---|
| 运算次数 | N³(N² 个输出元素,每个需要 N 次 multiply-add) |
| 传输元素数 | 3N²(A、B、C 各一个矩阵) |
| 比值 | O(N) |
结论:值得放 GPU,且 N 越大收益越高。
什么代码适合放 GPU?
- 运算/传输比高:大量计算,少量数据搬运
- 可大量并行:数万个线程同时做独立计算
- 数据尽量留在 GPU 上:哪怕某一步在 CPU 更快,只要能避免一次 Host-Device 传输,在 GPU 上算也值得
- 相邻线程访问相邻内存:为 coalescing 做准备(详见 Ch10)
与 FlashAttention 的联系
FlashAttention 的核心问题就是第一层的直接应用:
- N×N attention 矩阵(N=1024 时约 4MB)远超 shared memory 容量(48-96KB)
- 标准 softmax 需要看完整一行才能归一化 → 必须把完整矩阵写回 HBM(global memory)
- Online softmax 允许分块计算(数学上消除全局依赖)+ Tiling(物理上适配 shared memory)
- 两者结合,才能把中间结果留在 shared memory,避免反复读写 HBM