如果你曾经对C++应用程序的优化构建进行了剖析,那么你很有可能会查看分析报告,看到一些你预期会出现的函数缺失,所以你必须假设它们已经被内联但不能确定。同样,如果您曾经尝试使用 visualstudio中的Profile-Guided优化(PGO)功能 ,你可能对你的训练数据是否真的达到了预期的效果视而不见。
为了帮助实现这一点,在VisualStudio2013中,我们做了一些工作,帮助您了解编译器内联函数的时间以及PGO训练数据如何转换为优化的二进制文件。在这篇文章中,我将教你如何使用 Visual Studio CPU采样探查器 了解应用程序中的这两种优化。
样品
在本文中,我将评估一个非常基本的应用程序,它要求用户提供一个最大值,并计算所有小于用户提供的数的素数(示例可用) 在这里 ). 但是,为了更好地表示大型复杂应用程序,该示例被编写为可执行控制台应用程序,该应用程序调用包含用于计算素数的逻辑的dll。
控制台应用程序的主要功能如下所示
int∗tmain(int argc,∗TCHAR*argv[]) { int最大值; cout<<“选择要计算的最大值:”; cin>>最大值;
向量
素数; 素数=得到素数(最大值); cout<
素数计算是按以下方式编写的
向量
获取素数(int max) { 向量 素数; 对于(int n=0;n<最大值;n++) { if(is_素数(n)){ 加上素数(n,素数); } } 返回素数; } 布尔是素数(int n) { if(is_base(n)) 返回false; if(有_因子(n)) 返回false; 返回true; }
布尔是基数(int n) { if(n<2) 返回true; 返回false; }
布尔系数(int n) { 对于(int i=2;i
布尔系数(int n,int i){ 如果((n%%i)==0) 返回true; 返回false; }
空加素数(int n,向量
&素数) { 素数。推回(n); }
使用探查器
我要做的第一件事是分析应用程序以评估性能(为了获得准确的性能结果,您应该始终分析发布版本,因为调试版本禁用优化,所以在调试版本中可能会出现问题,而在优化版本中不会出现问题)。为此,我将从Debug菜单(Debug->Performance and Diagnostics)打开新的“Performance and Diagnostics”启动页。启动页面将引导我进入性能向导。
当性能向导打开时,我只需要按“Finish”启动我的项目,因为我将使用所有默认设置。
在应用程序启动之后,我将输入100K作为计算素数的数字,然后让它走。当报表打开时,我可以看到getu primes是我调用堆栈中的最后一帧,并显示100%的样本出现在这个函数中。
所以我只能推测其他函数(例如isèu prime)是内联的,但我不确定这是怎么回事。有可能在编译释放时,这些函数的执行速度足够快,以至于在执行时没有发生任何样本。
了解编译器内联
这是C++ Visual Studio 2013中C++功能分析的第一个功能改进。C++编译器有能力在编译期间将内联信息添加到.PDB文件中(但默认情况下它不是增加的,因为它增加了.PDB的大小)。若要启用此操作,请浏览“ProjialProjks:>配置属性-> C/C++ +>命令行”,并将“/D2ZI+”添加到“附加选项”字段(对您希望在内联信息中的每个项目都这样做)。
注意:/d2Zi+不是C++编译器中正式记录的标志,这意味着它的未来支持不保证。 .
现在,用版本配置和概要文件重新构建应用程序。这一次,当报表打开时,导航到“调用树”视图
打开后,右键单击任何列标题并选择上下文菜单底部附近的“添加/删除列”。当“添加/删除列”对话框打开时,选择以“inline”开头的五个新列
- 内联函数 :显示已内联到此函数中的函数
- 内联包容性样本 :显示在当前函数或子函数中执行内联代码时发生的样本数
- 内联独占示例 :显示执行内联到此函数的代码时发生的样本数
- 内联包含样本%% :显示此方法相对于内联样本总数的内联包含样本总数。
- 内联独占示例%% :显示在内联到该函数的代码中收集的相对于内联样本总数的样本总数。
现在,当我展开热路径时,有一列显示了内联到getu primes中的函数,以及最初在单独函数中的代码中出现的样本的样本计数信息。
[ 出于上面截图的目的,我隐藏了所有默认列 ]
现在,如果我调整“Inlined Functions”列的大小使其更宽,这样我就可以读取所有内联到getu primes的函数,我会看到isu primes、hasu factors和isu factors都内联到getu primes,所以我现在知道这就是为什么它们没有出现在我的报告中。
评估剖面引导优化
我要强调的下一个新特性是如何使用CPU采样探查器来理解评测引导优化(PGO)培训的有效性。
当您使用PGO时,您通过运行您认为能够代表客户使用应用程序的方式的场景来培训应用程序。在培训期间,PGO会记录执行的代码以及执行频率,然后在编译期间,这些信息将用于帮助编译器做出有关如何优化应用程序的更明智的决策。
在这里强调训练阶段的重要性是很重要的。如果您在培训数据上犯了错误(例如,针对用户很少使用的场景进行培训),那么您实际上可能会损害客户的性能,因为PGO优化了最花时间执行的功能以提高速度,并优化不经常执行或花费很少时间调整大小的函数,以帮助缩小二进制文件。因此,如果您在培训期间从不使用昂贵的代码路径,那么PGO很可能会针对代码的大小对其进行优化。如果该代码被证明是在一个热路径的用户的性能实际上会比你从来没有尝试过PGO它。
考虑到这一点,问题是一旦您收集了培训数据并优化了应用程序,您如何知道培训数据收集正确并且能够代表应用程序的实际使用?为了演示profiler如何帮助实现这一点,我将右键单击“PrimeNumbers”项目,然后选择“Profile-Guided-Optimization->instrument”,为PGO培训的示例应用程序添加工具
插入指令后,我选择“Profile Guided Optimization->Run Instrumented/Optimized Application”开始收集训练数据。因为插入指令的应用程序将比正常运行慢,所以我将输入10K作为限制。一旦应用程序完成了训练运行,我返回到“Profile Guided Optimization”菜单并选择“Optimize”以基于训练数据构建优化的二进制文件。
现在我们已经收集了训练数据并基于它优化了二进制文件,让我们评估一下训练在优化热路径应用程序方面的效果。为此,我将通过右键单击PrimeNumbers项目并选择“Profile-Guided Optimization->Run Instrumented/optimized application”来启动优化的应用程序。启动后,我将通过选择“Analyze->profiler->attach/Detach”并选择PrimeNumbers.exe将探查器附加到它
[您也可以使用profiler启动项目,但是由于PGO是一种不同于标准版本的构建类型,因此您将收到关于重新构建的警告。您需要选择“Do not continue with build”(不要继续生成),否则将丢失优化的生成,然后在探查器显示消息“build failed,You like to launch your performance session”(生成失败,是否仍要启动性能会话)时选择“Yes”(是)
我将再次输入100K作为要计算的数字,当分析会话结束时,我将导航到调用树视图,右键单击列标题并选择“添加/删除列…”。这次我选择“ispgo”和“pgotype”列,并按列顺序将它们上移到模块名称的正下方。
- 是PGO吗 :指定函数是否为PGO’d
- PGO型 :指定函数的速度还是大小
当我展开热路径时,我看到PrimeNumbers.exe中的wmain是PGO’d,但是PrimeCalculator中的两个函数都不是PGO’d。因此,我很快就可以看到我没有训练PrimeCalculator.dll,因为我错过了在训练运行之前检测.dll。虽然这是一个简单的错误,但它说明了探查器如何快速显示您的训练数据是否收集正确。
所以让我们再试一次。使用“Profile Guided Optimization”上下文菜单,我将插入PrimeCalculator项目,然后插入PrimeNumbers项目,然后选择“Run Instrumented/Optimized Build”来收集新的训练运行。我再次输入10K作为极限,当它完成时,使用“Profile-Guided Optimization”菜单来优化PrimeCalculator和PrimeNumbers项目。现在,我将再次启动探查器,这次当报告出现时,导航到调用树视图。当热路径展开时,您可以看到getu primes现在显示为已被PGO’d,并且我在热路径中控制的所有函数都显示为已被PGO’d。
利用PGO类型列
上面的示例是一个简单的示例,说明了概要文件如何向您显示培训数据的状态。更常见的例子是当客户报告性能问题时,profiler将发挥作用。然后,您要么再现问题并分析场景,要么让他们为您收集性能跟踪。然后,您可以打开报表并查看报表中的热路径是否将您的代码主要显示为PGO类型的大小、速度甚至PGO’d。如果热路径没有显示任何为速度而配置的函数,这会让您知道需要更新您的培训以更多地使用此代码路径。如果代码路径显示了为提高速度而分析的函数,那么您需要对实现进行改进,而不是依赖PGO来提供所需的性能改进。
在这一点上,对于非常小的应用程序(如PrimeNumbers示例应用程序),PGO总是会优化所有的函数以提高速度,这是毫无价值的。一个应用程序需要包含大约6000条指令,然后PGO才能开始决定大小和速度。为了在下面说明这一点,我对附加到 “使用PGO构建更快、高性能的本机应用程序”博客文章 .
今天就开始分析
上面是一个非常基本的例子,说明了如何使用profiler来理解编译器优化和PGO培训。随时可以 下载此博客文章中使用的示例项目 ,希望您可以通过在Visual Studio 2013中尝试CPU探查器来了解如何将其应用于应用程序。请注意,探查器中的新PGO支持要求您在Windows 8或更高版本上进行探查,并且报告必须在Visual Studio 2013 Premium或更高版本中查看。
有关分析的更多信息,请参见 VisualStudioDiagnostics团队的博客 ,并在 Visual Studio诊断论坛 .