渲染

使用 NVIDIA Nsight Graphics 中的着色器探查器识别着色器限制器

 

NVIDIA Nsight 图形 的一个不太出名但很酷的功能是着色器探查器。这使您能够在着色器中找到热点,帮助您指导优化工作。它可以让你深入了解为什么有时候表现不是你喜欢的。

在本文中,我们使用 NVIDIA Nsight 图形 跟踪分析工具来识别潜在的限制器,然后使用着色器探查器进行更深入的挖掘,以发现并修复问题。

第一步:从 GPU 跟踪分析工具开始

我们总是建议从 Nsight Graphics GPU 跟踪工具开始,而不是直接进入着色器探查器。这样,您就可以了解任何给定 DX12 或 VK 工作负载的性能限制是什么。例如,如果真正的问题是 GPU 利用率低,因为有很多微小的调度,它们之间都有屏障,那么尝试微调着色器是没有意义的。

首先,设置与要分析的应用程序的连接。选择 Connect 并填写启动游戏所需的参数(图 1 )。

Screenshot includes fields for the path to the application executable, working directory, command-line arguments, and so on.
图 1 。连接设置

选择GPU Trace 作为活动,将 Metric Set 配置为 高级模式度量 。 使用 高级模式度量 需要一个稳定且一致的框架,因为分析会在多个框架上进行多次传递。如果应用程序不满足这些要求,可以使用 Nsight Graphics 内置的 C ++捕获工具捕获应用程序的一个帧,并创建一个新的 EXE ,重复播放同一帧。

选择启动 GPU Trace 启动应用程序。当你到达你想要捕捉的画面时,选择生成 GPU Trace 捕获或者按 F11 。

捕获完成后,停止应用程序并打开跟踪。选择 Trace Analysis 。在的 Analysis 面板中GPU Trace(图 2 ),双击或将鼠标悬停在要分析的范围的标记上,在本例中,DispatchRays[0]

Screenshot of the Trace Analysis results with a large tooltip overlay.
图 2 。微量分析结果

工具提示显示了该工具在此 GPU 工作负载中检测到的所有性能增益机会的简洁视图,按其预计的 GPU 帧时间增益排序。工作负载有以下限制:

  • L2 Limited : L2 受限可能表明存在问题。有了工作量方面的知识,这不一定是你所期望的。
  • L1 长记分板暂停的翘曲: 这是翘曲停止的常见原因,通常是由于纹理提取。如果在启动纹理查找和使用查找的结果之间没有足够的工作,则会暂停扭曲,直到满足纹理查找。
  • 扭曲被本地内存限制暂停 :本地内存是“线程本地”。它是每个线程的本地内存,而不是线程组中所有线程之间共享的组共享内存。着色器不需要任何本地内存,所以这很有趣。本地内存节流意味着什么?这里还有很多东西要学。

选择 SM 翘曲延迟扭曲被本地内存限制暂停

The Trace Analysis view, showing an explanation of whatever is selected in the analysis results; in this case, an explanation of Local Memory Throttle.
图 3 。局部存储器节流阀的轨迹分析解释

解释窗口对问题进行了更有意义的描述,并提供了一些有用的建议。它建议启动着色器探查器,以定位具有lg_throttle暂停的特定 HLSL 指令。

第 2 步:切换到着色器探查器

在使用着色器探查器之前,务必确保 Nsight Graphics 可以访问着色器的符号。实现这一点的最简单方法是确保着色器为 使用/ Zi 选项编译 ,并将符号嵌入着色器二进制文件中。

有时,最好配置编译,使符号进入外部 PDB 文件。在这种情况下,请确保在 ToolsOptions 下指定正确的路径。

当 Nsight Graphics 可以看到着色器符号时,它可以将着色器中的位置映射回源代码,这使您更容易判断发生了什么。如果 Nsight Graphics 无法访问符号,则只能看到着色器反汇编(例如, DXIL )。

