C++|C++工程编译时间分析

C++|C++工程编译时间分析

文章图片

C++|C++工程编译时间分析

文章图片

C++|C++工程编译时间分析

导读来自Mercury Steam公司的工程师Carlos Fraguas最近分享了一篇博文 。
他向我们描述了如何通过C++ Build Insights SDK来向vcperf中添加新的/timetrace选项 , 另外还描述了他是如何使用这套SDK来制作工具来满足他的开发团队的需求的 。
今天就让我们来观摩观摩 。
背景大家好 , 我的名字叫Carlos Fraguas , 来自Mercury Steam公司 , 是一名游戏开发工程师 。
今天我分享一下我是如何利用C++ Build Insights SDK来帮助我创建工具 , 从而减少工程编译时间的 。 我还会演示如何向vcperf中添加一个新的可视化选项 。
有那么一件事情是程序员们最讨厌的:等待工程编译完成 。 为什么?大家看看下面的漫画就能明白 。


来源:XKCD.com
但是有时候坐在那里等待工程编译完成 , 确实是一件很令人沮丧的事情 。 长时间的工程编译会影响产品功能的开发和架构迭代(例如 , 对工程代码进行重构就十分耗费时间) , 所以我们认为:对开发团队来说 , 保持尽可能短的开发迭代的时间是很关键的 。
不久之前 , 我开始担心工程编译慢的问题并尝试找一些工具 , 看看能不能找到工程编译的时间瓶颈 。 直到C++ Build Insights SDK和vcperf发布 , 我才真正地在提升工程编译速度的事情上有了一些进展 。
尽管 , 曾经有一个问题 , 当时我还刚刚开始研究 , 相对于我们团队的其他同事来说 , 我还不是十分熟悉MSVC这套东西 。 而vcperf的出现了 , 它以可视化的界面来呈现工程编译过程 。 我能从这些图表中看到编译器和链接器是怎样工作的 , 以及它们的在时间上的花销 。
后来 , SDK发布了 , 我想着我可以自己试试这套东西 , 然后为我的开发团队创建一些自定义报表之类的 。 出乎意料的是 , 整个过程非常轻松 。 我们得到了我们想要的数据 。 另外 , 我成功地添加了一个新的可视化输出到vcperf中 , 真是棒极了 。
/timetrace新的/timetrace选项可以将工程编译可视化地呈现在浏览器中 , 谷歌的或Microsoft Edge都可以 。
下面是一些关键特性
> 可视化调用层级:相关的操作会显示在一起 , 方便查看 。
> 显示了编译前端和后端实际执行的工作:哪些头文件被包含了或者代码生成过程的时间消耗 。
> 将耗时较长的函数和模板实例化凸显出来:如果某个函数或者模板在图表上 , 那可能是一个问题 。
下面是一个编译图表的例子:

下图是图表中的单个文件视图:

下图显示了模板实例化的视图:

以下为生成这些图表的步骤:
> 克隆vcperf工程
> 获取工程编译的一份trace信息:
> 以管理员身份打开命令行
> 执行: vcperf /start SessionName
> 在Visual Studio或者其他地方编译你的工程
> 执行:vcperf /stop SessionName /timetrace outputFile.json
> 上述指令会停止信息收集并将trace信息保存到输出文件 。
> 自浏览器中输入edge://tracing , 打开并查看trace信息 。
以下是/timetrace选项具体的工作原理:
> 加载trace
> 订阅SDK里暴露的OnStartActivity和OnStopActivity事件来构建工程编译层级和父子关系 。
> 使用过滤器来过滤掉不相关的数据 。 (例如 , 有些函数编译地相当快 , 它不怎么耗费时间 , 所以就不是我们需要关心的)
> 计算进程和线程ID来保存调用层级(这里不使用原始ID) 。
> 按照预期格式进行结果输出 。
SDK如何帮助了我们?仅仅是使用C++ Build Insights SDK来可视化工程编译过程是远远不够的 。 我们还成功地制作了一些小工具来帮助我们的开发团队来分析将来开发的项目 。 下面是一些要点:
> 通过分析我们发现:编译器默认定义的某些默认构造函数会占据整个编译时间的大约3% 。 那个头文件被包含了几百次所以每次包含都会触发编译器定义构造函数 。 最终 , 我们将这个头文件实现为一个cpp文件并将包含次数降低为1次 。
> 我们检测到在头文件中不是非常必要的#include语句 。 这些包含语句又进一步地包含其他很多头文件 , 导致编译前端耗费相当多的时间进行处理 。 通过使用前置声明 , 我们减少了头文件之间的耦合 。
> 通过.dgml格式 , 我们在工程代码基础上创建了包含视图 , 这个可视化的结果视图十分有帮助 。
> 通过对所有的源文件采用统一的编译选项(除了PCH) , 我们减少编译单元并有效提升了工程的编译并发度 。 特别是我们发现有两个源文件创建了相同名称的obj文件 , 我们通过将ObjectFileName属性设置为$(IntDir)\\%(RelativeDir)\\来解决了这个问题 。
但是 , 这种方法创建和文件夹同样多的编译单元 , 通过将其中的一个源文件重命名并恢复ObjectFileName属性 , 这样就只会有一个编译单元 。
> SDK向我们发出了警告:某些第三方库没有使用/MP进行编译 。 这告诉我们 , 有机会将它们转为并发编译 。
> 我们发现模板类需要很长时间进行代码生成 , 所以我们可以在适当的时候对它们进行重构 。
可以看到 , SDK给出的信息在缩短编译时间方面 , 确实十分有帮助 。
总结感谢C++ Build Insights SDK和vcperf工具的开发团队 , 有了这套工具 , 我们可以深入的分析影响工程编译的具体原因 , 从而提升编译速度 , 这样 , 开发团队就会有更多时间关注在功能开发上 。
正所谓:天下武功 , 唯快不破 。
最后Microsoft Visual C++团队的博客是我非常喜欢的博客之一 , 里面有很多关于Visual C++的知识和最新开发进展 。
【C++|C++工程编译时间分析】大浪淘沙 , 如果你对Visual C++这门古老的技术还是那么感兴趣 , 则可以经常去他们那(或者我这)逛逛 。
本文来自:《Introducing vcperf /timetrace for C++ build time analysis》


    推荐阅读