Skip to content
Gains Summary
Main Navigation 首页 / Home
C++ 编程 / C++ Programming
系统与高性能 / Systems & Performance
Web 开发 / Web Development
人工智能 / Artificial Intelligence
工业软件 / Industrial Software
其他内容 / Other Topics
C++ 编程 / C++系统与性能 / SystemsWeb 开发 / Web人工智能 / AI工业软件 / Industrial

外观

Sidebar Navigation

← 系统与高性能 / Systems & Performance

性能工程 / Performance Engineering

1. 性能工程全景:从现象到证据 / Performance Engineering Overview

2. 基准测试:把变快变成可信实验 / Benchmarking as Evidence

3. 系统级追踪:用时间线看等待、调度与 I/O / System Tracing

4. CPU、内存、Cache 与 NUMA:把热点解释到硬件行为 / CPU, Memory, Cache and NUMA

5. GPU、Roofline 与异构瓶颈 / GPU, Roofline and Heterogeneous Bottlenecks

6. 优化闭环:从假设、改动到回归守护 / Optimization, Regression and Production

本页目录

GPU、Roofline 与异构瓶颈 / GPU, Roofline and Heterogeneous Bottlenecks ​

GPU 性能分析最容易走偏。

很多人一看到 CUDA Kernel,就直接打开 Nsight Compute 研究 Occupancy、寄存器、Shared Memory 和 Warp Stall。可如果端到端时间主要花在 CPU 准备、数据传输或同步等待,单个 Kernel 再优化也救不了整条流水线。

所以 GPU 性能要分两层看:

text
端到端异构流水线
  -> CPU、GPU、拷贝、同步、队列是否顺畅

单个 GPU Kernel
  -> 访存、计算、并发、指令和资源是否高效
1
2
3
4
5

1. 先看流水线,再看 Kernel ​

一个 GPU 项目通常不是只有 Kernel。

text
CPU 准备输入
  |
  v
Host -> Device 拷贝
  |
  v
Kernel A
  |
  v
Kernel B
  |
  v
Device -> Host 拷贝
  |
  v
CPU 后处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

慢可能发生在任何位置。

Nsight Systems 用来看整条时间线。

Nsight Compute 用来看单个 Kernel。

顺序通常是:

text
Nsight Systems
  |
  +-- GPU 是否空闲?
  +-- 拷贝和计算是否重叠?
  +-- Kernel 之间是否有空洞?
  +-- CPU 是否在等待 GPU?
  +-- GPU 是否在等待 CPU 提交?
  |
  v
选出值得深入的 Kernel
  |
  v
Nsight Compute
  |
  +-- 内存访问
  +-- Occupancy
  +-- Warp Stall
  +-- 吞吐上限
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

如果 Systems 显示 GPU 大量空闲,Compute 的局部优化优先级就很低。

2. Roofline 的直觉 ​

Roofline 是一个性能上限模型。

它把程序放在“计算能力”和“内存带宽”两个屋顶下面。

核心概念是 Arithmetic Intensity,算术强度。

text
Arithmetic Intensity = 计算量 / 数据搬运量
1

例如每读写很多字节只做一次加法,算术强度低。

例如矩阵乘法中每个数据被重复参与大量乘加,算术强度高。

简化图:

text
Performance
^
|                         compute roof
|-------------------------------
|                       /
|                     /
|                   /
|                 /
| memory roof   /
+----------------------------------> Arithmetic Intensity
1
2
3
4
5
6
7
8
9
10

左侧受内存带宽限制。

右侧受计算峰值限制。

Roofline 的意义不是精确预测每毫秒,而是阻止错误努力。

如果一个 Kernel 明显受内存带宽限制,你继续手写更多计算指令通常没用。应该先减少数据搬运、改善合并访问、复用数据或改变算法。

3. GPU 时间线常见瓶颈 ​

GPU 空洞 ​

GPU 时间线上有大段空白,说明 GPU 没有工作。

原因可能是:

  • CPU 准备数据太慢;
  • Kernel 启动太频繁且粒度太小;
  • 同步点过多;
  • Stream 没有并发;
  • 数据传输阻塞;
  • 依赖关系串行;
  • 内存分配发生在热路径;
  • 任务调度器没有及时提交。

拷贝和计算没有重叠 ​

Host 和 Device 之间传输数据可能占很大时间。

如果每次都同步拷贝再执行 Kernel,再同步拷贝回来,GPU 会经常等待。

优化方向包括 pinned memory、异步拷贝、多 Stream、批处理和减少 Host/Device 往返。

频繁同步 ​

同步会切断流水线。

常见同步来源:

  • cudaDeviceSynchronize();
  • 同步内存拷贝;
  • 默认 Stream 的隐式依赖;
  • 读取 Device 结果到 Host;
  • 错误检查写法过于粗;
  • 调试或日志路径。

同步不是不能用,而是要知道它放在哪里、阻断了什么。

4. Nsight Systems 的使用方式 ​

Nsight Systems 的目标是建立端到端图景。

你可以先加 NVTX 标记业务阶段。

cpp
nvtxRangePushA("build input");
buildInput();
nvtxRangePop();