着色器探查器是帧探查器的一部分。再次连接到应用程序,但这次选择 Activity 下的 Frame Profiler 。当您选择 发射架剖面仪 时,应用程序应该在这个 HUD (图 4 )的顶部启动。

Profiler overlay, showing the request to press F11 to capture a frame.
图 4 。轮廓覆盖

导航至应用程序中要配置文件的部分,然后按 F11 键捕获一帧进行分析。从这里,在 Nsight Graphics 中选择 Profile Shaders 。这将运行一个简短的采样会话,然后向您展示一个摘要视图(图 5 )。

The Shader Profiler view, showing a summary of hotspots sorted by sample count.
图 5 。着色器探查器摘要视图

这是一个故障。

Function Summary 按在这些着色器中命中的采样数的顺序显示顶级着色器的列表。这是一个很好的着色器延迟代理,可以让您专注于可以从优化中获得最大好处的着色器。

Correlation 列中,有多个绿色记号,它们总是好的。在本例中,这意味着 Nsight Graphics 能够将样本与源代码关联起来。

要打开着色器视图,请选择第一个文件名。左边是源代码,右边是 DXIL 。在本文中,您不必关心 DXIL ,所以将视图更改为仅使用 HLSL

这很微妙,但在最右边的滚动条右边有一个重要的指令样本热图。回想起GPU Trace 分析建议你找lg_throttle摊位。上面说:

LSU 是执行对本地和全局内存访问的单元。
运行着色器探查器,找到哪个 HLSL 指令的 lg _ thottle 暂停最多。
是否在本地范围内声明动态索引数组?
着色器是否有导致溢出的注册压力?
如果一级和二级命中率很低,那么尽量减少未命中率。

在着色器探查器中,由于lg_throttle原因,显示为 LGTHR 的采样被暂停。

Shader Profiler source view, split into left and right panes. (left) The shader source code. (right) Sample counts and a breakdown of the stall reasons with each sample.
图 6 。 Shader Profiler 源代码视图,带有示例和暂停原因

“是否在本地范围内声明了动态索引数组?”

动态索引数组由一个变量索引,其中索引的值在编译时是未知的。

当这种情况发生时,编译器通常会将数组放入本地内存,而不是放在寄存器中。内存比寄存器慢。

下面的代码示例显示了一个动态索引数组。

vertUvs[vertexOrder[0]] = cornerUv + du;
vertUvs[vertexOrder[1]] = cornerUv + dv;
vertUvs[vertexOrder[2]] = cornerUv;

怎么回事?看起来代码以不同的顺序填充数组,这取决于三角形是否翻转。

int3 vertexOrder = isFlipped ? int3(2, 1, 0) : int3(0, 1, 2);

动态索引该数组的行为会使编译器将该数组移动到内存中。它会影响这个代码位以及引用该数组的所有代码位。这就是为什么convertTriangleBaryUvsToBaryVws也很热门。

你能在没有动态索引的情况下做到这一点吗?是的,你可以。更改翻转方式的结果如图 7 所示。

Screenshot of alternative code using a branch instead of dynamic indexing.
图 7 。不使用动态索引的替代代码

那些特定的摊位被取消了。它将此调度的时间从 8.67 毫秒减少到 7.1 毫秒。它不仅提高了着色器代码的效率,而且由于内存流量减少,还大大减少了 L2 中的限制器。

Before optimization, DispatchRays takes 8.67 ms.
图 8 。优化前跟踪
After optimization, DispatchRays takes 7.1 ms.
图 9 。优化后的跟踪

总结

NVIDIA Nsight 图形 是分析渲染工作负载的强大工具。这是一个快速的演练,只涉及一些功能。我们强烈建议使用它。

免责声明

从驱动程序版本 467.07 开始,本文中的测试和结果都是正确的。驱动程序和编译器开发一直在继续。这意味着优化机会也会随着时间的推移而改变。

 

 

Tags