_mm256_lddqu_si256 和 _mm256_loadu_si256 有什么区别

what's the difference between _mm256_lddqu_si256 and _mm256_loadu_si256

我一直在根据我在网上找到的示例使用 _mm256_lddqu_si256。后来我发现了_mm256_loadu_si256。 Intel Intrinsics 指南仅指出 lddqu 版本在跨越高速缓存行边界时可能表现更好。 loadu 的优点是什么?一般来说,这些功能有何不同?

没有理由使用 _mm256_lddqu_si256,将其视为 _mm256_loadu_si256 的同义词。 lddqu 仅出于历史原因而存在,因为 x86 朝着具有更好的未对齐向量加载支持的方向发展,而支持 AVX 版本的 CPU 运行 它们完全相同。没有AVX512版本。

Compilers do still respect the lddqu intrinsic 并发出该指令,因此如果您希望代码与 运行 相同但具有不同的校验和或机器代码字节,则可以使用它。


没有 x86 微架构 运行 vlddquvmovdqu 有任何不同。 IE。这两个操作码可能在所有 AVX CPU 上解码为相同的内部 uop。他们可能总是会,除非出现一些非常低功耗或专用的微体系结构而没有有效的未对齐矢量负载(自 Nehalem 以来一直是这样)。编译器在自动向量化时从不使用 vlddqu

lddqu 不同于 Pentium 4 上的 movdqu。参见 History of … one CPU instructions: Part 1. LDDQU/movdqu explained

lddqu 被允许(并且在 P4 上 )两个对齐的 16B 加载并获取该数据的 window。 movdqu 在架构上只从预期的 16 个字节加载。 这对存储转发有影响:如果您正在加载刚刚存储在未对齐存储中的数据,请使用 movdqu,因为存储转发仅适用于完全对齐的加载包含在以前的商店中。但除此之外,您通常总是想使用 lddqu。 (这就是为什么他们不只是让 movdqu 总是使用 "the good way",而是引入了一条让程序员担心的新指令。但对我们来说幸运的是,他们改变了设计,所以我们没有不用再担心使用哪个未对齐的加载指令了。)

它还对 UnCacheable (UC) 或 Uncacheable Speculate Write-combining (UCSW, aka WC) 内存类型(可能背后有 MMIO 寄存器)的可观察行为的正确性有影响。


两条 asm 指令的代码大小没有差异:

  # SSE packed-single instructions are shorter than SSE2 integer / packed-double
  4000e3:       0f 10 07                movups xmm0, [rdi]   

  4000e6:       f2 0f f0 07             lddqu  xmm0, [rdi]
  4000ea:       f3 0f 6f 07             movdqu xmm0, [rdi]

  4000ee:       c5 fb f0 07             vlddqu xmm0, [rdi]
  4000f2:       c5 fa 6f 07             vmovdqu xmm0, [rdi]
  # AVX-256 is the same as AVX-128, but with one more bit set in the VEX prefix

在 Core2 及更高版本上,没有理由使用 lddqu,但与 movdqu 相比也没有任何缺点。英特尔放弃了 Core2 的特殊 lddqu 东西,所以这两个选项同样糟糕。

特别是在 Core2 上,避免在具有两个对齐负载和 SSSE3 的软件中进行缓存行拆分 palignr 有时比 movdqu 更胜一筹,尤其是在第二代 Core2 (Penryn) 上 palignr 只是一个 shuffle uop 而不是 Merom/Conroe 上的 2 个。 (Penryn 将 shuffle 执行单元扩大到 128b)。

请参阅 Dark Shikaris 的 2009 年 x264 开发者博客 post:Cacheline splits, take two 了解更多关于过去糟糕时期未对齐加载策略的信息

Core2 之后的一代是 Nehalem,其中 movdqu 是在加载端口中具有专用硬件支持的单个 uop 指令。在指针对齐时告诉编译器仍然有用(特别是对于自动矢量化,尤其是没有 AVX),但它们在任何地方都使用 movdqu 并不是性能灾难,特别是如果数据实际上对齐在运行-时间。


我完全不知道为什么英特尔甚至制作了 lddqu 的 AVX 版本。我想对于解码器来说,在所有模式下(使用传统的 SSE 前缀,或使用 AVX128 / AVX256)将该操作码视为 movdqu / vmovdqu 的别名更简单,而不是将该操作码解码为其他带有 VEX 前缀的东西。

所有当前支持 AVX 的 CPU 都具有高效的硬件未对齐加载/存储支持,可以尽可能最佳地处理它。例如当数据在 运行 时间对齐时,与 vmovdqa.

的性能差异完全为零

Nehalem 之前不是这种情况; movdqulddqu 用于解码为多个 uop 以处理可能未对齐的地址,而不是将硬件支持放在加载端口中,单个 uop 可以激活它而不是在未对齐的地址上出错。

但是,Intel's ISA ref manual entry for lddqu 表示 256b 版本最多可以加载 64 个字节(取决于实现):

This instruction may improve performance relative to (V)MOVDQU if the source operand crosses a cache line boundary. In situations that require the data loaded by (V)LDDQU be modified and stored to the same location, use (V)MOVDQU or (V)MOVDQA instead of (V)LDDQU. To move a double quadword to or from memory locations that are known to be aligned on 16-byte boundaries, use the (V)MOVDQA instruction.

IDK 有多少是故意写的,有多少是在更新 AVX 条目时预先添加的 (V)。我不认为英特尔的优化手册建议真正在任何地方使用 vlddqu,但我没有检查。

没有vlddqu的AVX512版本,所以我认为这意味着英特尔已经决定替代策略未对齐加载指令不再有用,并且是甚至值得保留他们的选择权。