C ++内层改进:Zipliner

Visual Studio 2019 版本16.3和16.4包括对C ++内衬的改进。 其中包括在对某些例程进行优化后内联的功能,称为“ Zipliner”。根据您的应用程序,您可能会看到一些次要的代码质量改进和/或主要的构建时间(编译器吞吐量)改进。


C2内衬


Terry Mahaffey概述了Visual Studio的内联决策 。 这详细介绍了衬板的一些约束条件和需要改进的方面,其中一些与此处特别相关:

  1. 内联函数是递归的,并且可能经常重新做已经完成的工作。 内联决策是上下文相关的,重播针对相同功能的决策并不总是有利可图的。
  2. 内衬非常注重预算。 它很难平衡可执行文件的大小和运行时的性能。
  3. 内线世界的观点始终是“预先优化的”,例如,对复制传播和无效控制路径的了解非常有限。

现代C ++


不幸的是,繁重的通用编程中常见的许多编码模式和习惯用法都遇到了这些限制。 考虑一下Eigen库中的以下例程:

Eigen::Matrix<float,-1,1,0,-1,1>::outerStride(void) 

调用innerSize:

 template<typename Derived> class DenseBase ... Index innerSize() const {    return IsVectorAtCompileTime ? this->size()        : int(IsRowMajor) ? this->cols() : this->rows(); } 

outsideStride的实例化只返回其成员之一。 因此,它是全面内联扩展的极佳选择。 为了实现这一目标,尽管编译器必须针对模块中externalStride的每个调用站点完全评估和扩展externalStride的18个被调用者。 这会吞噬优化器的吞吐量以及内联代码大小的预算。 还值得一提的是,即使对行和静态列的调用,它们对行和列的调用也都进行了内联扩展。
如果优化器仅内联两行成员返回,那就更好了:

 ?outerStride@?$Matrix@N$0?0$0?0$0A@$0?0$0?0@Eigen@@QEBA_JXZ PROC ; Eigen::Matrix<double,-1,-1,0,-1,-1>::outerStride, COMDAT mov    rax, QWORD PTR [rcx+8]    ret 0 

内联优化IR


对于例程的子集,内联函数现在将扩展例程的本已优化的IR,从而绕过获取IR和重新扩展被调用者的过程。 这具有双重目的,即可以更快地扩展呼叫站点,还可以让内线用户更准确地衡量其预算。

首先,优化器将总结出,在最初编译时,outerStride是此更快扩展的候选对象(请记住,c2.dll尝试在调用者之前编译例程)。 然后,内联函数可以使用字段访问替换对该externalStride实例的调用。

这种更快的内联扩展的候选对象是没有局部变量的叶函数,它们最多引用两个不同的参数,全局变量或常量。 实际上,这是针对最简单的获取器和设置器的。

好处


Eigen库中有很多示例,例如externalStride,其中大型调用树仅扩展为一条或两条指令。 大量使用Eigen的模块可能会显着提高吞吐量。 我们测量了优化程序,最多可减少25-50%的此类再现时间。

新的Zipliner还将使衬板能够更准确地衡量其预算。 Eigen开发人员早就意识到MSVC不符合其规范(请参阅EIGEN_STRONG_INLINE)。 Zipliner应该有助于减轻这种担忧,因为现在使用带拉链的例程被视为实际上是“免费”的内联。

试试看功能


默认情况下,此选项在Visual Studio 2019 16.3中已启用,并在16.4中进行了一些改进。 请下载Visual Studio 2019并尝试新的改进。 可以通过以下评论或通过电子邮件(visualcpp@microsoft.com)与我们联系。 如果您在使用Visual Studio或MSVC时遇到问题,或有对我们的建议,请通过帮助>发送反馈>报告问题/在产品中提供建议或通过Developer Community告诉我们。 您也可以在Twitter( @VisualC )上找到我们。

Source: https://habr.com/ru/post/zh-CN483336/


All Articles