g++: optimization -march=haswell and newer changes 数值结果

g++: optimization -march=haswell and newer changes numerical result

当我注意到 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 处理器上执行。

这是因为 GCC 默认使用融合乘加 (fma) 指令(如果可用)。相反,Clang 默认情况下不使用它们,即使它可用。

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

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

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