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

时间:2019-04-20 23:44:54

标签: c++ segmentation-fault memory-alignment avx avx2

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

我在WSL中使用:

SELECT deptName, deptDesc, empFirstName, empLastName
FROM department
LEFT JOIN employee on department.deptID=employee.deptID
ORDER BY deptName;

使用标志进行编译:

gcc version 6.3.0 20170516 (Debian 6.3.0-18+deb9u1)

再现段错误的最小代码示例是:

g++ -mavx2 -g minimal.cpp

GDB输出:

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

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

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

1 个答案:

答案 0 :(得分:4)

正如彼得·科德斯(Peter Cordes)在上面的评论中已经提到的,这里的问题是new不遵守C ++ 17之前的扩展对齐方式。 (请参阅C ++ 17中采用的[P0035R4],以使new可用于具有超过alignof(maxalign_t)对齐方式的内存中。)

GCC7 and later支持与-std=gnu++17-std=c++17(或仅-faligned-new)对齐。如果您turn on those options,您的代码将运行Just Work™并自动将所需的对齐方式传递到operator new


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

_mm_alloc已在评论中提及。在GCC上,_mm_alloc似乎基本上映射到posix_memalign,因此您也可以直接使用它。一个可移植的C ++ 11解决方案将分配一个足够大的缓冲区,以容纳您的类的对象,以及开始时需要进行填充以确保正确对齐的任何空间。然后,您可以使用std::alignplacement 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) };
}

live example here