使用 -mfma 编译时的非法指令

Posted

技术标签:

【中文标题】使用 -mfma 编译时的非法指令【英文标题】:Illegal Instruction when compiling with -mfma 【发布时间】:2018-04-26 15:51:52 【问题描述】:

我在 Intel SandyBridge E5-2670 上使用 GCC 5.3.0 进行编译。当我使用这些标志-O3 -DEIGEN_NO_DEBUG -std=c++11 -Wall -Wextra -Werror -march=native -ffast-math 时,代码运行没有错误。当我添加-mfma 时,我得到了非法指令。

我认为使用-march=native 永远不会产生非法指令。我使用gdbbt 运行程序,但它显示了一个有效的(至少对我而言)堆栈,所以我不认为-mfma 暴露了错误的指针或其他内存问题。

#0  0x000000000043a59c in ConvexHull::SortConvexHull() ()
#1  0x000000000043badd in ConvexHull::ConvexHull(Eigen::Matrix<double, -1, -1, 0, -1, -1>) ()
#2  0x000000000040b794 in Group::BuildCatElement() ()
#3  0x0000000000416b60 in SurfaceModel::ProcessGroups() ()
#4  0x00000000004435c6 in MainLoop(Inputs&, std::ostream&) ()
#5  0x000000000040494e in main ()

然后我重新编译调试 (-O0 -g),所有其他选项都相同,gdb 回来了

0x00000000004140df in Eigen::internal::pmadd<double __vector(4)>(double __vector(4) const&, double __vector(4) const&, double __vector(4) const&) (a=..., b=..., c=...)
     at ./../eigen-eigen-5a0156e40feb/Eigen/src/Core/arch/AVX/PacketMath.h:178
178       __asm__("vfmadd231pd %[a], %[b], %[c]" : [c] "+x" (res) : [a] "x" (a), [b] "x" (b));

回溯显示错误从第 259 行开始

using namespace Eigen;
252 gridPnts.rowwise() -= gridPnts.colwise().mean(); //gridPnts is MatrixXd (X by 3)
253 Matrix3d S = gridPnts.transpose() * gridPnts;
254 S /= static_cast<double>(gridPnts.rows() - 1);
255 Eigen::SelfAdjointEigenSolver<MatrixXd> es(S);
256 Eigen::Matrix<double, 3, 2> trans;
257 trans = es.eigenvectors().block<3, 2>(0, 1);
258 MatrixXd output(gridPnts.rows(), 2);
259 output = gridPnts * trans;

使用-mfma 编译的目的是看看我是否可以提高性能。这是Eigen 中的错误还是我使用不正确?

【问题讨论】:

This 可能相关。在不相关的说明上;小心-ffast-math - 确保你知道它会使用什么技巧,以及你可能会因此得到错误的结果 - 它很有用如果你知道你在做什么,但要注意它正在破坏标准并且有一些棘手的陷阱 - 我个人的经验是,有限的性能提升不值得花时间追踪奇怪的错误。.. @JesperJuhl 感谢-ffast-math 上的 cmets。这里的练习是查看仅使用编译器选项可以找到哪些性能增益/损失。如果我们决定使用它,我们当然需要按照您的建议彻底审查它。 不客气。为了使用编译器选项获得安全的性能提升,我建议您对 -O2-O3-Os 进行基准测试,并且您绝对想研究 LTO 和 PGO - 最后两个 在我的情况下领先显着减少可执行文件/库的大小以及可测量的加速,并且所有这些都无需使用破坏标准提供的保证的技巧(尽管使用 LTO,您确实可以获得更长的构建时间)。 【参考方案1】:

要调试非法指令,您应该首先查看反汇编,而不是回溯或源代码。但是,在您的情况下,即使从源代码中您也可以很容易地看到违规(非法)指令是vfmadd231pd,它来自 FMA 指令集扩展。但是您拥有的 SandyBridge CPU 不支持此 ISA 扩展,因此通过在编译器中启用它,您会自食其果。

在 Linux 上,你可以通过这个 shell 命令检查你的 CPU 是否支持 FMA:

grep -q '\<fma\>' /proc/cpuinfo && echo supported || echo not supported

【讨论】:

【参考方案2】:

-mfma 将FMA instruction set 添加到允许的指令集。为此,您至少需要一个 Intel-Haswell 或 AMD-Piledriver CPU。

-march=native 之外添加-mInstructionSet 将永远不会有帮助 - 要么它已经被包含,要么它会允许编译器使用非法指令(在你的 CPU 上)。

【讨论】:

如果他的目标是提高机器性能,用-mtune=native 替换-march=native 会适得其反。

以上是关于使用 -mfma 编译时的非法指令的主要内容,如果未能解决你的问题,请参考以下文章

使用带有自定义对齐分配器实现的最新 g++ 使用 SSE 和 -O3 选项编译时出现非法指令(核心转储)

npm -v 和 node.js 在交叉编译的 nodejs0.12.2 上抛出非法指令

运行基本 Avx512 代码时获得非法指令

使用 Intel Core i7 的 AVX 上的非法指令

GCC非法指令

运行DPDK程序报错:非法指令 核心已转储