Nsight Compute快速上手指南(中文)

Overview

之前经常使用的Nsight Systems是一款系统级的性能分析工具,用于分析应用程序的整体性能,使用 Nsight Systems可以了解应用程序的执行流程、确定性能瓶颈以及发现并行化的机会;而Nsight Compute与Nsight Systems不同,其是一款针对 CUDA 应用程序的内核(Kernel)级性能分析和调试工具,它专注于 GPU 上的计算性能,提供了详细的性能指标、诊断和指导,帮助开发者优化 CUDA Kernel 的性能。

简而言之,Nsight Systems主要偏向于对应用程序的整体流程进行分析,而Nsight Compute更着重于对应用程序中Launch的所有Kernel进行详细的分析。

这里是Nvidia官方提供的关于Nsight Compute的技术文档:

Kernel Profiling Guide — NsightCompute 12.4 documentation

安装方法

与Nsight Systems类似,Nsight Compute可以在服务器上通过命令行指令(CLI版本)进行profiling并生成相关的分析报告,分析报告下载到本地后使用对应的GUI版本进行分析报告内容的查看。

1. CLI版本

如果使用的是Nvidia提供的PyTorch容器镜像,则里面已经预装好了Nsight Compute,以24.03版本为例:

Nvidia PyTorch镜像中的Nsight工具

这时候启动容器以后可以直接使用ncu 指令来使用Nsight Compute。

2. GUI版本

如果想在自己的电脑上查看dump出来的分析报告,可以前往Nvidia官网下载GUI版本的Nsight Compute,链接如下:

Getting Started with Nsight Compute

如何运行

Nsight Compute(或ncu)里面的一个很重要的概念就是replay,为了对待分析的应用程序获取到用户指定的性能指标,ncu可能需要多次重复运行一个Kernel才能获取到需要所有需要的指标信息:

Depending on which metrics are to be collected, kernels might need to be replayed one or more times, since not all metrics can be collected in a single pass. For example, the number of metrics originating from hardware (HW) performance counters that the GPU can collect at the same time is limited. In addition, patch-based software (SW) performance counters can have a high impact on Kernel runtime and would skew results for HW counters.

同时replay的次数是ncu根据用户设定的需要采样的指标而自动设定的,用户无法主动更改。对ncu的replay原理更详细的介绍可以参考技术文档中的replay一节。对一个Kernel进行多次replay的模式有很多种,可以根据replay模式不同将ncu划分成不同的运行模式(在CLI中使用ncu时可以通过参数--replay-mode 来指定replay的模式)。

1. 一般模式

--replay-mode 参数被设置为kernelapplication 时,可认为ncu处于一般的profiling模式下,参考的命令行指令如下所示:

1
ncu --replay-mode kernel/application <other args> <program to be profiled>

在这两个选项下,ncu会串行化进程中的所有Kernel函数来进行profiling,所以如果有需要与其他进程之间进行强制同步的Kernel函数(比如NCCL、MPI通信等),profiling的运行就有可能被卡住

Nvidia官方给出的ncu profiling特性的解释

所以这两个选项只适合用于串行程序或者一部分不含同步的并行程序中Kernel级别的profiling。具体来说,这两个选项之间存在着一些细微的差别,假设为了采集需要的指标,ncu需要重复运行程序中的所有Kernel $N$次,则:

  • kernel 选项下程序只会被运行一次,但是这个程序中的所有Kernel都会被重复profiling $N$次;
  • application 选项下程序会被执行$N$次,每次执行中一个程序内部的所有Kernel只会被profiling $1$次。

一般来说,application 选项是更被推荐的选项,因为其相对于Kernel 选项来说在profiling时需要额外消耗的资源(比如内存等)更少,同时程序执行$1$次后也会知道一共有多少个Kernel需要被profiling。

2. Range Profiling

