AVX2:分配给类成员时出现分段错误__m256i

AVX2: segmentation fault when assigning to __m256i class member

本文关键字:分段 错误 m256i 分配 成员 AVX2      更新时间:2023-10-16

我目前正在重构一个命令式C++程序,该程序广泛使用AVX2基元,以构建结构良好的基于类的程序。不幸的是,在分配给具有 AVX2 数据类型的类成员时,我遇到了段错误。

我在 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]);

我已经尝试在构造函数之后分配类成员,相同的段错误。

更新:这是一个相关的问题,但它不是重复的。(这里:关注班级成员,与"新"的关系只有在最初的问题之后才变得明显)

正如Peter Cordes在上面的评论中已经提到的,这里的问题是new不尊重C++17之前的扩展对齐。(参见 [P0035R4],该 [] 于 C++17 年采用,以使new可用于具有超过alignof(maxalign_t)对齐的内存)。

GCC7 及更高版本支持与-std=gnu++17-std=c++17(或只是-faligned-new)对齐的新内容。如果打开这些选项,您的代码将正常工作™并自动将所需的对齐方式传递给operator new


但是较旧的GCC,包括您的6.3,没有,因此您必须手动确保获得正确对齐的内存。有几种方法可以做到这一点。

评论中已经提到了_mm_alloc。在GCC上,_mm_alloc似乎基本上映射到posix_memalign,所以你也可以直接使用它。可移植的 C++11 解决方案将分配一个足够大的缓冲区,以容纳类中的对象以及开始时填充所需的任何空间,以确保正确对齐。然后,您可以使用std::align和放置 new 在适当对齐的地址处构造对象。

话虽如此,无论您选择哪种分配正确对齐内存的方法,我强烈建议通过为您的类提供分配和释放函数来封装这些东西。对齐要求是类型本身的属性,不应由类的用户知道,由于实现细节,例如它具有类型__m256i的成员,任何类型MyClass的对象都具有扩展的对齐要求,每当通过新表达式分配此类对象时,都必须考虑这些要求。您应该禁止通过新表达式创建此类型的对象,或者提供必要的工具以使该类型在新表达式中正常工作...

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) };
}

现场示例在这里