Jan*_*ard 24 c++ sse alignment intrinsics
我使用了很多SSE编译器内在函数编写了一个3D矢量类.一切正常,直到我开始实现具有3D矢量作为新成员的类.我在发布模式下经历了奇怪的崩溃,但在调试模式下却没有,反之亦然.
所以我读了一些文章,并认为我需要将拥有3D矢量类实例的类对齐到16个字节.所以我只是在类之前添加了_MM_ALIGN16(__declspec(align(16)),如下所示:
_MM_ALIGN16 struct Sphere
{
// ....
Vector3 point;
float radius
};
Run Code Online (Sandbox Code Playgroud)
这似乎首先解决了这个问题.但在更改了一些代码后,我的程序又开始以奇怪的方式崩溃.我在网上搜索了一些,发现了一篇博客文章.我尝试了作者Ernst Hot为解决这个问题做了什么,它对我也有用.我在我的类中添加了new和delete运算符,如下所示:
_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
};
Run Code Online (Sandbox Code Playgroud)
恩斯特提到这种方法也可能存在问题,但他只是链接到一个不再存在的论坛,而没有解释为什么它可能会有问题.
所以我的问题是:
定义运算符有什么问题?
为什么不添加_MM_ALIGN16足够的类定义?
处理SSE内在函数的对齐问题的最佳方法是什么?
Chr*_*ica 19
首先,您必须关注两种类型的内存分配:
静态分配.对于要正确对齐自动变量,你的类型需要一个正确对齐规范(如__declspec(align(16)),__attribute__((aligned(16)))或你的_MM_ALIGN16).但幸运的是,如果类型成员(如果有)给出的对齐要求不够,则只需要这样做.因为你已经正确对齐Sphere,所以你不需要这个Vector3.如果你Vector3包含一个__m128成员(这很可能,否则我建议这样做),那么你甚至不需要它Vector3.因此,您通常不必弄乱编译器特定的对齐属性.
动态分配.这么简单的部分.问题是,C++在最低级别使用了一个相当类型无关的内存分配函数来分配任何动态内存.这只能保证所有标准类型的正确对齐,这可能恰好是16个字节,但不能保证.
为了弥补这一点,你必须重载内置operator new/delete来实现你自己的内存分配,并在引擎盖下使用对齐的分配函数而不是旧的malloc.重载operator new/delete本身就是一个主题,但并不像最初看起来那么困难(虽然你的例子还不够),你可以在这个优秀的FAQ问题中阅读它.
不幸的是,你必须对任何需要非标准对齐的成员的类型执行此操作,在您的情况下都是Sphere和Vector3.但是你可以做些什么来使它变得更容易只是为这些运算符创建一个具有适当重载的空基类,然后从这个基类派生所有必要的类.
大多数人有时会忘记的是标准分配器std::alocator使用全局operator new进行所有内存分配,因此您的类型不适用于标准容器(并且std::vector<Vector3>用例并不罕见).你需要做的是制作你自己的标准符合分配器并使用它.但是为了方便和安全,实际上最好只专注std::allocator于你的类型(可能只是从你的自定义分配器派生它),这样它总是被使用,你不需要在每次使用时都使用正确的分配器std::vector.不幸的是,在这种情况下,你必须再次为每个对齐类型专门化它,但一个小的邪恶的宏有助于此.
另外,你必须使用全局operator new/delete而不是自定义的东西来寻找其他东西,比如std::get_temporary_buffer和std::return_temporary_buffer,并且如果需要那么关心它们.
不幸的是,我认为,对于这些问题还没有更好的方法,除非你是在一个本地与16对齐的平台上并且知道这一点.或者你可能只是重载全局,operator new/delete以便始终将每个内存块对齐到16个字节,并且无需关心包含SSE成员的每个单独类的对齐,但我不知道这种方法的含义.在最坏的情况下,它应该只是导致浪费内存,但话又说回来,你通常不会动态地使用C分配小物件++(虽然std::list和std::map想象不同的看法这一点).
总结一下:
使用类似的东西来保证静态内存的正确对齐__declspec(align(16)),但只有在任何成员都没有注意的情况下才会这样做,这通常就是这种情况.
operator new/delete具有非标准对齐要求的成员的每种类型的过载.
使cunstom符合标准的分配器用于对齐类型的标准容器中,或者更好,专门std::allocator用于每个对齐类型.
最后一些一般性建议.通常,在执行许多向量运算时,您只能在计算量较大的块中从SSE中获利.为了简化所有这些对齐问题,特别是关心每个包含a的类型的对齐问题,Vector3制作一个特殊的SSE向量类型并且仅在长时间的计算中使用这个,使用正常的非存储和成员变量的SSE向量.
| 归档时间: |
|
| 查看次数: |
14177 次 |
| 最近记录: |