【问题标题】:What does it mean for an SSE vector to be "16 byte alligned" and how can I ensure that it is?SSE 向量“16 字节对齐”是什么意思,我如何确保它是?
【发布时间】:2023-03-19 22:50:01
【问题描述】:

我现在正在使用向量和矩阵,有人建议我应该使用 SSE 而不是使用浮点数组。然而,在阅读 C 内在函数和汇编指令的定义时,看起来有一些函数的不同版本,其中向量必须是“16 字节对齐”,而较慢的版本是向量未对齐。将向量对齐 16 字节是什么意思?如何确保我的向量是 16 字节对齐的?

【问题讨论】:

  • 很确定这意味着您的结构将被填充,以便大小始终是 16 字节的倍数...然后您可以在主板等上获得更好的“总线”传输,作为离散的一组矢量将坐在公共汽车上并一起旅行,而不会被分解和重新组装......这有意义吗?
  • @Grantly 所以如果我只是使用 __m128 类型的变量或使用最大成员为 16 个字节的联合,我可以使用这些函数吗?
  • 对不起,我必须投反对票,因为错误的答案被标记为接受,这可能会误导未来的读者。
  • 我再次编辑和改进了我的答案......我是否要删除它我很伤心 - 尊重那些更准确地回答问题的人。但我做了很多阅读,希望现在的答案是关于主题的并且有帮助。如果它喜欢 - 我会删除它

标签: c assembly sse


【解决方案1】:

对齐确保对象在某个地址上对齐,该地址是 2 的某个幂的倍数。 16 字节对齐意味着地址的数值是 16 的倍数。对齐很重要,因为 CPU 通常效率较低或完全无法加载没有所需对齐的内存。

您的 ABI 决定了类型的自然对齐方式。通常,整数类型和浮点类型与它们自己的大小对齐,或者与您的 CPU 可以一次处理的最大对象的大小对齐,以较小者为准。例如,在 64 位 Intel 机器上,32 位整数在 4 个字节上对齐,64 位整数在 8 个字节上对齐,而 128 位整数在 8 个字节上对齐。

结构和联合的对齐方式与其最对齐的字段相同。这意味着如果您的 struct 包含一个具有 2 字节对齐的字段和另一个具有 8 字节对齐的字段,则该结构将对齐为 8 个字节。

在 C++ 中,您可以使用 alignof 运算符,就像 sizeof 运算符一样,来获取类型的对齐。在 C 中,当您包含 <stdalign.h> 时,可以使用相同的构造;或者,您可以使用 _Alignof 而不包含任何内容。

AFAIK,在 C 或 C++ 中没有强制对齐为特定值的标准方法,但有特定于编译器的扩展可以做到这一点。在 Clang 和 GCC 上,您可以使用 __attribute__((aligned(N))) 属性:

struct s_Stuff {
   int var1;
   short  var2;
   char padding[10];
} __attribute__((aligned(16)));

(Example.)

(此属性不要__attribute__((align(N)))混淆,后者设置变量的对齐方式。)

在我的脑海中,我不确定 Visual Studio,但according to SoronelHaetir,那就是__declspec(align(N))。不确定它在结构声明中的位置。

在向量指令的上下文中,对齐很重要,因为人们倾向于创建浮点值数组并对其进行操作,而不是使用已知对齐的类型。但是,<emmintrin.h> 中的 __m128__m256__m512(以及它们的所有变体,如 _m128i 等),如果您的编译器环境有它,则保证在正确的边界上对齐以供使用具有对齐的内在函数。

根据您的平台,malloc 可能会或可能不会返回在矢量对象的正确边界上对齐的内存。 C11 中引入了aligned_alloc 来解决这些问题,但并非所有平台都支持它。

  • 苹果:不支持aligned_allocmalloc 返回平台支持的最紧急对齐的对象;
  • Windows:不支持aligned_allocmalloc 返回在最大对齐上对齐的对象,VC++ 自然会在没有对齐规范的情况下放置对象;向量类型使用_aligned_malloc
  • Linux:malloc 返回对象aligned on an 8- or 16-byte boundary;使用aligned_alloc

一般来说,可以请求稍微多一点的内存并自己执行对齐,而惩罚最小(除了你自己编写一个类似free 的函数,它将接受此函数返回的指针):

void* aligned_malloc(size_t size, size_t alignment) {
    intptr_t alignment_mask = alignment - 1;
    void* memory = malloc(size + alignment_mask);
    intptr_t unaligned_ptr = (intptr_t)memory;
    intptr_t aligned_ptr = (unaligned_ptr + alignment_mask) & ~alignment_mask;
    return (void*)aligned_ptr;
}

