计算优化
计算优化核心原则
- 辩证看待占用率:高占用率不等于高性能
- 最大化指令级并行:隐藏延迟,保持计算单元忙碌
- 消除线程束分化:避免线程束内分支发散
- 利用线程束专用化(specialization):流水线并行优化
占用率优化
占用率定义
GPU 上的占用率指的是一个流多处理器(Multiprocessor)的活跃线程束数量与该流多处理器最大支持的线程束数量之比:
其中:
- 活跃线程束:流多处理器上同时被调度执行的线程束
- 流多处理器最大线程束数量:由线程束调度器决定(硬件固定)
- 活跃线程束数量:由内核硬件资源使用量限制
影响占用率的资源限制
| 资源类型 | 说明 | 影响 |
|---|---|---|
| 寄存器数量 | 每线程分配的寄存器数 | 寄存器过多 → 占用率下降 |
| 共享内存 | 每线程块使用的共享内存 | 共享内存过大 → 占用率下降 |
高占用率的优势
高占用率带来的性能优势:
- 确保总有足够的活跃线程束切换,隐藏长延迟操作
- 提供足够高的全局内存访问并发数
- 提高内存带宽利用率
- 确保每个流多处理器上有足够多线程,平衡负载
辩证看待占用率
并不是所有应用都需要高占用率!
结合屋顶模型(Roofline Model)分析:
| 应用类型 | 特征 | 占用率策略 | 理由 |
|---|---|---|---|
| 内存瓶颈 | 计算/访存比低 | 高占用率 | 需要足够高的访存并发数,将延迟瓶颈转化为带宽瓶颈 |
| 计算瓶颈 | 计算/访存比高 | 低占用率也可 | 充分的指令级并行带来高计算单元利用率,追求高占用率反而降低性能 |
案例分析:
// 计算瓶颈场景:矩阵乘法
// 高占用率策略(错误)
__shared__ float tileA[8][8]; // 小分块
__shared__ float tileB[8][8];
// 结果:计算/访存比降低,性能变差
// 低占用率策略(正确)
__shared__ float tileA[32][32]; // 大分块
__shared__ float tileB[32][32];
// 结果:计算/访存比高,计算单元利用率高,性能好
占用率调优
# 使用 Moore Perf Compute 分析占用率
mcu --metrics occupancy,sm_throughput -o report ./application
# 关键指标
# - sm__average_warp_execution_efficiency: 线程束有效执行比例
# - sm__active_warps.pct: 流多处理器上活跃线程束比例
消除线程束分化
线程束分化原理
在 MT GPU 中:
- 一个线程束包含 32 个线程
- 线程束共享同一个程序计数器(PC,Program Counter)
- 单指令多线程(SIMT)模型允许线程执行不同路径
- 线程束分化:线程束内线程执行不同分支路径
线程束分化的代价:
- GPU 串行执行各分支路径
- 为每个线程设置活跃掩码
- 非活跃线程的计算单元闲置
- 总执行时间 ≈ 各分支时间之和