当将 exe 复制到另一台计算机(2022 Intel 处理器)时,在一台 Windows 计算机(2018 Intel 处理器)中编译的 Fortran 代码会给出不同的结果

Mil*_*ila 3 floating-point fortran intel intel-fortran fast-math

当将 exe 复制到另一台计算机(具有 2022 Intel 处理器)时,在一台 Windows 计算机中使用 Visual Studio 2019 在 2018 Intel 处理器上编译的 Fortran 代码是否可能会产生稍微不同的结果?您能否列出导致此行为的可能原因?

Pet*_*des 8

基于 CPU 指令集的运行时调度到不同版本的函数和/或到所有内核的自动并行化可能是候选方案。

\n

一些编译器优化假装 FP 数学是关联的。事实上,FP 数学并不具有很强的关联性。不同的临时值会引入不同的舍入误差。

\n

如何在点积或数组之和等循环中找到并行性(如自动向量化)的不同选择可能会导致不同的舍入,这可以使结果在数值上更准确,但与操作的源顺序不同1。此类循环只能通过假装 FP 数学具有关联性来进行矢量化。

\n
\n

具有不同数量内核的自动并行化(例如 OpenMP)可以以不同的方式将问题分解为不同的小计。如果您的程序使用所有核心,那么这是一个可能的候选者。

\n

英特尔编译器还可以根据可用的 SIMD 指令集来生成分派到函数的不同版本的代码。因此,您可以拥有 SSE4 版本、AVX2+FMA 版本和 AVX-512 版本(甚至可能使用 512 位向量。)

\n

如果循环使用相同数量的向量寄存器,则不同的 SIMD 宽度会导致不同数量的累加器。因此,这是一组不同的数字相加到小计中,例如点积。

\n

只有其中一个处理器具有 AVX-512 吗?或者其中之一是没有 AVX 的奔腾或赛扬?如果是这样,这也是一个可能的因素。

\n

TBB 或 SVML 等库函数中的运行时调度也可能是一个因素,而不仅仅是编译器直接生成的代码。

\n
\n

英特尔编译器 FP 数学选项

\n

英特尔编译器默认为-fp-model=fast. 请参阅英特尔 Fortran英特尔 C/C++ 编译器的文档(都是经典编译器,不是基于 LLVM 的 OneAPI,尽管它们可能-ffast-math默认打开)。C++ 编译器文档的描述似乎更详细。

\n

(其他主流编译器,如 LLVM 和 GCC ( gfortran),默认为-fno-fast-math。但是如果您使用 OpenMP,您可以让编译器将特定归约循环中的和或乘积或其他任何内容视为关联。)

\n

具体来说,英特尔默认值为fast=1,并且还有更激进的优化级别,-fp-model=fast=2默认情况下不启用。

\n

另请参阅英特尔 2018 年文章使用英特尔\xc2\xae 编译器的浮点结果的一致性\nor\n为什么我的应用程序\xe2\x80\x99t 总是给出相同的答案?,涵盖了英特尔编译器所做的值不安全优化的种类以及各种 FP 模型开关如何影响它。和/或2008 年同名演讲中的幻灯片。

\n

引用那篇文章的一些描述:

\n
    \n
  • -fp-model=consistent- 操作顺序不一定与源相同,但在每台机器上都相同。与 OpenMP 自动并行时运行间的一致性。否则,通过动态调度减少,添加小计的方式可能取决于哪个线程首先完成哪个工作。
  • \n
  • -fp-model=precise - 仅允许价值安全优化(但仍包括收缩a*b+c为 FMA)。
  • \n
  • -fp-model=strict- 允许访问 FPU 环境(例如,您可以更改 FP 舍入模式,编译器优化将尊重这种可能性)。
    \n禁用浮点压缩\n例如融合乘加 (fma) 指令\n意味着 \xe2\x80\x9c 精确\xe2\x80\x9d 和 \xe2\x80\x9c 除外
  • \n
\n

这部分可能不会导致不同机器之间的优化,但它很有趣:

\n
\n

ANSI Fortran 标准比 C 标准限制较少:它要求编译器遵循括号指定的计算顺序,但允许编译器按照其认为合适的方式对表达式进行重新排序。因此,英特尔\nFortran 编译器实现了相应的开关\n/assume:protect_parens (-assume protected_pa​​rens),这会导致重新关联的行为符合标准,对性能的影响比 /fp:precise (-fp-model precision )。除重新关联外,此开关不会影响\n任何值不安全的优化。

\n
\n
\n

脚注 1:从数字上看,不同并不总是更糟

\n

浮点数学总是存在舍入误差(除了极少数情况,例如添加 5.0 + 3.0 或尾数有足够尾随零的其他情况,因此1在移位尾数以对齐它们后无需舍入任何位)。按照源顺序和源指定精度进行 FP 数学运算得到的数字将具有舍入误差。

\n

使用多个累加器(通过向量化和展开归约)通常在数值上更好,这是朝着成对求和方向迈出的一步;sum += a[i]如果元素全部或大部分为正且大小统一,则简单是添加数组的最差方法。将小数与大数相加会损失很多精度,因此要对 16 或 32 个不同的存储桶进行求和意味着在将这些存储桶加在一起之前,总数不会那么大。另请参见\n Simd matmul 程序给出不同的数值结果

\n

您可以开玩笑地称这是错误的,因为它使验证/测试程序是否完全按照您的想法进行操作变得更加困难,并且因为它不是源所说的那样。

\n

但是,除非你正在做 Kahan 求和(误差补偿)或双精度(扩展精度)或其他依赖于精确 FP 舍入语义的数值技术,否则快速数学优化只会给你在不同的情况下错误的答案。比来源的方式,并且从数学上讲可能错误更少。

\n

(除非它还引入了一些近似值,例如rsqrtps+ 牛顿迭代而不是x / sqrt(y)。这可能只适用于fast=2,这不是默认值。但我不确定。一些优化也可能不关心变成-0.0+0.0

\n