我有一个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类的原子版?
答案 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通常由以下内容解除,
您无法指定功能参数的对齐方式。
并且您无法指定对齐方式,因为MSC编写者希望保留决定对齐的自由,
x86编译器使用不同的方法来对齐堆栈。通过 默认情况下,堆栈是4字节对齐的。虽然这是空间 高效,你可以看到有一些数据类型需要 8字节对齐,并且为了获得良好的性能,16字节 有时需要对齐。编译器可以在某些上确定 场合,即动态8字节堆栈对齐 有益的 - 特别是当堆栈上有双值时。
编译器以两种方式执行此操作。首先,编译器可以使用 链接时代码生成(LTCG),由用户指定时 编译和链接时间,生成完整的调用树 程序。有了它,它可以确定调用树的区域在哪里 8字节堆栈对齐将是有益的,并且它确定 呼叫站点,其中动态堆栈对齐获得最佳回报。该 当函数在堆栈上加倍时使用第二种方法,但是, 无论出于何种原因,尚未进行8字节对齐。编译器 应用启发式(随着每次迭代而改进) 编译器)确定该函数是否应该是动态的 8字节对齐。
因此,只要您将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;
}