将std :: atomic与对齐的类一起使用

时间:2014-02-13 00:51:15

标签: c++ c++11 sse

我有一个mat4类,一个使用sse内在函数的4x4矩阵。此类使用_MM_ALIGN16对齐,因为它将矩阵存储为一组__m128。问题是,当我声明atomic<mat4>时,我的编译器对我大吼:

f:\program files (x86)\microsoft visual studio 12.0\vc\include\atomic(504): error C2719: '_Val': formal parameter with __declspec(align('16')) won't be aligned

当我尝试将与_MM_ALIGN16对齐的任何类作为函数的参数传递时(不使用const &),这是我得到的相同错误。

如何声明mat4类的原子版?

4 个答案:

答案 0 :(得分:4)

MSC编译器从未支持x86堆栈上的参数超过4个字节的对齐,并且没有解决方法。

您可以通过编译

自行验证
struct A { __declspec(align(4)) int x; }; 
void foo(A a) {}                      

对,

// won't compile, alignment guarantee can't be fulfilled
struct A { __declspec(align(8)) int x; };

对,

// __m128d is naturally aligned, again - won't compile
struct A { __m128d x; };

MSC通常由以下内容解除,

  

您无法指定功能参数的对齐方式。

     

align (C++)

并且您无法指定对齐方式,因为MSC编写者希望保留决定对齐的自由,

  

x86编译器使用不同的方法来对齐堆栈。通过   默认情况下,堆栈是4字节对齐的。虽然这是空间   高效,你可以看到有一些数据类型需要   8字节对齐,并且为了获得良好的性能,16字节   有时需要对齐。编译器可以在某些上确定   场合,即动态8字节堆栈对齐   有益的 - 特别是当堆栈上有双值时。

     

编译器以两种方式执行此操作。首先,编译器可以使用   链接时代码生成(LTCG),由用户指定时   编译和链接时间,生成完整的调用树   程序。有了它,它可以确定调用树的区域在哪里   8字节堆栈对齐将是有益的,并且它确定   呼叫站点,其中动态堆栈对齐获得最佳回报。该   当函数在堆栈上加倍时使用第二种方法,但是,   无论出于何种原因,尚未进行8字节对齐。编译器   应用启发式(随着每次迭代而改进)   编译器)确定该函数是否应该是动态的   8字节对齐。

     

Windows Data Alignment on IPF, x86, and x64

因此,只要您将MSC与32位平台工具集一起使用,这个问题就不可避免了。

x64 ABI明确指出了对齐,定义了一些大小的非平凡结构或结构作为指针参数传递。这在Section 3.2.3 of the ABI中有详细说明,MSC必须实现这一点才能与ABI兼容。

路径1:使用其他Windows编译器工具链:GCC或ICC。

路径2:转移到64位平台MSC工具集

路径3:使用std::atomic<T>将用例减少到T=__m128d,因为可以跳过堆栈并直接在XMM寄存器中传递变量。 < / p>

答案 1 :(得分:3)

atomic<T>可能有一个构造函数,它将T的副本作为(formal) parameter传递。例如,在与GCC 4.5打包的atomic标头中:

97: atomic(_Tp __i) : _M_i(__i) { }

这是有问题的,原因与任何其他具有内存对齐类型作为参数的函数完全相同:函数跟踪堆栈中的内存对齐数据会非常复杂和缓慢。

即使编译器允许,这种方法也会incur a significant performance penalty。假设您正在尝试优化速度,我将实现一种不太精细的内存访问方法。在执行一系列计算时锁定对大块内存的访问,或者明确地设计程序,以便线程永远不会尝试访问同一块内存。

答案 2 :(得分:1)

我在MSVC中使用Agner Fog's vectorclass遇到了类似的问题。问题发生在32位模式下。如果您在64位模式释放模式下编译,我认为您不会遇到此问题。在Windows和Unix中,堆栈上的所有变量在64位模式下都与16字节对齐,但不一定在32位模式下。在编写时错误的手册中,他写了

  

“错误C2719:带__declspec的形式参数(对齐('16'))将不会对齐”。   Microsoft编译器无法将向量作为函数参数处理。该   最简单的解决方案是将参数更改为const引用,例如:   Vec4f my_function(Vec4f const&amp; x){   ......}

因此,如果在将类传递给函数时使用const引用(如上所述),它也应该在32位模式下工作。

编辑:基于此Self-contained, STL-compatible implementation of std::vector我认为你可以使用“瘦包装”。像。的东西。

template <typename T>
struct wrapper : public T
{
    wrapper() {}
    wrapper(const T& rhs) : T(rhs) {}
};

struct __declspec(align(64)) mat4
{
    //float x, y, z, w;
};

int main()
{
    atomic< wrapper<mat4> > m;  // OK, no C2719 error
    return 0;
}

答案 3 :(得分:0)

我不自称理解__declspec(align(foo))应该如何运作,但是this standard C++ program compiles and runs fine in gcc & clang using alignas(16)

struct alignas(16) mat4 {
    float some_floats[4][4];
};

std::atomic<mat4> am4;
static_assert(alignof(decltype(am4)) == 16,
              "Jabberwocky is killing user.");

int main() {
    static const mat4 foo = {{
        { 1, 2, 3, 4 },
        { 1, 2, 3, 4 },
        { 1, 2, 3, 4 },
        { 1, 2, 3, 4 }
    }};
    am4 = foo;
}