我目前正在重构一个命令式的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]);
我已经尝试在构造函数之后分配类成员,同样的段错误。
更新:This是一个相关的问题,但它不是重复的。(这里:专注于班级成员,与“新”的关系只在初步问题后变得明显)
正如Peter Cordes在上面的评论中已经提到的那样,问题在于new
不尊重扩展对齐前C ++ 17。 (参见C ++ 17中采用的[P0035R4],使new
可用于超过alignof(maxalign_t)
对齐的记忆)。
GCC7 and later支持与-std=gnu++17
或-std=c++17
(或只是-faligned-new
)对齐。如果您使用operator new
,您的代码将Just Work™并自动将所需的路线传递给turn on those options。
但是较老的GCC,包括你的6.3,没有,所以你必须手动确保获得正确对齐的内存。有几种方法可以做到这一点。
_mm_alloc
已在评论中提及。在海湾合作委员会,_mm_alloc
似乎基本上映射到posix_memalign
,所以你也可以直接使用它。便携式C ++ 11解决方案将分配一个足够大的缓冲区来容纳类的对象以及开头填充所需的任何空间,以确保正确对齐。然后,您可以使用std::align
和placement 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) };
}