AVX2:分配给 __m256i class 成员时出现分段错误
AVX2: segmentation fault when assigning to __m256i class member
我目前正在重构一个命令式 C++ 程序,该程序广泛使用 AVX2 基元到结构良好的基于 class 的程序。不幸的是,我在分配给具有 AVX2 数据类型的 class 成员时遇到了段错误。
我在 WSL 中使用:
gcc version 6.3.0 20170516 (Debian 6.3.0-18+deb9u1)
使用标志编译:
g++ -mavx2 -g minimal.cpp
重现段错误的最小代码示例是:
#include <immintrin.h>
class MyClass
{
public:
MyClass(int* arr);
__m256i value;
};
MyClass::MyClass(int* arr){
this->value = _mm256_set_epi32(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7]);
}
int main(){
int arr[8] = {0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7};
MyClass* m = new MyClass(arr);
}
GDB 输出:
Program received signal SIGSEGV, Segmentation fault.
0x00000000080007cf in MyClass::MyClass (this=0x8413e70, arr=0x7ffffffedd90) at minimal.cpp:11
11 this->value = _mm256_set_epi32(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7]);
我已经尝试在构造函数之后分配 class 成员,同样的段错误。
更新: 是一个相关问题,但它不是重复的。(此处:关注 class 成员,与 "new" 的关系在最初的问题后才变得明显)
正如 Peter Cordes 在上面的评论中提到的,这里的问题是 new
不遵守 C++17 之前的扩展对齐。 (参见 C++17 中采用的 [P0035R4],使 new
可用于对齐超过 alignof(maxalign_t)
的内存。
GCC7 and later supports aligned new with -std=gnu++17
or -std=c++17
(or just -faligned-new
). Your code will Just Work™ and automatically pass the required alignment to operator new
if you .
但是较旧的 GCC,包括您的 6.3,不会,因此您必须手动确保获得正确对齐的内存。有几种方法可以做到这一点。
_mm_alloc
已经在评论中提到了。在 GCC 上,_mm_alloc
似乎基本上映射到 posix_memalign
,因此您也可以直接使用它。可移植的 C++11 解决方案将分配一个足够大的缓冲区以容纳 class 的对象以及开始时填充所需的任何 space 以确保正确对齐。然后,您可以使用 std::align
and placement new 在适当对齐的地址构造您的对象。
综上所述,无论您选择哪种分配正确对齐内存的方法,我都强烈建议通过为您的 class 提供分配和释放函数来封装这些内容。对齐要求是类型本身的 属性,不应该由 class 的用户知道,由于实现细节,例如它具有类型 __m256i
,任何类型 MyClass
的对象都有扩展的对齐要求,每当通过 new 表达式分配此类对象时都必须考虑这一点。您应该禁止通过 new 表达式创建此类型的对象,或者提供必要的设施以使该类型与 new 表达式一起正确工作...
C++11 解决方案可能如下所示:
#include <cstddef>
#include <memory>
#include <immintrin.h>
class MyClass
{
__m256i value;
public:
MyClass(const int* arr)
{
this->value = _mm256_set_epi32(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7]);
}
void* operator new(std::size_t size)
{
return _mm_malloc(size, alignof(MyClass));
}
void* operator new[](std::size_t size)
{
return _mm_malloc(size, alignof(MyClass));
}
void operator delete(void* ptr)
{
_mm_free(ptr);
}
void operator delete[](void* ptr)
{
_mm_free(ptr);
}
};
int main()
{
int arr[8] = {0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7};
auto m = std::unique_ptr<MyClass> { new MyClass(arr) };
}
我目前正在重构一个命令式 C++ 程序,该程序广泛使用 AVX2 基元到结构良好的基于 class 的程序。不幸的是,我在分配给具有 AVX2 数据类型的 class 成员时遇到了段错误。
我在 WSL 中使用:
gcc version 6.3.0 20170516 (Debian 6.3.0-18+deb9u1)
使用标志编译:
g++ -mavx2 -g minimal.cpp
重现段错误的最小代码示例是:
#include <immintrin.h>
class MyClass
{
public:
MyClass(int* arr);
__m256i value;
};
MyClass::MyClass(int* arr){
this->value = _mm256_set_epi32(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7]);
}
int main(){
int arr[8] = {0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7};
MyClass* m = new MyClass(arr);
}
GDB 输出:
Program received signal SIGSEGV, Segmentation fault.
0x00000000080007cf in MyClass::MyClass (this=0x8413e70, arr=0x7ffffffedd90) at minimal.cpp:11
11 this->value = _mm256_set_epi32(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7]);
我已经尝试在构造函数之后分配 class 成员,同样的段错误。
更新:
正如 Peter Cordes 在上面的评论中提到的,这里的问题是 new
不遵守 C++17 之前的扩展对齐。 (参见 C++17 中采用的 [P0035R4],使 new
可用于对齐超过 alignof(maxalign_t)
的内存。
GCC7 and later supports aligned new with -std=gnu++17
or -std=c++17
(or just -faligned-new
). Your code will Just Work™ and automatically pass the required alignment to operator new
if you
但是较旧的 GCC,包括您的 6.3,不会,因此您必须手动确保获得正确对齐的内存。有几种方法可以做到这一点。
_mm_alloc
已经在评论中提到了。在 GCC 上,_mm_alloc
似乎基本上映射到 posix_memalign
,因此您也可以直接使用它。可移植的 C++11 解决方案将分配一个足够大的缓冲区以容纳 class 的对象以及开始时填充所需的任何 space 以确保正确对齐。然后,您可以使用 std::align
and placement new 在适当对齐的地址构造您的对象。
综上所述,无论您选择哪种分配正确对齐内存的方法,我都强烈建议通过为您的 class 提供分配和释放函数来封装这些内容。对齐要求是类型本身的 属性,不应该由 class 的用户知道,由于实现细节,例如它具有类型 __m256i
,任何类型 MyClass
的对象都有扩展的对齐要求,每当通过 new 表达式分配此类对象时都必须考虑这一点。您应该禁止通过 new 表达式创建此类型的对象,或者提供必要的设施以使该类型与 new 表达式一起正确工作...
C++11 解决方案可能如下所示:
#include <cstddef>
#include <memory>
#include <immintrin.h>
class MyClass
{
__m256i value;
public:
MyClass(const int* arr)
{
this->value = _mm256_set_epi32(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7]);
}
void* operator new(std::size_t size)
{
return _mm_malloc(size, alignof(MyClass));
}
void* operator new[](std::size_t size)
{
return _mm_malloc(size, alignof(MyClass));
}
void operator delete(void* ptr)
{
_mm_free(ptr);
}
void operator delete[](void* ptr)
{
_mm_free(ptr);
}
};
int main()
{
int arr[8] = {0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7};
auto m = std::unique_ptr<MyClass> { new MyClass(arr) };
}