在今天的文章中,我们将会介绍最新版的VS2022中的MSVC编译器的17.0版本的新变化,包括一项新特性,此特性会影响浮点收缩类指令,例如FMA(Fused Multiply Add)。我们还将介绍在VS2022之前的版本对FMA收缩指令的支持情况,另外,一个新的编译开关/fp:contract已经被加入到编译器中的浮点progmas中,它可以用来对收缩指令的生成做出更加精细的控制。
何谓收缩(Contraction)?
一个收缩操作,是指在源代码中的两个操作由可执行代码中的单个指令来执行。例如FMA操作和倒数平方根操作。前者计算 ((a * b) + c),而后者计算 (1/sqrt(a))。收缩的优点在于,它提升了涉及的计算速度并减少了应用程序的代码大小。你可能不想使用收缩的原因是,因为中间结果没有四舍五入,所以结果可能与你从单独的指令中得到的结果略有不同。 这通常不是问题,但我们特别关注我们所说的“精确”是什么意思,并且不想承诺我们没有实现的东西。 你可以控制是否使用收缩,这样就可以在需要精确度的时候禁用收缩来获得一致的结果,也可以在不需要的时候启用收缩。
VS2022之前的版本对收缩的支持情况
在VS2022之前的Visual Studio版本中,在/fp:precise的默认FP模式下,收缩操作所生成的代码是不一致的。 这种不一致出现在不同平台之间以及 FMA 的标量和矢量版本之间。 编译器可以在 ARM 和 ARM64 平台上生成 FMA 的标量和向量版本。 在支持 FMA 指令的 x86 和 x64 平台上,编译器只能生成向量 FMA 指令。 我们正在解决这种不一致的问题并更新 VS2022 中有关 FP 模式的文档。
VS2022对收缩的支持情况
尽管收缩往往会提高应用程序的性能,但它们可能会在调试和发布版本以及 ISA 目标(例如:SSE2 与 AVX2)之间产生不一致的结果,并可能导致破坏测试覆盖率中的现有假设。 为了解决这个问题,,从 VS2022 版本 17.0 开始,在所有平台上的 /fp:precise 模式下默认不会生成收缩。 我们引入了一个新的 /fp:contract 编译器开关,它可以与 /fp:precise 一起使用以启用收缩。 /fp:contract 开关将在所有平台上启用向量和标量收缩。 下图展示了/fp:contract和 /fp:precise的行为。请注意下图是针对VS2022来说的:
浮点编译指示(Progmas)的行为也被修改以与浮点标志的行为一致。 float_control 编译指示现在将在打开时禁用收缩,并在关闭时恢复先前的收缩设置。 这个新的行为已经被更新到 float_control、fenv_access 和 fp_contract pragma的文档中了。
使用这种新行为,可能会出现性能降级,因为默认情况下不再生成收缩。 添加 /fp:contract 标志应该可以缓解这种情况。 可以使用浮点编译指示在函数级别进一步控制收缩行为。
请注意,如果目标架构支持,诸如 fma、fmaf 和 fmal 等内在函数仍可用于生成 FMA 机器指令。
如何在VS2022中打开/fp:contract
为你的项目启用 /fp:contract,十分简单,步骤如下:
在 Visual Studio 中,在附加选项框中添加 /fp:contract 选项(项目|属性|配置属性|C/C++|命令行|附加选项)
由于收缩的生成是一种优化,添加 /fp:contract 标志可能不会为调试版本产生收缩。
如果你将项目从 VS2019 升级到 VS2022 并看到不同的浮点结果,那么应该检查以下事项:
> 如果你的代码是使用 /fp:fast 构建的,这可能是预期的行为。 /fp:fast 允许编译器更积极地优化事物,但会损失一些 FP 精度。 在这种情况下,可能会触发更多优化。
> 如果你的代码是使用 /fp:precise 构建的(或未指定 /fp 模型),请尝试抛出 /fp:contract 以确认收缩是否是 FP 更改的原因。 如果是,请查看你的场景继续抛出 /fp:contract 是否有意义。
> 如果你的代码是使用 /fp:strict 构建的,请告诉我们,可能存在编译器错误。
总结
浮点计算,我一生的痛。
猿友你也一样吗?
最后
Microsoft Visual C++团队的博客是我非常喜欢的博客之一,里面有很多关于Visual C++的知识和最新开发进展。大浪淘沙,如果你对Visual C++这门古老的技术还是那么感兴趣,则可以经常去他们那(或者我这)逛逛。
本文来自:《The /fp:contract flag and changes to FP modes in VS2022》