nvtxRangePushA("solve kernels");
launchKernels();
nvtxRangePop();
1
2
3
4
5
6
7

这样时间线里不只有 CUDA API 和线程号,还能看到项目语义。

常见观察顺序:

text
1. 先看总时间和业务阶段
2. 看 CPU 线程是否有长等待
3. 看 CUDA API 调用是否密集
4. 看 GPU 队列是否有空洞
5. 看 memcpy 和 kernel 是否重叠
6. 看同步点是否切断并发
7. 选出一个真正影响端到端的 kernel
1
2
3
4
5
6
7

命令形态可以是:

bash
nsys profile -t cuda,nvtx,osrt -o report ./app
nsys stats report.nsys-rep
1
2

cuda 记录 CUDA API 和 GPU 工作。

nvtx 记录应用标记。

osrt 记录部分操作系统运行库事件,例如锁和 I/O 调用。

5. Nsight Compute 的使用方式 ​

Nsight Compute 针对单个 Kernel。

它回答:

  • 一个 Kernel 的 DRAM/L2/Shared Memory 行为如何;
  • Warp 为什么停顿;
  • Occupancy 受寄存器、Shared Memory 还是线程块限制;
  • 指令吞吐是否接近硬件上限;
  • 内存访问是否合并;
  • 分支是否导致发散;
  • 每个 Source Line 或 SASS 指令的成本在哪里。

命令形态:

bash
ncu --set full ./app
ncu --kernel-name my_kernel ./app
1
2

不要对整个复杂程序无脑 --set full。

它可能很慢,数据也很多。通常先用 Nsight Systems 找到目标 Kernel,再让 Nsight Compute 只采这个 Kernel。

6. Occupancy 不是最终目标 ​

Occupancy 描述 GPU 上可驻留 Warp 的比例。

它低时可能表示寄存器、Shared Memory 或线程块配置限制并发。

但 Occupancy 高不等于性能好。

如果 Kernel 受内存带宽限制,高 Occupancy 可能只是让更多 Warp 一起等内存。

如果 Kernel 指令依赖很强,增加 Occupancy 可能能隐藏延迟。

如果 Kernel 每个线程寄存器很多,强行降低寄存器使用可能导致 spilling,把数据溢出到本地内存,反而变慢。

所以 Occupancy 只能作为解释指标,不能作为目标指标。

7. GPU 访存问题 ​

GPU 访存常见问题:

  • 全局内存访问不合并;
  • 访问 stride 太大;
  • 读写重复;
  • Shared Memory bank conflict;
  • 寄存器 spilling;
  • Host/Device 频繁往返;
  • Unified Memory 迁移页;
  • 数据布局不适合线程组织;
  • 原子操作竞争。

常见优化方向:

  • 改善线程到数据的映射;
  • 使用 SoA 而不是 AoS;
  • 分块和 Shared Memory 复用;
  • 减少全局内存写;
  • 合并多个小 Kernel;
  • 使用异步拷贝和流水线;
  • 降低原子热点;
  • 把 CPU 后处理移到 GPU 或反过来减少往返。

8. CPU/GPU 共同瓶颈 ​

异构程序常见问题不是某一个设备慢,而是协作方式慢。

text
CPU: [prepare][wait gpu....][prepare][wait gpu....]
GPU: [idle...][kernel][idle...][kernel]
1
2

这说明 CPU 和 GPU 没有形成流水线。

可能的优化方向:

  • 把任务批量化;
  • 减少同步;
  • 使用多个 Stream;
  • 用双缓冲隐藏拷贝;
  • 让 CPU 准备下一批数据;
  • 延迟读取结果;
  • 把小 Kernel 合并;
  • 把部分轻量任务留在 CPU,避免传输开销。

但要用端到端指标判断。GPU 利用率提高,如果总延迟变差,仍然不是成功。

9. Roofline 报告模板 ​

text
目标:
  pressure_update kernel 占端到端 31%,需要判断是否值得优化。

Systems 证据:
  GPU 在求解阶段利用率高,CPU 准备不是主瓶颈。

Compute 证据:
  DRAM 吞吐接近硬件可达带宽;
  每个元素执行少量计算;
  L2 命中率低,访问模式为 stride。

模型:
  算术强度低,属于 memory-bound。

实验:
  调整数据布局,让相邻线程读取连续地址。

预期:
  Global load efficiency 上升,DRAM 字节数下降或有效吞吐上升。

成功标准:
  Kernel 时间下降,并且端到端求解时间下降。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

10. 本篇总结 ​

GPU 性能要先看异构流水线,再看单个 Kernel。

Nsight Systems 帮你判断 CPU、GPU、拷贝和同步之间的时间关系。

Nsight Compute 帮你解释某个 Kernel 内部的访存、计算、Occupancy 和停顿。

Roofline 模型帮助你判断应该减少数据搬运、提高复用、改善访问模式,还是追求更高计算吞吐。

最后更新于:

Pager
上一篇4. CPU、内存、Cache 与 NUMA:把热点解释到硬件行为 / CPU, Memory, Cache and NUMA
下一篇6. 优化闭环:从假设、改动到回归守护 / Optimization, Regression and Production

持续记录,持续成长

Copyright © Tidenflow