在生成时对函数内联进行故障排除
使用 Build Insights 函数视图来排查函数内联对 C++ 项目中生成时间的影响。
先决条件
- Visual Studio 2022 17.8 或更高版本。
- 如果安装使用 C++ 的桌面开发工作负载或使用 C++ 的游戏开发工作负载,则默认启用 C++ Build Insights。
显示已安装组件的列表。 C++ Build Insights 突出显示并被选中,这意味着它已安装。
显示已安装组件的列表。 C++ Build Insights 突出显示并被选中,这意味着它已安装。
概述
Build Insights 现已集成到 Visual Studio 中,可帮助你优化生成时间,特别是对于 AAA 游戏等大型项目。 Build Insights 提供诸如函数视图之类的分析功能,可帮助诊断生成期间昂贵的代码生成。 它显示为每个函数生成代码所需的时间,并显示 __forceinline
的影响。
__forceinline
指令指示编译器内联函数,无论其大小或复杂度如何。 内联函数可以通过减少调用函数的开销来提高运行时性能。 但这样做的代价是,它会增加二进制文件的大小并影响生成时间。
对于优化生成,生成代码所花费的时间对总生成时间有重大影响。 一般来说,C++ 函数优化可以很快完成。 在特殊情况下,某些函数会变得足够大且足够复杂,从而给优化程序带来压力,并明显降低生成速度。
本文介绍如何使用 Build Insights 函数视图查找生成中的内联瓶颈。
设置生成选项
若要度量 __forceinline
的结果,请使用“发布”生成,因为调试生成不会内联 __forceinline
,这是因为调试生成使用 /Ob0
编译器开关,这会禁用该优化。 为“发布”和 x64 设置生成:
- 在“解决方案配置”下拉列表中,选择“发布”。
- 在“解决方案平台”下拉菜单中,选择“x64”。
将优化级别设置为最大优化:
在解决方案资源管理器中,右键单击项目名称,然后选择“属性”。
在项目属性中,导航到“C/C++”>“优化”。
将“优化”下拉菜单设置为“最大优化(支持速度)(
/O2
)”。单击“确定”,关闭对话框。
运行 Build Insights
在所选项目上,使用上一节中设置的“发布”生成选项,通过从主菜单中选择“生成”>“在选择时运行 Build Insights”>“重新生成”来运行 Build Insights。 也可以右键单击解决方案资源管理器中的项目,然后选择运行“Build Insights”>“重新生成”。 选择“重新生成”而不是“生成”来衡量整个项目(而不仅仅是当前可能存在异常的几个文件)的生成时间。
生成完成后,将打开一个事件跟踪日志 (ETL) 文件。 它保存在 Windows TEMP
环境变量指向的文件夹中。 生成的名称基于收集时间。
“函数”视图
在 ETL 文件的窗口中,选择“函数”选项卡。它显示已编译的函数以及为每个函数生成代码所花费的时间。 如果为函数生成的代码量可以忽略不计,则它将不会出现在列表中,以免降低生成事件的收集性能。
在“函数名称”列中,performPhysicsCalculations() 突出显示并标有火焰图标。:::
“时间[秒, %]”列显示以挂钟责任时间 (WCTR) 编译每个函数所花费的时间。 此指标根据函数对并行编译器线程的使用情况在函数之间分配挂钟时间。 例如,如果两个不同的线程在一秒内同时编译两个不同的函数,则每个函数的 WCTR 记录为 0.5 秒。 各函数在总编译时间中所占的比例份额反映了这一点,并且考虑了每个函数在并行执行期间消耗的资源。 因此,WCTR 可以更好地衡量每个函数在同时发生多个编译活动的环境中对整体生成时间的影响。
“Forceinline 大小”列大致显示为该函数生成了多少条指令。 单击函数名称前面的 V 形图标可查看该函数中扩展的各个内联函数以及每个函数大致生成了多少条指令。
可以通过单击“时间”列对列表进行排序,以查看哪些函数编译时间最长。 “火焰”图标表示生成该函数的成本很高,值得调查。 过度使用 __forceinline
函数会显著减慢编译速度。
可以使用“筛选器函数”框搜索特定函数。 如果函数的代码生成时间太短,则不会显示在“筛选器函数”中。
通过调整函数内联来缩短生成时间
在此示例中,performPhysicsCalculations
函数的编译时间最长。
在“函数名称”列中,performPhysicsCalculations() 突出显示并标有火焰图标。
进一步调查,通过选择该函数前面的 V 形,然后从最高到最低对 Forceinline 大小列进行排序,我们可以看到导致问题的最大因素。
performPhysicsCalculations() 展开并显示其中内联的一长串函数。 显示 complexOperation()、recursiveHelper() 和 sin() 等多个函数的实例。 Forceinline Size 列显示 complexOperation() 是最大的内联函数,有 315 条指令。 recursiveHelper() 有 119 条指令。 Sin() 有 75 条指令,但它的实例比其他函数多得多。
有一些较大的内联函数,例如 Vector2D<float>::complexOperation()
和 Vector2D<float>::recursiveHelper()
,它们导致了问题。 但是 Vector2d<float>::sin(float)
、Vector2d<float>::cos(float)
、Vector2D<float>::power(float,int)
和 Vector2D<float>::factorial(int)
还有更多实例(此处并未全部显示)。 将它们加起来,生成的指令总数很快就会超过几个较大的生成函数。
查看源代码中的这些函数,我们发现执行时间将花在循环内。 例如,以下是 factorial()
的代码:
static __forceinline T factorial(int n)
{
T result = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 0; j < i; ++j) {
result *= (i - j) / (T)(j + 1);
}
}
return result;
}
与函数本身的成本相比,调用此函数的总体成本可能微不足道。 当调用函数(将参数推送到堆栈、跳转到函数、弹出返回参数以及从函数返回)所花费的时间与执行函数所花费的时间大致相同,并且函数被多次调用时,将函数设为内联是最有益的。 如果不是这种情况,将其设为内联可能会导致收益降低。 我们可以尝试从中删除 __forceinline
指令,看看是否有助于缩短生成时间。 power
、sin()
和 cos()
的代码相似,因为代码由一个将执行多次的循环组成。 我们也可以尝试从这些函数中删除 __forceinline
指令。
我们通过从主菜单中选择“生成”>“选择时运行 Build Insights”>“重新生成”来重新运行 Build Insights。 也可以右键单击解决方案资源管理器中的项目,然后选择运行“Build Insights”>“重新生成”。 像以前一样,我们选择“重新生成”而不是“生成”来度量整个项目(而不仅仅是当前可能存在异常的几个文件)的生成时间。
生成时间从 25.181 秒减少到 13.376 秒,并且 performPhysicsCalculations
函数不再显示在“函数”视图中,因为它对生成时间的影响不足以被计算。
在“函数名称”列中,performPhysicsCalculations() 突出显示并标有火焰图标。:::
诊断会话时间是生成所需的总时间加上收集 Build Insights 数据的任何开销。
下一步是分析应用程序,以查看应用程序的性能是否受到更改的负面影响。 如果是,我们可以根据需要有选择地添加 __forceinline
。
导航到源代码
在“函数”视图中的文件上双击、右键单击或按 Enter 以打开该文件的源代码。
提示
- 可以在“文件”>“另存为”,将 ETL 文件保存到更永久的位置,以保留生成时间的记录。 然后,可以将其与将来的生成进行比较,以查看更改是否正在改善生成时间。
- 如果不小心关闭了 Build Insights 窗口,请通过在临时文件夹中找到
<dateandtime>.etl
文件将其重新打开。TEMP
Windows 环境变量提供了临时文件文件夹的路径。 - 若要使用 Windows 性能分析器 (WPA) 深入研究 Build Insights 数据,请单击 ETL 窗口右下角的“在 WPA 中打开”按钮。
- 拖动列以更改列的顺序。 例如,你可能希望将“时间”列移至第一列。 可以通过右键单击列标题并取消选择不想看到的列来隐藏列。
- “函数”视图提供了一个筛选框,用于查找你感兴趣的函数。 它针对你提供的名称执行部分匹配。
- 如果你忘记了如何解释“函数”视图试图向你显示的内容,请将鼠标悬停在选项卡上以查看描述视图的工具提示。 如果将鼠标悬停在“函数”选项卡上方,工具提示将显示:“显示子节点为强制内联函数的函数统计信息的视图。”
故障排除
- 如果未显示“生成见解”窗口,请重新生成而不是生成。 如果未实际生成任何内容,则不会出现 Build Insights 窗口;如果自上次生成以来没有更改任何文件,则可能出现这种情况。
- 如果“函数”视图未显示任何函数,则表示你可能没有使用正确的优化设置进行生成。 确保使用完整优化生成发布,如设置生成选项中所述。 此外,如果函数的代码生成时间太短,则它不会出现在列表中。
另请参阅
生成见解提示和技巧
内联函数 (C++)
更快的 C++ 生成,已简化:时间的新度量标准
Visual Studio 中的 Build Insights 视频 - Pure Virtual C++ 2023
排查头文件对生成时间的影响
Visual Studio 2022 17.8 中的 Build Insights 的函数视图
教程:vcperf 和 Windows Performance Analyzer
使用 C++ 生成见解缩短代码生成时间