将 Eigen 与“-march=native”一起使用时,导致此内存访问冲突错误 (0xC0000005) 的原因是什么?
What is causing this memory access violation error (0xC0000005) when using Eigen with "-march=native"?
我正在代码块中重写一些 c++ 代码(最初作为 MEX 函数在 Matlab 中编写),以便我可以使用为 c++ 设计的调试和分析工具。我正在重写的代码使用 Eigen 和 SIMD 内部指令,因此我需要使用 -march=native
标志进行编译。当 运行 我的主项目时,我遇到了内存访问冲突错误。这是导致问题的代码的简化版本:
#include <iostream>
#include <fstream>
#include <string>
#include <sys/stat.h>
#include <immintrin.h>
#include <Eigen/Dense>
#include "Parameters.h"
using namespace std;
int main()
{
Parameters p;
p.na = 16;
p.TXangle = Eigen::VectorXd::LinSpaced(p.na,0,p.na-1);
cout << p.TXangle << endl;
cout << "Hello world!" << endl;
return 0;
}
其中参数是使用以下两个文件定义的自定义 class:
Parameters.h
#ifndef PARAMETERS_H_INCLUDED
#define PARAMETERS_H_INCLUDED
class Parameters
{
public:
int na;
Eigen::VectorXd TXangle;
Parameters();
};
#endif // PARAMETERS_H_INCLUDED
Parameters.cpp
#include <string>
#include <Eigen/Dense>
#include "Parameters.h"
Parameters::Parameters()
{
//ctor
}
断开的行是在初始化 p.TXangle 时。此时,程序抛出 (0xC0000005) 错误。如果我不使用“-march=native”进行编译,那么错误就不会发生并且程序运行正常。使用“-march=native”构建时,我还会收到几个对齐警告。我的计算机最多支持 AVX2 指令,并且我正在使用 MinGW GCC 进行编译(不确定如何检查代码块上的 gcc 版本)。
gcc 版本为 8.1.0
更新:
这是@Sedenion 在评论中询问的价值吗?
这是调试器停止的确切行:
**更新:**
根据评论中的讨论,反汇编程序在失败时显示代码在这条汇编指令处:
我很难理解这个,阅读汇编对我来说还是有点新鲜。这是同一点的寄存器:
我使用 Eigen 3.4.0 和 mingw(gcc 8.1.0 with -mavx -m64 -std=c++17 -g
)在 Windows 上使用 AVX(-mavx
,也由 -march=native
用于 OP)。正如评论中的人们已经怀疑的那样,mingw-gcc 无法将堆栈变量正确对齐到 32 字节,这肯定是 AVX 所要求的问题(比较 bug issue for gcc, also see e.g. )。
崩溃 与 VectorXd
的使用无关 (由于它使用动态内存分配,因此不应受到影响)。相反, Eigen::VectorXd::LinSpaced()
调用是问题所在。在 Eigen 中,这最终会调用以下涉及 PacketMath.h 中的 AVX 指令的函数:
template<> EIGEN_STRONG_INLINE Packet4d plset<Packet4d>(const double& a) {
return _mm256_add_pd(_mm256_set1_pd(a), _mm256_set_pd(3.0,2.0,1.0,0.0));
}
在这个调用中,涉及临时堆栈变量,它们不是 mingw 对齐的 32 字节。有一次,一个对齐的 mov vmovapd
被尝试到这样一个 non-aligned 地址:
mov rax,QWORD PTR [rbp+0x10]
vmovapd YMMWORD PTR [rax],ymm0
例如,在一个 运行 中,我得到的 rax=0x67f890
只有 16 字节而不是 32 字节对齐。捕获行为的最小可重现示例如下(https://godbolt.org/z/qbE6z1nb8,请注意 godbolt 不支持 mingw,因此问题不会出现在那里):
#include <iostream>
#include <immintrin.h>
__m256d Set(const double&) {
__m256d temp = _mm256_setzero_pd(); // Crashes on mingw
return temp;
}
int main() {
Set(2);
std::cerr << "End" << std::endl;
}
Set()
的未使用参数只是为了在堆栈上获取偏移量以触发问题。
它在使用 mingw-gcc(使用 -mavx -m64
)时崩溃,但是在 Windows 上使用 clang 或 MSVC 编译时 运行 没问题。它也 运行 在我试过的所有 Linux 编译器上都很好。
所以,简而言之,你的代码是正确的,崩溃发生在Eigen。 no "magic switch" for gcc 可以解决这个问题。因此,我猜你有 3 个选择:
- 等待 mingw problem 修复。根据帖子,它仍然存在于 gcc 11.2.0 中。但考虑到历史悠久,我怀疑它会很快得到修复。
- 不要使用 AVX 或更高版本(或
-march=native
)编译,而是坚持使用 <=SSE4.2。当然,这 可能 影响性能。我建议进行分析以确定您是否属于这种情况。
- 使用其他编译器,例如 clang 或 MSVC。
我正在代码块中重写一些 c++ 代码(最初作为 MEX 函数在 Matlab 中编写),以便我可以使用为 c++ 设计的调试和分析工具。我正在重写的代码使用 Eigen 和 SIMD 内部指令,因此我需要使用 -march=native
标志进行编译。当 运行 我的主项目时,我遇到了内存访问冲突错误。这是导致问题的代码的简化版本:
#include <iostream>
#include <fstream>
#include <string>
#include <sys/stat.h>
#include <immintrin.h>
#include <Eigen/Dense>
#include "Parameters.h"
using namespace std;
int main()
{
Parameters p;
p.na = 16;
p.TXangle = Eigen::VectorXd::LinSpaced(p.na,0,p.na-1);
cout << p.TXangle << endl;
cout << "Hello world!" << endl;
return 0;
}
其中参数是使用以下两个文件定义的自定义 class:
Parameters.h
#ifndef PARAMETERS_H_INCLUDED
#define PARAMETERS_H_INCLUDED
class Parameters
{
public:
int na;
Eigen::VectorXd TXangle;
Parameters();
};
#endif // PARAMETERS_H_INCLUDED
Parameters.cpp
#include <string>
#include <Eigen/Dense>
#include "Parameters.h"
Parameters::Parameters()
{
//ctor
}
断开的行是在初始化 p.TXangle 时。此时,程序抛出 (0xC0000005) 错误。如果我不使用“-march=native”进行编译,那么错误就不会发生并且程序运行正常。使用“-march=native”构建时,我还会收到几个对齐警告。我的计算机最多支持 AVX2 指令,并且我正在使用 MinGW GCC 进行编译(不确定如何检查代码块上的 gcc 版本)。
gcc 版本为 8.1.0
更新: 这是@Sedenion 在评论中询问的价值吗?
这是调试器停止的确切行:
**更新:** 根据评论中的讨论,反汇编程序在失败时显示代码在这条汇编指令处:
我很难理解这个,阅读汇编对我来说还是有点新鲜。这是同一点的寄存器:
我使用 Eigen 3.4.0 和 mingw(gcc 8.1.0 with -mavx -m64 -std=c++17 -g
)在 Windows 上使用 AVX(-mavx
,也由 -march=native
用于 OP)。正如评论中的人们已经怀疑的那样,mingw-gcc 无法将堆栈变量正确对齐到 32 字节,这肯定是 AVX 所要求的问题(比较 bug issue for gcc, also see e.g.
崩溃 与 VectorXd
的使用无关 (由于它使用动态内存分配,因此不应受到影响)。相反, Eigen::VectorXd::LinSpaced()
调用是问题所在。在 Eigen 中,这最终会调用以下涉及 PacketMath.h 中的 AVX 指令的函数:
template<> EIGEN_STRONG_INLINE Packet4d plset<Packet4d>(const double& a) {
return _mm256_add_pd(_mm256_set1_pd(a), _mm256_set_pd(3.0,2.0,1.0,0.0));
}
在这个调用中,涉及临时堆栈变量,它们不是 mingw 对齐的 32 字节。有一次,一个对齐的 mov vmovapd
被尝试到这样一个 non-aligned 地址:
mov rax,QWORD PTR [rbp+0x10]
vmovapd YMMWORD PTR [rax],ymm0
例如,在一个 运行 中,我得到的 rax=0x67f890
只有 16 字节而不是 32 字节对齐。捕获行为的最小可重现示例如下(https://godbolt.org/z/qbE6z1nb8,请注意 godbolt 不支持 mingw,因此问题不会出现在那里):
#include <iostream>
#include <immintrin.h>
__m256d Set(const double&) {
__m256d temp = _mm256_setzero_pd(); // Crashes on mingw
return temp;
}
int main() {
Set(2);
std::cerr << "End" << std::endl;
}
Set()
的未使用参数只是为了在堆栈上获取偏移量以触发问题。
它在使用 mingw-gcc(使用 -mavx -m64
)时崩溃,但是在 Windows 上使用 clang 或 MSVC 编译时 运行 没问题。它也 运行 在我试过的所有 Linux 编译器上都很好。
所以,简而言之,你的代码是正确的,崩溃发生在Eigen。 no "magic switch" for gcc 可以解决这个问题。因此,我猜你有 3 个选择:
- 等待 mingw problem 修复。根据帖子,它仍然存在于 gcc 11.2.0 中。但考虑到历史悠久,我怀疑它会很快得到修复。
- 不要使用 AVX 或更高版本(或
-march=native
)编译,而是坚持使用 <=SSE4.2。当然,这 可能 影响性能。我建议进行分析以确定您是否属于这种情况。 - 使用其他编译器,例如 clang 或 MSVC。