C++ SSE、本质和对齐
我已经使用许多SSE编译器内部函数编写了一个3D向量类。一切都很顺利,直到我开始使用新的3D向量来创建类。我在发布模式下遇到了奇怪的崩溃,但在调试模式下没有,反之亦然 因此,我阅读了一些文章,并认为我需要将拥有3D vector类实例的类也对齐到16字节。因此,我刚刚在类前面添加了C++ SSE、本质和对齐,c++,alignment,sse,intrinsics,C++,Alignment,Sse,Intrinsics,我已经使用许多SSE编译器内部函数编写了一个3D向量类。一切都很顺利,直到我开始使用新的3D向量来创建类。我在发布模式下遇到了奇怪的崩溃,但在调试模式下没有,反之亦然 因此,我阅读了一些文章,并认为我需要将拥有3D vector类实例的类也对齐到16字节。因此,我刚刚在类前面添加了\u MM\u ALIGN16(\u declspec(align(16))如下所示: _MM_ALIGN16 struct Sphere { // .... Vector3 point; f
\u MM\u ALIGN16
(\u declspec(align(16)
)如下所示:
_MM_ALIGN16 struct Sphere
{
// ....
Vector3 point;
float radius
};
一开始这似乎解决了问题。但在更改了一些代码后,我的程序又以奇怪的方式崩溃了。我在网上搜索了更多的内容,找到了一篇文章。我尝试了作者Ernst Hot所做的解决问题的方法,它也对我有效。我在我的类中添加了新的和删除运算符,如下所示:
_MM_ALIGN16 struct Sphere
{
// ....
void *operator new (unsigned int size)
{ return _mm_malloc(size, 16); }
void operator delete (void *p)
{ _mm_free(p); }
Vector3 point;
float radius
};
Ernst提到,这个论坛也可能有问题,但他只是链接到一个不再存在的论坛,没有解释为什么它会有问题
因此,我的问题是:
\u MM\u ALIGN16
不够\u declspec(align(16))
\uuuu declspec(align(16))
影响编译器在内存中放置对象的方式,只要它有选择。对于新对象,编译器别无选择,只能使用操作符new
返回的内存double
区别对待。否则,请阅读编译器文档了解解决方法。每个有缺陷的编译器都有自己的问题集,因此也有自己的解决方法集基本上,您需要确保向量正确对齐,因为SIMD向量类型通常比任何内置类型具有更大的对齐要求 这需要做以下几件事:
Vector3
在堆栈或结构成员上时正确对齐。这是通过将\uuuu属性(对齐(32))
应用到Vector3
类(或编译器支持的任何属性)来完成的。请注意,您不需要将该属性应用于包含Vector3
的结构,这是不必要的,也是不够的(即不需要将其应用于Sphere
)Vector3
或其封闭结构正确对齐。这可以通过使用posix_memalign()
(或平台的类似函数)来完成,而不是使用普通malloc()
或operator new()
,因为后两个函数会对齐内置类型的内存(通常为8或16字节),不能保证足够用于SIMD类型首先,您必须注意两种类型的内存分配:
- 静态分配。要使自动变量正确对齐,您的类型需要一个正确的对齐规范(例如,
,\u declspec(align(16))
,或您的\u属性((aligned(16))
)。但幸运的是,只有当类型的成员给出对齐要求时(如果有的话),您才需要这个规范是不够的。因此您不需要为您的\u MM\u ALIGN16
,因为您的Sphere
已正确对齐。如果您的Vector3
包含Vector3
成员(这很可能,否则我建议这样做),那么对于\u m128
,您甚至不需要它。因此,您通常不必处理特定于编译器的对齐属性Vector3
问题是,C++在最低级别上使用了一种相当类型的不可知的内存分配函数来分配任何动态内存。这只保证了所有标准类型的正确对齐,这可能是16字节但不能保证。
为了补偿这一点,您必须重载内置的
操作符new/delete
来实现您自己的内存分配,并在引擎盖下使用对齐的分配函数,而不是好的旧malloc
。重载操作符new/delete
本身就是一个主题,但并不像一开始看起来那么困难(尽管你的例子还不够)你可以在
不幸的是,对于每种类型,如果有任何成员需要非标准对齐,您必须这样做,在您的情况下,无论是Sphere
还是Vector3
。但是您可以做的只是为这些操作符创建一个带有适当重载的空基类,然后从这个基类派生所有必要的类美国
大多数人有时会忘记的是,标准分配器std::alocator
使用全局运算符new
进行所有内存分配,因此您的类型无法与标准容器一起工作(而std::vector
也不是很少见的用例)。您需要做的是创建自己的标准一致分配器并使用它。但为了方便和安全,实际上最好只为您的类型专门化std::allocator
(可能只是从自定义分配器派生)这样就可以一直使用它,而且每次使用std::vector
时,您不必关心如何使用正确的分配器。不幸的是,在这种情况下,您必须再次为每个对齐类型专门化它,但是一个小的邪恶宏可以帮助您做到这一点
此外,您还必须使用全局运算符new/del查找其他内容