C++ SSE、本质和对齐

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

我已经使用许多SSE编译器内部函数编写了一个3D向量类。一切都很顺利,直到我开始使用新的3D向量来创建类。我在发布模式下遇到了奇怪的崩溃,但在调试模式下没有,反之亦然

因此,我阅读了一些文章,并认为我需要将拥有3D vector类实例的类也对齐到16字节。因此,我刚刚在类前面添加了
\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
    不够

  • 处理SSE Intrinsic带来的对齐问题的最佳方法是什么

  • 运算符本身的问题是它们不够。它们不会影响堆栈分配,对此,您仍然需要
    \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查找其他内容