在 GCC 上设置打包的 long long 的正确对齐以与 avx2 指令一起使用

Posted

技术标签:

【中文标题】在 GCC 上设置打包的 long long 的正确对齐以与 avx2 指令一起使用【英文标题】:Setting proper alignment of packed long long on GCC to use use with avx2 instructions 【发布时间】:2019-08-10 10:34:42 【问题描述】:

简介:

我正在编写一个函数来使用AVX2 指令处理x86_64 程序集中的4 个压缩long long int。这是我的头文件的样子:

avx2.h

#define AVX2_ALIGNMENT 32

// Processes 4 packed long long int and 
// returns a pointer to a result 
long long * process(long long *);

process 函数的汇编实现如下:

avx2.S:

global process

process:
    vmovaps ymm0, [rdi]
    ;other instructions omitted

vmovaps ymm0, [rdi] 要求 rdi 是 32 字节对齐的。在汇编中它由align 32 指令控制。

问题

当使用 GCC 编译时,它具有 __BIGGEST_ALIGNMENT__ 定义,在我的实现中是 16。6.2.8/3 的 C18 标准声称

扩展对齐由大于的对齐表示 _Alignof (max_align_t)。是否支持任何扩展对齐以及存储持续时间是由实现定义的 支持。

所以 GCC 上实现定义的扩展对齐也是 16,我不确定代码是否会导致 UB:

#include "avx2.h"

//AVX2_ALIGNMENT = 32, __BIGGEST_ALIGNMENT__ = 16
_Alignas(AVX2_ALIGNMENT) long long longs[] = 1, 32, 432, 433;
long long *result = process(longs);

有没有办法在没有 UB 的情况下重写代码? (我知道内在的immintrin.h,这不是问题的主题)。

【问题讨论】:

您当然可以只使用vmovups 而不是vmovaps 并忘记对齐。 @PaulR 我刚刚检查了vmovaps/vmovups 的延迟/TP/uops 并注意到它们在Skylake 上几乎相同(都具有 10c 延迟和 2uops)所以很可能vmovups 应该是可取的......不是吗? 如果您的数据恰好是 32 字节对齐的,那么性能应该没有明显差异。如果它没有对齐,那么由于相当微妙的缓存/内存问题,您可能会看到相对较小的性能差异,这在您的用例中可能很重要,也可能不重要,具体取决于代码对性能的关键程度,您的内存访问模式,以及在初始加载后您正在执行多少计算。 【参考方案1】:

您的代码已经没有 UB。任何体面的编译器都会在它不支持的 _Alignas() 上出错。

请注意,该标准表示此支持的存在/不存在是实现定义的。它没有在任何地方提到UB。 实现应该知道它支持什么,并在编译时检查它是否可以支持给定的_Alignas

猜测一个糟糕的低质量实现会决定_Alignas() 的过高值是 UB。我还没有真正检查过。


可以编译此代码的实现 (gcc/clang/MSVC/ICC) 都至少支持 _Alignas(256) 用于自动和静态存储,AFAIK。 (我遗漏了可能仍然存在并且可能支持 AVX2 的 SunCC。我认为它也很好,但我没有查看它的 asm 输出) 可能几乎是任意大的,尤其是对于静态存储。

所有这些编译器都知道如何将堆栈过度对齐到 32 或 64,因此除了堆栈大小限制之外,没有理由不能将堆栈过度对齐。

可以安全地假设每个支持 Intel 内在函数的编译器也支持 _Alignas() 的扩展对齐,至少达到几个缓存行的大小。

(仅供参考,您可以#include <alignof.h>,因此您可以像在 C++ 中一样使用alignas())。


警告:__m256 变量的 MinGW 堆栈对齐

最后我听说,MinGW 还是坏了。它知道如何为_Alignas(32) 对齐堆栈,但无法为__m256/__m256i/d 变量这样做,可能会以未对齐的vmovaps 溢出/重新加载它们。

或者类似的东西。如果你关心 MinGW,最好看看这个。或者只在面向 Windows 时使用 clang。

【讨论】:

实现应该知道它支持什么,并在编译时检查它是否支持给定的 _Alignas。 我用我的 gcc-7.4.0 和允许的最大对齐方式对其进行了测试运行良好的是8192 * 8192 * 4。尝试设置_Alignas(8192 * 8192 * 8) 会导致error: requested alignment is too large @St.Antario:哦,太好了,感谢您测试 gcc 至少按我预期的方式工作。

以上是关于在 GCC 上设置打包的 long long 的正确对齐以与 avx2 指令一起使用的主要内容,如果未能解决你的问题,请参考以下文章

为啥gcc会有一个long long的警告?

GCC 发出的那两个 long in vtable 汇编代码是啥? [复制]

Tar大量数据打包-bash: /bin/tar: Argument list too long

在 C 中给出 unsigned long long 变量值的警告

如何在 Android 模拟器中手动选择 lat 和 long 而不是选择地图上的点,

GCC 优化对位操作的有效性