为了解决一般模式下ncu无法profiling包含同步Kernel程序的问题,Nvidia在ncu之后的版本中更新了「Range Profiling」的功能(ncu的版本需要不小于$2023.1$),其允许用户在程序需要profiling但是包含强制同步Kernel的程序段添加范围标定,然后对标定范围内的程序进行指标的profiling,下面是添加标定的范例(在megatrontraining.py中添加):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from cofutils import cofnsys
import torch

if is_rank_0():
# 使用cofnsys或者torch提供的cuda接口都是可以的
#cofnsys.start()
torch.cuda.cudart().cudaProfilerStart()

loss_dict, skipped_iter, grad_norm, num_zeros_in_grad = \
train_step(forward_step_func,
train_data_iterator,
model,
optimizer,
opt_param_scheduler,
config)

if is_rank_0():
#cofnsys.stop()
torch.cuda.cudart().cudaProfilerStop() # 有开始的就一定要有结束的部分

在标定profiling的范围之后,就可以在ncu中使用--replay-mode 参数中的app-range 来进行对标定范围内的profiling,示例代码如下:

1
ncu --replay-mode app-range <other args> <program to be profiled>

需要注意的是,Range Profiling虽然允许了在profiling时不串行执行Kernels,从而可以对包含强制同步Kernel的程序进行profiling,但是其最终profiling后得到的指标也将不再是Kernel级别的分析,而是对指定的range内的所有CUDA调用进行平均后的结果,所以如果想对包含强制同步Kernel的程序进行Kernel级别的分析通过这条路是行不通的:

Similar to Range Replay, metrics are not associated with individual kernels but with the entire selected range. This allows the tool to execute workloads (kernels, CUDA graphs, …) without serialization and thereby supports profiling workloads that must be run concurrently for correctness or performance reasons.

3. 实验性用法

为了在包含同步Kernel的程序里实现Kernel级别的profiling(虽然官方并没有提供支持),尝试利用ncu提供的参数–kernel-name 进行支持。--kernel-name参数允许在ncu的一般模式下使用正则表达式对程序中需要进行profiling的Kernel进行过滤,可以让只有名称满足正则表达式匹配的Kernel能够得到profiling,这样我们就可以手动编写正则表达式,把并行程序中需要同步的Kernel过滤掉(大多数都是通信算子,无关痛痒),只profiling其他的Kernel即可,下面提供一个过滤nccl通信函数的例子:

1
2
3
# 在profiling时过滤掉所有以「nc」开头的Kernel(其实就是nccl相关的函数)
ncu --replay-mode application --kernel-name "regex:^[^n][^c].*" \
<other args> <program to be profiled>

这种确实可以让包含同步Kernel的程序Kernel级别的profiling跑起来,但是有的时候会遇到程序重新跑了几次后就莫名其妙的hang在那里了,现在还在探索这个问题的解决方法。

CLI重要参数介绍

关于ncu在命令行运行时指定的参数详细信息可以在Nvidia提供的官方文档中进行查看,这里对一些比较重要的参数进行介绍。

1. I/O

-o 参数:指定输出的分析报告.ncu-rep 的路径以及名称;

-f参数:如果ncu指定了-o 参数、输出分析报告,分析报告指定的路径下有同名文件,可以指定-f参数强制将同名文件覆盖,否则ncu会报错停止运行;

—-log-file 参数:将ncu在运行时产生的log存在指定路径的文件中;

如果在ncu运行时不指定-o 参数,则不会产生分析报告,并会将所有的分析结果打印在终端中,同样可以通过设置参数来决定终端中显示什么样的信息:

—-page参数:选择在终端中打印分析报告中的哪部分信息,一般使用details 来打印用户指定采集的分析指标的详细信息;

—-csv参数:将终端中打印的Kernel的分析信息组织成csv格式的输出,方便导出到文件中进行后续的数据分析。

ncu还可以使用—-import 将之前生成的分析报告读入,然后根据—-page—-csv 等参数的设置将分析报告在终端中打印出来。

2. Filter

ncu在运行时可以根据参数的设置对要profiling的Kernel、进程、设备等进行过滤,首先来看Kernel的过滤:

