Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
[原文发表地址]:Visualizing Common DirectX Performance Problems with GPU Usage in Visual Studio 2015
[原文发表时间]:2016/2/4 9:12 AM
我们努力在即将发布的Visual Studio 2015 Update2中添加了些新的功能,并对GPU 使用率工具的一些问题进行了修复。对于那些不熟悉GPU 使用率工具的开发者而言,GPU 使用率工具是一个轻量级的捕获工具,它为我们提供在运行DirectX 游戏时,CPU和GPU上的相关工作量信息,以及在GPU上执行每一个DirectX调用所花费的时间信息。
GPU 使用率工具能够输出很多的数据,所以我想先介绍下如何在DirectX应用程序中诊断和调查常见的性能问题。
运行正常
在我们调查性能问题之前,我们需要知道我们正常运行一个应用程序的基准线是什么。如我所用的我们这里楼下一个团队提供的例子MiniEngine 。最酷的是,MiniEngine 是一个基本完整的游戏引擎,它也足够小,很容易在代码上进行有趣的调整和实验。
让我们从包含MiniEngine的ModelViewer项目开始吧,我使用Alt+F2启动了诊断工具会话。打开GPU 使用率,运行10秒左右,收集如下的一条日志。

从上面的概述中很容易看出这个应用程序在我的系统上运行正常。帧率稳定在60FPS,并且GPU利用率保持在50%左右。如果我们在这个日志上选择一段时间,我们可以进一步挖掘出高性能是什么样的。

值得一提的是,在Update1中我们对GPU 使用率进行了修改,它显示了一个通过进程和线程分解的GPU分析视图,而不像我们在Visual Studio 2015 RTM中使用的用处不大的内核视图。如果你需要看到系统上的所有进程,只需要从进程下拉框中选择“All”,所有不同内核的进程将显示出来。
首先要看的是上图中细灰色的线,它向下和各种时间线栏交织。这些是来自于你的监听器V-sync 事件。要检查你的帧率,你只需看一下是否能得到每个显示给用户的帧的当前事件。当前事件在每栏中被显示成标记“P”(这个标记除非你把它放大,否则可能看不到)。你也可以在时间线控制顶上面的过滤框中仅输入“Present”。如此之后,过滤器将只列出Present事件,你可以用箭头向下遍历事件列表查看所有的Presents事件。
在我们上面的例子中,我们调整了所有的CPU进程和所有的GPU进程到大约16.6毫秒去创建每一帧。正因这样,每帧能随着v-sync事件按时出现,同时我们能够获得玩家所期待的像黄油一样平滑的60fps。
CPU 部分
那么如果这个应用程序运行并不理想,情况会怎样?在对代码进行了一些调整后,新收集的日志如下所示。

我们的帧时间现在已经超过图表所能显示的范围,我们的FPS也差不多在30以下浮动,而且我们的GPU利用率实际上也已经降低了。这里我们看到的是CPU问题的经典症状。此处我们的CPU有太多的工作要做,所以它无法及时地给GPU发送指令。GPU一直处于等待状态而CPU却在疯狂地旋转。如果我们进入详细信息页面,可以更加清楚地看到这种不平衡。

在GPU栏中,工作块和正常情况下的大小相同。但是在CPU栏中,我们现在有一个线程(那个引起了我们渲染命令的问题的线程)完全占据了CPU。Present调用与v-sync分隔开来,所以我们开始丢帧,而且我们的游戏对玩家而言开始变得不连贯起来。注意:GPU 3D栏显示的一些蓝色小标记实际上是devenv.exe,它是Visual Studio进程,在GPU中做一点工作。
当我们知道我们正在处理一个CPU问题时,我们需要查看在CPU上我们的时间是如何花费的。当分析出这个应用程序正在做的是非图像相关的CPU工作时,我们就可以把这段时间减去。或者使用一些像DirectX12这样的,具有更强的能力——使用命令列表在多线程中分解渲染代码的技术,使得CPU不断地给GPU提交工作。
把GPU使用率工具与其他Visual Studio诊断工具相集成的一个好处是,你可以协同运行不同的工具,方便查看其他方面。我们的GPU使用率工具简单地展示了CPU上活跃着的线程和进程,但并不包括在那段时间中发生了什么。回到诊断工具页(Alt+F2)我可以选择GPU和CPU 使用率然后同时运行它们。

现在在我查看GPU使用率详细信息的时间段里,我也可以查看在CPU上发生的一切。看起来大量时间花在了我的每帧更新阶段中对一个特定枚举类型的AI处理上。
GPU 部分
下图是MiniEngine的另一个运行,中途我增加了一些设置。从这个概况可以清晰地看到我们的性能从60fps掉到30fps的区域,并且在这个过程中我们看到一个相应的GPU利用率增高。

在这个概况中可以明显地看出性能的降低,但是实际上是什么造成了这个性能变化呢?在上述情形中,打开两段不同的详细信息对我们的分析工作是非常有帮助的,一段是从这个应用程序执行正常时开始,另一段是从不正常时开始。

在左图中我们可以看到GPU工作块均匀分布在每一个v-sync中。同时在右图中GPU栏完全是满的而且Present事件也没有在每一个v-sync间隔中。虽然这也是有帮助的,但是在这一点上我们也可以从一个简单的FPS计数器就得到很多类似的信息。如果想要使这个对比更有用,我们可以详细分析时间线上的GPU 使用率块或事件列表,更具体地弄清楚GPU的时间到底消耗在什么地方。事件列表中的DirectX事件最初将只以扁平列表显示,或者在DirectX12中以CommandList分组显示。在如此大的列表中查明每一个调用在做些什么是非常棘手的一件事,所以为了充分利用GPU 使用率,最好在你的代码的适当位置加一些标记事件。当这些事件到达GPU时,GPU 使用率将会在UI中把它们按等级分组,并将如下示例所包含的DirectX事件的持续时间收起来。对于DirectX11查看BeginEvent API,DirectX12查看PIXSetMarker API。有了这样的分层,你就可以在渲染代码的不同区域深层分析时间消耗了。

在以上的例子中,我们展示了在MiniEngine渲染代码的主渲染部分的大量时间消耗。随着我们在事件列表中选择事件或者对其分组,顶上的时间线将更新并显示这个事件的时间量,如果这个事件从CPU调用的话,我们也可以在CPU栏看到类似的。因此你可以看到从API在CPU上被调用,到GPU队列实际上开始着手执行那段代码而产生的延迟。
通过查看写有注释的代码好的和坏的部分,找到由于修改代码或者资源引起的性能问题,是轻而易举的。


如果使用过滤器选项,在事件列表中导航不方便的话,列排序能够帮助调整你看到的数据。下图是以GPU Duration排序的分组事件,而非以执行时间排序的所有”Decompress and downsample”事件实例。

我将会在今后的博客中进一步介绍一些其他在Update1 和Update2 中加入的GPU 使用率工具新功能。