【问题标题】:Copying __m256d in constructor causes segfault在构造函数中复制 __m256d 会导致段错误
【发布时间】:2014-03-20 20:55:06
【问题描述】:

下面的代码是导致我在代码中看到的错误的简化案例。当我手动调用A(const A&) 的构造函数时,一切都很好,但是当我尝试将一个vector<A> 复制到另一个时,我在A(const A& in) : vec(in.vec) {} 行出现段错误。

为什么会发生这种情况,我该如何处理我的代码?

编辑:所以我通过 @mpromonet 添加了代码,它现在运行完成,但是 1. 它要慢得多(超过 3 倍)和 2. 当我使用 -pg 编译以找出原因时,调用 _mm256_add_pd 时出现段错误。

#include <immintrin.h>
#include <vector>
using std::vector;


struct A {
    union {
        struct {
            double a, b, c;
        };
        __m256d vec;
    };

    A() : a(0), b(0), c(0) {}
    A(const A& in) : vec(in.vec) {}
};

int main() {
    vector<A> e(10);
    vector<A> b;
    b = e;
}

编辑: 这段代码在我的程序中占了大部分时间。使用g++ --std=c++11 -ffast-math -march=native -O3 -fno-inline -g -pg 编译。请参阅下面的调用图。我有一个Electron 对象数组,其中包含两个用__attribute__((aligned(32))) 声明的Vector 对象。为了使数组对齐,我这样做了

Electron* data_orig = new Electron[s+3]; 
Electron* data = (Electron*)((void*)data_orig + 64 - (size_t)data_orig % 64);

没有 AVX

struct Vector {
    double x;
    double y;
    double z;

    Vector(double a, double b, double c) :
        x(a), y(b), z(c)
        {}

    Vector(const Vector& u) :
        x(u.x), y(u.y), z(u.z)
        {}


    Vector(const UnitVector& u);


    Vector operator*=(const double m) { x*=m; y*=m; z*=m; return *this; }
    Vector operator+=(const Vector& in) {
        x+=in.x; y+=in.y; z+=in.z;
        return *this;
    }
    ostream& operator<<(ostream& os) {
        os << "x:" << x << " y:" << y << " z:" << z;
        return os;
    }

};
Vector::Vector(const UnitVector& u) :
        x(u.x*u.mag), y(u.y*u.mag), z(u.z*u.mag)
        {}

使用 AVX

struct Vector {
    union {
        struct {
            double x;
            double y;
            double z;
        };
        __m256d vec __attribute__ ((aligned (32)));
    };

    Vector(double a, double b, double c) :
        x(a), y(b), z(c)
        {}

    Vector(const Vector& in) : vec(in.vec) {}
    //: x(in.x), y(in.y), z(in.z) {}


    Vector(const UnitVector& u);

    Vector operator*=(const double m) {
        vec = _mm256_mul_pd(vec, _mm256_set1_pd(m));
        return *this;
    }

    Vector operator+=(const Vector& in) {
        vec = _mm256_add_pd(vec, in.vec);
        return *this;
    }
    ostream& operator<<(ostream& os) {
        os << "x:" << x << " y:" << y << " z:" << z;
        return os;
    }

};
Vector::Vector(const UnitVector& u) { vec = _mm256_mul_pd(u.vec, _mm256_set1_pd(u.mag));}

【问题讨论】:

  • 我认为这是因为向量如何处理内存 - 可能存在指针未对齐
  • 对象肯定没有对齐。 有点 可怕的是,较新的编译器有时会在所有情况下发出未对齐的访问。因此,虽然它不会崩溃,但您可能会受到性能影响。最糟糕的是,你甚至都不知道。
  • 看起来__m256d 只是double 和__attribute__((__vector_size__(32))) 上的typedef
  • @Mysticial:如果内存对齐,vmovups 和vmovaps 一样快;唯一的区别是当数据未对齐时它不会崩溃。使用vmovaps 的唯一原因是强制执行对齐。
  • @StephenCanon 是的。这就是我想要的强制执行。但是最新的 VS 和 ICC 编译器总是使用vmovups。所以我什至不知道什么时候出现了偏差。 :(

标签: c++ g++ stdvector intrinsics avx


【解决方案1】:

问题在于您系统的分配器不保证 32B 对齐,而您的编译器假设 __m256d 类型。

听起来您的目标是 clang?如果是这样,请执行以下操作:

typedef double __attribute__((vector_size(32),aligned(16))) m256d_unaligned;

然后在你的结构定义中使用这个:

m256d_unaligned vec;

告诉编译器不要假设向量是对齐的。 (如果您使用的系统甚至不能保证 16B 对齐 - 不太可能 - 您可能需要进一步减少对齐参数)。

一种更具侵入性(但可能更高效)的解决方案是使用自定义分配器,以保证需要它的类型的 32B 对齐。

【讨论】:

  • 其实我的目标是 g++ 4.8。这有什么改变吗?
  • 我相信,但我不确定这是否也适用于最近的 GCC。
  • 好的,我试试。现在我只是在程序集中用vmovupd 替换vmovapd,它正在运行;虽然很慢。
  • 不幸的是,尝试以这种方式对齐它不起作用,所以我继续使用我确保分配 32 字节对齐的数组。现在没有错误。不过它非常慢,没有 AVX 内在函数需要 13.5 秒,使用 AVX 需要 71 秒。知道为什么吗?
  • 我在上面的问题中添加了我的代码中花费时间最长的部分,而不是粘贴整个内容。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-22
  • 2022-06-15
  • 1970-01-01
相关资源
最近更新 更多