—-kernel-name 参数:支持根据准确名称或正则表达式来过滤需要进行profiling的Kernel

-c 参数:限制程序中进行profiling的Kernel函数的总数

-s 参数:跳过程序中的前$N$个Kernel后再进行profiling

再来看一些ncu对于需要进行profiling的进程进行选择的参数:

—-target-processes 参数:选择ncu目标进行profiling的进程,application-only 选项只会让ncu对根进程进行profiling(然而这些根进程一般都只是启用后续的程序用的,捕捉不到什么有用的Kernel信息),all 选项会对根程序产生的所有子进程进行profiling,一般来说我们选择这个选项。

—-target-processes-filter 参数:支持通过准确名称或正则表达式来过滤ncu需要进行profiling的进程,具体可以参见Nvidia文档中的说明。

最后是ncu对于需要进行profiling的设备进行选择的参数:

—devices 参数:指定需要使用ncu进行profiling的(GPU)设备编号(默认是所有参与的GPU),Nvidia官方推荐在每个Node中只使用一个设备进行profiling,否则可能会引发程序的stall:

Nvidia官方对于为什么一个Node上只推荐对一个rank进行profiling的解释

3. Profile Section

使用ncu进行profiling时可以获取关于Kernel各个方面的指标,但是如果运行时需要获取所有的指标的话就可能导致ncu运行的时间非常长,如果只想获取到特定方面的指标,可以通过参数—-section 指定在profiling时需要采集的特定方面的指标,这样就可以有效减少不必要的profiling时间,关于所有的section的选项可以在Nvida官方文档中进行查看,这里列举一些常用的section进行说明:

MemoryWorkloadAnalysis: 可以让ncu对Kernel在内存利用方面的指标进行profiling,具体包含最大吞吐率、最大带宽利用率等指标;

ComputeWorkloadAnalysis:Detailed analysis of the compute resources of the streaming multiprocessors (SM), including the achieved instructions per clock (IPC) and the utilization of each available pipeline. Pipelines with very high utilization might limit the overall performance.

指标示例:

CWA example

Occupancy:Occupancy is the ratio of the number of active warps per multiprocessor to the maximum number of possible active warps. Another way to view occupancy is the percentage of the hardware’s ability to process warps that is actively in use. Higher occupancy does not always result in higher performance, however, low occupancy always reduces the ability to hide latencies, resulting in overall performance degradation. Large discrepancies between the theoretical and the achieved occupancy during execution typically indicates highly imbalanced workloads.

指标示例:

Occupancy Example

使用举例

1. Profiling以及后获取csv格式报告的方法

1
2
3
4
5
6
# 在运行以下指令时ncu只会根据section的设置去捕获Kernel在内存利用方面的metrics
ncu -o test -f --section "regex:MemoryWorkloadAnalysis(_Chart|_Tables)?" \
--target-processes all --replay-mode xx [The command you want to profile]

# 在生成test.ncu-rep报告后,让ncu重新读入并把详细信息打印成csv格式的文件并存储
ncu -i test.ncu-rep --page details --csv --log-file test.csv

2. 添加了许多filter条件的profiling

1
2
3
4
5
6
# 以下的指令中ncu会profiling编号为0的device上的所有因为用户「command」产生的子进程中
# 名称为python的进程,并只会profiling这些进程中不以「nc」开头的Kernel函数
ncu -o test1 -f --replay-mode application --target-processes all \
--section "regex:MemoryWorkloadAnalysis(_Chart|_Tables)?"\
--kernel-name "regex:^[^n][^c].*" --device 0 \
--target-processes-filter python [The command you want to profile]

额外补充

与Profiling产生的Overhead相关的因素(比如说加了Profiling后程序运行很长时间),在Profiling时间较长时可以从这些因素里排查原因:

Kernel Profiling Guide — NsightCompute 12.4 documentation


Nsight Compute快速上手指南(中文)
https://androsheep.win/post/ncu/
作者
AndroidSheep
发布于
2024年4月13日
许可协议