纯粹主义者可能会争辩说,将指针视为整数是邪恶的,但在撰写本文时,他们可能无法提供实用的跨平台解决方案作为交换。

【讨论】:

【解决方案2】:

新的编辑可以改进和集中我对特定查询的回答

为了确保内存中的数据对齐,C 中有特定的函数来强制执行此操作(假设您的数据是兼容的 - 您的数据匹配或离散地符合您所需的对齐方式)

要使用的函数是[_aligned_malloc][1],而不是原版的malloc

// Using _aligned_malloc  
// Note alignment should be 2^N where N is any positive int.  
int alignment = 16;
ptr = _aligned_malloc('required_size', alignment);  
if (ptr == NULL)  
{  
    printf_s( "Error allocation aligned memory.");  
    return -1;  
}  

这将(如果成功)强制您的数据在 16 字节边界上对齐,并且应该满足 SSE 的要求。

我对结构成员对齐问题的看法较旧的答案很重要 - 但不是直接回答查询

为确保结构成员字节对齐,您可以注意如何在结构中排列成员(最大在前),或者您可以(在某种程度上)在编译器设置、成员属性或结构属性中进行设置。

假设 32 位机器,4 字节整数:这仍然是 4 字节对齐在内存中(第一个最大的成员是 4 字节),但填充为 16 字节大小。

struct s_Stuff {
   int var1;  /* 4 bytes */
   short  var2;  /* 2 bytes */
   char padding[10];  /* ensure totals struct size is 16 */
}

编译器通常会填充每个 成员 以帮助自然对齐,但填充也可能位于结构的末尾。这是结构成员数据对齐

较旧的编译器结构成员对齐设置可能类似于下面这两张图片...但这与 数据对齐 不同,后者涉及内存分配和数据存储。

当 Borland 使用短语(来自图像)数据对齐,而 MS 使用 Struct 成员对齐时,这让我感到困惑。 (虽然它们都专门指结构成员对齐)

为了最大限度地提高效率,您需要为您的硬件(或本例中的向量处理)编写代码,因此假设 32 位、4 字节整数等。然后您想使用紧凑结构来节省空间,但填充结构可能提高速度。

struct s_Stuff {
   float f1;   /* 4 bytes */
   float f2;   /* 4 bytes */
   float f3;   /* 4 bytes */
   short  var2;  /* 2 bytes */
}

这个结构可以被填充以将结构成员对齐为 4 字节的倍数....除非您指定它保持单字节结构成员对齐,否则编译器将执行此操作em> - 所以 FILE 的大小可能是 14 字节,但在 MEMORY 中,这个结构的数组仍然是 16 字节(浪费了 2 字节),数据对齐未知(malloc 默认可能是 8 字节)但不能保证。如上所述,您可以在某些平台上使用_aligned_malloc 强制内存中的数据对齐)

同样关于结构中的member alignment,编译器将使用最大成员的倍数来设置对齐方式。或者更具体地说:

结构总是与最大类型的对齐方式对齐 要求

...来自here

如果您使用的是 UNION,那么您是正确的,它被强制为最大可能的结构,请参阅here

检查您的编译器设置是否也与您想要的结构成员对齐/填充相矛盾,否则您的结构的大小可能与您期望的不同。

现在,为什么它更快?请参阅here,它解释了对齐如何允许硬件传输离散的数据块并最大限度地利用传递数据的硬件。也就是说,数据不需要在每个阶段都被拆分或重新排列——通过硬件处理

通常,最好将您的编译器设置为与您的硬件(和平台操作系统)产生共鸣,以便您的对齐(和填充)最适合您的硬件处理能力。 32 位机器通常最好使用 4 字节(32 位)成员对齐方式,但随后以 4 字节成员对齐方式写入文件的数据可能会占用比预期更多的空间。

特别是关于 SSE 向量,正如 link 所述,4 * 4 字节是确保 16 字节对齐的最佳方式,也许像这样。 (他们在这里指的是数据对齐)

struct s_data {
   float array[4];
}

或只是floatsdoubles 的数组。

【讨论】:

【解决方案3】:

xx字节对齐意味着一个变量的内存地址模xx为0。

确保这是一个特定于编译器的操作,例如 Visual c++ 有 __declspec(align(...)),它适用于编译器分配的变量(例如在文件或函数范围内),对齐有点困难对于动态内存,您可以使用aligned_malloc,虽然您的库可能已经保证malloc的16字节对齐,但通常需要这样调用的更大对齐。

【讨论】:

    猜你喜欢
    • 2012-04-30
    • 1970-01-01
    • 2014-09-24
    • 2016-06-11
    • 1970-01-01
    • 2011-05-09
    • 2023-03-25
    • 1970-01-01
    相关资源
    最近更新 更多