gpt4 book ai didi

c++ - SSE、内在函数和对齐

转载 作者:行者123 更新时间:2023-12-01 22:56:58 24 4
gpt4 key购买 nike

我已经使用很多 SSE 编译器内在函数编写了一个 3D vector 类。一切正常,直到我开始将具有 3D vector 的类作为 new 的成员。我在 Release模式下遇到了奇怪的崩溃,但在 Debug模式下则没有,反之亦然。

所以我阅读了一些文章并认为我需要将拥有 3D vector 类实例的类也对齐到 16 个字节。所以我刚刚添加了 _MM_ALIGN16 ( __declspec(align(16) ) 在类前面,如下所示:

_MM_ALIGN16 struct Sphere
{
// ....

Vector3 point;
float radius
};

这似乎首先解决了这个问题。但是在更改了一些代码之后,我的程序又开始以奇怪的方式崩溃了。我在网上搜索了更多,找到了 blog文章。我尝试了作者 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
};

Ernst 提到这种方法也可能有问题,但他只是链接到一个不再存在的论坛,而没有解释为什么会出现问题。

所以我的问题是:
  • 定义运算符有什么问题?
  • 为什么不添加 _MM_ALIGN16给类定义够了吗?
  • 处理 SSE 内在函数带来的对齐问题的最佳方法是什么?
  • 最佳答案

    首先,您必须关注两种类型的内存分配:

  • 静态分配。要正确对齐自动变量,您的类型需要正确的对齐规范(例如 __declspec(align(16))__attribute__((aligned(16))) 或您的 _MM_ALIGN16 )。但幸运的是,只有在类型成员(如果有)给出的对齐要求不充分时,您才需要这个。所以你不需要这个 Sphere ,鉴于您的 Vector3已经正确对齐。如果您的 Vector3包含 __m128成员(很有可能,否则我建议这样做),那么您甚至不需要它来 Vector3 .所以你通常不必弄乱编译器特定的对齐属性。
  • 动态分配。简单的部分就这么多。问题是,C++ 在最低级别使用了一种与类型无关的内存分配函数来分配任何动态内存。这只能保证所有标准类型的正确对齐,这可能恰好是 16 个字节,但不能保证。

    为此,您必须重载内置 operator new/delete实现你自己的内存分配并在引擎盖下使用对齐的分配函数而不是旧的 malloc .重载 operator new/delete本身就是一个主题,但并不像一开始看起来那么难(尽管您的示例还不够),您可以在 this excellent FAQ question 中阅读有关它的信息。 .

    不幸的是,您必须为具有任何需要非标准对齐的成员的每种类型执行此操作,在您的情况下,两者都是 SphereVector3 .但是你可以做的让它更容易一些只是为这些运算符创建一个带有适当重载的空基类,然后从这个基类派生所有必要的类。

    大多数人有时容易忘记的是标准分配器 std::alocator使用全局operator new对于所有内存分配,因此您的类型将不适用于标准容器(并且 std::vector<Vector3> 并不是那么罕见的用例)。您需要做的是制作自己的标准符合分配器并使用它。但为了方便和安全,实际上最好专门化 std::allocator为您的类型(也许只是从您的自定义分配器派生它),以便它始终被使用,并且每次使用 std::vector 时您都不需要关心使用正确的分配器.不幸的是,在这种情况下,您必须再次为每个对齐类型专门化它,但是一个小的邪恶宏对此有所帮助。

    此外,您必须使用全局 operator new/delete 留意其他事情。而不是您自定义的,例如 std::get_temporary_bufferstd::return_temporary_buffer ,并在必要时照顾他们。

  • 不幸的是,我认为还没有更好的方法来解决这些问题,除非您使用的平台本身与 16 对齐。并了解这个 .或者你可能只是重载了全局 operator new/delete始终将每个内存块对齐到 16 个字节,并且无需关心包含 SSE 成员的每个类的对齐,但我不知道这种方法的含义。在最坏的情况下,它应该只会导致浪费内存,但是你通常不会在 C++ 中动态分配小对象(尽管 std::liststd::map 可能对此有不同的看法)。

    所以总结一下:
  • 使用诸如 __declspec(align(16)) 之类的东西来注意静态内存的正确对齐,但前提是它尚未被任何成员照顾,通常是这种情况。
  • 过载 operator new/delete对于具有非标准对齐要求的成员的每种类型。
  • 制作一个自定义的符合标准的分配器,用于对齐类型的标准容器,或者更好的是,专门化 std::allocator对于每个对齐的类型。


  • 最后是一些一般性建议。通常,在执行许多 vector 运算时,您只能在计算量大的块中从 SSE 中获利。为了简化所有这些对齐问题,尤其是关注包含 Vector3 的每个类型的对齐问题。 ,制作特殊的 SSE vector 类型并仅在冗长的计算中使用它可能是一个很好的方法,使用普通的非 SSE vector 存储和成员变量。

    关于c++ - SSE、内在函数和对齐,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12502071/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com