C++|VS2022中浮点收缩的变化

C++|VS2022中浮点收缩的变化

文章图片

C++|VS2022中浮点收缩的变化

在今天的文章中 , 我们将会介绍最新版的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 精度 。在这种情况下 , 可能会触发更多优化 。