内容创建/渲染

使用 NVIDIA Nsight Graphics 优化图形应用的 GPU 工作负载

图形开发者和发烧友的一大消遣方式是比较 GPU 的规格,并惊叹于每一代新一代产品中着色器核心、RT 核心、万亿次浮点运算能力和整体计算能力的不断增加。实现这些数字所代表的最大理论性能是图形编程领域的一大关注点。大量渲染数据(例如三角形、像素和光线)流经高度并行的 GPU 计算管线,就像在制造工厂的一组装配线上一样。最大吞吐量要求工厂持续运行,不会中断工作或设备闲置。

本文介绍了 Nsight Graphics 2024.3 中的几个新功能,帮助您了解和管理这些虚拟装配线,并为游戏和图形应用程序创建优化的并行工作负载。

每个线程束的活动线程直方图 

线程束 是一组 32 个线程,构成可编程着色器的基本执行单元。使用 HLSL 或 GLSL 编写的光线追踪、计算、顶点、像素和其他类型的着色器可编译为机器指令,并最终在线程束大小的硬件组上运行。线程束中的线程并行运行,数百个线程束本身并行运行。当可编程着色成为工作负载的限制因素时,高效运行线程束对于达到峰值性能至关重要。

线程束在名为 Streaming Multiprocessor (SM) 的硬件单元上运行,并使用名为 Single Instruction, Multiple Threads (SIMT) 的计算模型执行。每个线程束一次在其所有线程上发出一条指令,且每个线程都有自己的操作数。例如,如果一行着色器代码添加两个数字,则在一个线程束中,加法指令开始在所有 32 个线程中同时运行,从而从 64 个输入中生成 32 个唯一的和。

线程束以次优方式运行的一种方法是通过着色器代码中的 if 语句或控制流分支导致线程差异。编译器可以通过在 if 语句下运行所有代码块,然后忽略未使用的结果,或使用展开的循环来完全避免分支。但是,当真正的动态分支被编译下来并在执行时遇到时,并且条件表达式在线程束中的所有线程之间不是统一的,则线程束必须一次同时执行 if-body 和 else-body。

同样,在 SIMT 模型中一次只能发出一条指令,因此 SM 必须屏蔽不适用于分支活动端的线程。有关现代 NVIDIA GPUs 中线程束指令调度的更完整说明,请参阅 NVIDIA Tesla V100 GPU 架构 (第 26 页)。

图 1 显示了线程束中线程发散的简化可视化,线程束的假设大小为 8 个线程。大写字母表示程序伪代码中的语句。由于发出每个指令时线程执行的空闲通道,吞吐量会降低。

Some brief pseudocode for an in-else statement in a shader (left) and a graphic showing eight lines of execution representing threads split into two chunks of four threads during the if-else blocks (right).
图 1. SIMT 线程束执行模型下线程调度的简化可视化

请注意,这不同于前面提到的以无分支方式在 if 语句中运行两个块。真正的分支可以避免线程未执行的任何副作用。更重要的是,当驱动分支的条件表达式有良好的 warp 级分布时,动态分支可以成为制胜法。条件表达式得到的齐次结果越多,差异就越小,因此对吞吐量的影响就越小。在 vertex 和 pixel shaders 中, warp 会根据 locality of vertices 和 pixels 进行分组。在 ray tracing 和 compute shaders 中,用户可以更明确地控制工作分组方式。

影响线程差异性能的其他因素包括任何分支下着色器指令的总体百分比,以及特定机器指令及其操作类型。了解实际影响的唯一方法是,在测量整体性能时能够跟踪 warp 线程效率,并关联其中一个线程的变化。

这是新的 Active Threads per Warp 直方图的用武之地。Nsight Graphics 的 GPU Trace 工具中的 Shader Profiler 视图中现已提供此紧凑图形,包括着色器工作流、自上而下、自下而上、热点和源/反汇编。它说明了线程差异对任何给定着色器、函数或单个源代码行的总体影响。

如图 2 所示,直方图右侧的值 (接近 32) 表示指令执行效率更高。显示的值根据执行每个代码块时的性能计数器采样得出近似值。弹出工具提示会更详细地显示直方图。当启动 GPU Trace 时,必须将“Timeline Metrics”设置设置为“Top-Level Triage”或“Ray Tracing Triage”(如果可用),并启用“Real-Time Shader Profiler”。

The Active Threads per Warp column is shown in the Hot Spots view of GPU Trace. Each row shows information about the lines of shader code using up the most samples in the recorded interval. Active Threads per Warp is a small colored histogram with a blue marker indicating the average value.
图 2、Nsight Graphics 中 GPU Trace 工具内的活动线程每 Warp 直方图

如果函数存在性能瓶颈,且每个线程束的活动线程数不足,则应考虑改进线程束一致性或减少分支的策略。有关光线追踪工作负载,请参阅 Shader Execution Reordering(SER),它专为解决光线追踪着色器中的线程和数据分散问题而设计。其他算法更改可能会提高线程执行的一致性;例如,使用不同的光线采样模式。对于任何类型的着色器,还可以通过在 D3D12 或 Vulkan 中将分支转换为线程束感知着色器代码来提高效率。

直方图的分布揭示了代码块的行为是否一致,并且查看每个线程束的活动线程数达到或接近 100% 也可能验证线程发散不是最初被怀疑的限制因素。

图 3 说明了路径追踪等高级照明技术如何在二次光线从场景中物体反弹时导致着色器发散。SER 提高了执行一致性,因为使用相同命中着色器的光线可以在线程束级别更好地利用 SIMT。当 SER 正常工作时,您应该会看到“Active Threads per Warp”得到改进。

