【问题标题】:g++: optimization -march=haswell and newer changes numerical resultg++:优化 -march=haswell 和更新的变化数值结果
【发布时间】:2018-01-03 07:57:23
【问题描述】:

当我注意到 g++ 似乎会根据选择的优化改变结果时,我一直致力于优化性能,当然还进行了回归测试。到目前为止,我认为-O2 -march=[whatever] 应该为数值计算产生完全相同的结果,无论选择什么架构。然而,这似乎不是 g++ 的情况。虽然使用一直到 ivybridge 的旧架构产生的结果与 clang 对任何架构的结果相同,但对于 haswell 和更新的 gcc,我得到不同的结果。这是 gcc 中的错误还是我误解了一些关于优化的问题?我真的很吃惊,因为 clang 似乎没有表现出这种行为。

请注意,我很清楚这些差异在机器精度范围内,但它们仍然干扰了我的简单回归检查。

下面是一些示例代码:

#include <iostream>
#include <armadillo>

int main(){
    arma::arma_rng::set_seed(3);
    arma::sp_cx_mat A = arma::sprandn<arma::sp_cx_mat>(20,20, 0.1);
    arma::sp_cx_mat B = A + A.t();
    arma::cx_vec eig;
    arma::eigs_gen(eig, B, 1, "lm", 0.001);
    std::cout << "eigenvalue: " << eig << std::endl;
}

编译使用:

g++ -march=[architecture] -std=c++14 -O2 -o test example.cpp -larmadillo

gcc 版本:6.2.1

clang 版本:3.8.0

针对 64 位编译,在 Intel Skylake 处理器上执行。

【问题讨论】:

  • 你编译的是 32 位还是 64 位?
  • 我记得使用英特尔编译器时遇到过这样的问题,这在 Haswell 上也给出了不同的结果。在我从事的项目中,英特尔的某个人给了我们一大堆编译器标志,基本上是为了不使用编译器提供的完整优化。我不知道标志了,但我记得它是在-O3。请注意,英特尔编译器(通常)使用与 GCC 编译器相同的标志。
  • 我之前听说过与 Intel 编译器的不同之处,并且有充分的理由避免使用 -O3。令我惊讶的是 gcc 给出的结果与 gcc 不同...尤其是 gcc -march=core2,sandybridge,ivybridge 给出的结果与 clang -march=core2,sandybridge,ivybridge,haswell,broadwell 相同。
  • 我认为这是由于 fused-multiply-add 指令而发生的。如果你使用-mno-fused-add,你能重现这些差异吗?
  • @geza:你是对的。如果我使用-mno-fused-madd,我会消除差异(但会收到弃用警告;-))。那么这是否意味着 clang 不使用 fused-multiply-add,即使我使用 -O3-march=haswell?在任何情况下,我都没有在我的项目中观察到 fused-multiply-add 带来的任何实际速度提升。

标签: c++ g++ compiler-optimization gcc6


【解决方案1】:

这是因为 GCC 默认使用 fused-multiply-add (fma) 指令,如果它可用的话。相反,Clang 默认不使用它们,即使它可用。

a*b+c 的结果可以differ 无论是否使用 fma,这就是为什么当您使用 -march=haswell 时会得到不同的结果(Haswell 是第一个支持 fma 的 Intel CPU)。

您可以决定是否要通过-ffp-contract=XXX 使用此功能。

  • -ffp-contract=off,你不会得到 fma 指令。
  • -ffp-contract=on,你会得到 fma 指令,但只有在语言标准允许的情况下才会收缩。在当前版本的 GCC 中,这意味着关闭(因为它尚未实现)。
  • -ffp-contract=fast(这是 GCC 的默认设置),您将获得 fma 指令。

【讨论】:

  • 一般-O3及以上可以重新排序操作(“a+b+c+d”严格按照语言“(((a+b)+c)+d)”但-O3允许编译器将其重写为“(a+b)+(c+d)”,它执行得更快,但会产生数值上不同的结果(固定精度 FP)。FMA 略有不同,因为 ab+c WITHOUT FMA 首先计算 ab,然后在添加 c 之前将其截断为 64 位,但 FMA 在添加 c 之前不会截断乘积。因此,关于不重新排序操作的 -O2 规则不禁止使用它,但它会产生不同的数值结果。
  • @Tim:-O3 真的是这样吗?我在哪里可以读到这个?乍一看,我认为无论优化级别如何,都禁止重新排序添加。
  • 啊,这些天在 gcc 中是 -ffast-math 由 -Ofast 而不是 -O3 (gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html) 启用,但通常在 gcc 和 clang 等现代规则之前跨越多年的不同编译器,民间规则是 -O2 将是安全且不会改变结果的优化限制。 MSVC 中的类似行为是 /fp:fast 我的评论只是说 FMA 与 -ffast-math 不完全相同,因为它不涉及重新排序操作,但它实际上主要是由保持精度的需要驱动的否则丢弃。
  • 好吧,-Ofast-O3 完全不同,正是因为它包含-ffast-math。我认为优化级别 -O[1-X] 永远不允许重新排序浮点运算。是的,FMA 不是-ffast-math,这是真的 :) 但是应用它确实会改变程序的行为。实际上,对我来说,奇怪的是 GCC 的默认值是-ffp-contract=fast。它可能会破坏以前很好的程序。
  • 编译器仍然会尊重优先级(即在添加之前的 mult)顺序,但它可以假装 FP 数学是关联的(实际上标志可能是 -fassociative-math),当然,它不是在固定精度下工作时。请注意,重新排序的版本在数学上并不是“不太准确”,取决于所涉及的值可能更准确,但是虽然重新排序确实违反了 C++ 的严格规则,但许多人发现这是一个有用的优化(您可以简单地自己为表达式添加括号以获得相同的结果和性能提升)。
猜你喜欢
  • 2013-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-10
  • 1970-01-01
  • 2011-12-27
  • 2012-02-04
  • 2020-03-29
相关资源
最近更新 更多