A graphic showing a side view of a room and ray tracing rays entering the room from a camera direction. Initially the rays are bundled together whereby neighboring rays shade neighboring objects. A second panel shows the rays after one bounce, after which they are mixed up such that neighboring rays don’t have any coherence. Shader Execution Reordering then reorders the rays to be bundled coherently.
图 3、路径追踪等高级照明技术会导致着色器发散,因为二次光线会反射出场景中的物体 (以各种颜色表示)

在更高层次上,改善整体着色时间需要理解和减少 warp 失速。当 warp 达到长延迟运算时,尤其是内存访问和纹理获取等操作,会停止。当一个 warp 停止时,可以为下一个指令安排另一个 warp。但是,这只能购买如此多的并行性。如果过多的 warp 处于静止状态,则 SM 未得到充分利用,甚至会处于空闲状态。延迟的长度取决于许多因素,例如内存查找所达到的缓存级别 (如果有)。

GPU Trace 一直提供用于分析这些延迟的工具,但箭袋中的新箭头是 Warp Latency 直方图,该直方图以前曾作为单个平均 Warp Latency 周期计数呈现。通过观察线程束延迟的分布情况,我们可以更深入地了解着色器计时的可变性,并提供提示,说明是否已提前退出,以及多次着色器调用的参数是否会导致不同的行为。请注意,直方图目前仅包含调用同一着色器的时间轴中不相交区域的单独延迟数据点。

The Average Warp Latency column is shown in the Shader Pipelines view of GPU Trace. Each row shows information about the shaders using up the most samples in the recorded interval. Active Threads per Warp is a small colored histogram with a blue marker indicating the average value.
图 4、Nsight Graphics 中 GPU Trace 工具中的平均 Warp 延迟直方图

有关优化 GPU 工作负载的更多详细信息,请查看以下资源:

D3D12 工作图 

CPU 和 GPU 之间的交互通信是图形管线中的另一个常见瓶颈。即使批量数据驻留在 GPU 上,从 CPU 发出渲染指令的行为也会在 GPU 处于空闲状态时产生泡沫。工作图是 D3D12 中的一项新功能,旨在减少调度 GPU 工作对 CPU 的依赖性。GPU 驱动的调度已经出现一段时间了,但工作图形引入了比 ExecuteIndirect 等现有方法更先进的功能。有关工作图形的概述,请参阅 Direct3D 12 中使用工作图形推进 GPU 驱动渲染

Nsight Graphics 2024.2 中引入了对 Shader Profiler 中分析 Work Graph 节点整体的初步支持。在 2024.3 中,Shader Profiler 现在支持 Work Graph 的源关联,从而在 Shader Source 视图和 Hot Spots 列表中启用完整的逐行分析功能。由于 Work Graph 是 D3D12 中的一项新功能,因此此功能应能帮助开发者探索和更好地理解 Work Graph 的性能特征。请注意,源关联需要最新的 R565 系列驱动。

A list showing names of Work Graph shaders and corresponding source code line numbers are shown in the Hot Spots view in GPU Trace, ordered by the most expensive lines of code.
图 5、GPU Trace 中热点视图中显示的 Work Graph 着色器中的特定行。单击任何一行即可打开该着色器的完整源视图,其中显示每行性能统计数据

同样,Nsight Aftermath SDK 2024.3 增加了对用于跟踪工作图形的着色器的支持,并提供上下文信息,以帮助缩小源自工作图形工作负载的相关 GPU 错误。

Vulkan 更新 

最近发布的 Vulkan 1.4 标准将十几个先前可选扩展程序提升到所需的扩展程序集,并引入了更高的最低硬件限制。有关更多信息,请参阅 Khronos 简化使用 Vulkan 1.4 的 GPU 加速应用程序的开发和部署 。Nsight Graphics 2024.3 在 Frame Debugger 中提供 Vulkan 1.4 支持。有关支持 1.4 的测试版驱动,请访问 Vulkan 驱动支持

即使您未直接使用 Vulkan 1.4,Nsight Graphics 现在也支持所有新推广的扩展。此外,还添加了对许多其他扩展程序的支持,包括 VK_NV_inherited_viewport_scissor 和 VK_NV_device_generated_commands_compute。有关完整列表,请参阅 NVIDIA Nsight Graphics 用户指南

此版本还增加了对在 Windows 和 Linux 桌面平台上使用 Vulkan SC 的应用程序的帧调试和 GPU 追踪的支持。有关 Vulkan SC 和驱动支持的更多信息,请访问 Vulkan 驱动支持

结束语 

在开始针对并行性进行优化之前,您可能需要对 GPU 计算模型有一个基本的了解。然而,由于数据模式、编译器和硬件优化以及许多其他二阶影响,该理论如何转化为实践可能很难预测。根据工具中的假设检验和测量记录制定策略。了解指标的含义,然后在您进行调整时追踪其变化情况。虽然同时实现 GPU 上每个硬件单元的 100%利用率并不切实际,但增量改进可以帮助您达到应用的性能需求。

Nsight Graphics 2024.3 现已推出 。使用位于 Nsight Graphics 窗口右上角的“Feedback”(反馈)按钮,向我们介绍您使用这些新功能的体验。

详细了解 Nsight 开发者工具 并探索 Nsight Tools 教程 。在 Nsight Graphics Developer 论坛 上提问、提供反馈,以及与图形开发者社区互动。

致谢 

感谢 Avinash Baliga、Jeff Kiel、Axel Mamode、Aurelio Reis 和 Louis Bavoil 为本文做出的贡献。

标签