【问题标题】:x86 SIMD instructions 16 byte alignment in assembly (Without C intrinsics)x86 SIMD 指令汇编中的 16 字节对齐(无 C 内在函数)
【发布时间】:2021-04-15 15:10:20
【问题描述】:

假设我有一个由 8 字节 元素组成的数组,该数组从内存中传递给我的汇编函数,长度为 未知长度。我想对其进行一些 128 位 SIMD 操作(最高 SSE4)。内存最好是 16 字节对齐的。所以我会检查数组是否对齐,然后根据使用 movapsmovups

我知道您可以检查 16 字节对齐方式:

test dil, 0xf        ; rdi stores address of array

如果它不是 16 字节对齐的,那么检查它是否是 8 字节对齐是否很好或有用,这意味着它是 8 的奇数倍?

test dil, 0x7           ; ZF=1 here after rdi&0xf !=0 implies rdi%16 == 8

如果这是真的,那么我应该对数组的第一个元素执行额外的步骤,然后 movaps 来加载数组元素吗?否则我应该只使用像 movups 这样的未对齐操作吗?

这样可以吗?

【问题讨论】:

  • 你一次问了很多问题。通常,每个 Stack Overflow 问题都应该问一个问题。也就是说,是的,值得一开始就确保对齐。也就是说,要检查 8 字节对齐,请使用 test rdi, 0x7 而不是 0x8
  • @fuz:根据文本,我认为他们实际上想要检查 8 的奇数倍,即距离 16 字节对齐的 1 个元素。 test dil, 8 仅在您与 test dil, 0x7 建立 8 字节对齐后才有效,所以是的,在发现它不是 16 的倍数后,它就被打破了。ptr & 8 可能适用于从对齐边界偏移 15 个字节的指针,而不是 8 个。经过编辑以修复问题中的代码,因此答案可以在不分散注意力的情况下谈论问题的真正意义。
  • 如果你有一个 pre-Nehalem Intel CPU,你不应该使用movups,因为将负载分成上半部分和下半部分实际上更便宜(使用movhps/movlps /movsd)。您期望不是 8 字节对齐的内存的可能性有多大?您的数组长度通常是什么数量级? (
  • @chtz 用于学校项目,未指定非 8 字节对齐的概率,但顺序肯定大于 100 字节
  • @trollinator 获得有关性能编程的好建议的最佳方法是解释操作的上下文是什么。这样,就有可能了解您尝试做什么以及您的限制是什么。通常,必须通过从不同的角度看待问题来解决性能问题。如果不提供上下文,您就否认了这种可能性,并且除了最一般的回答之外很难给出任何内容。

标签: assembly x86-64 sse simd memory-alignment


【解决方案1】:

如果您的数组通常按 16 对齐,最好不要进行更多检查以查找奇数开始的情况,只需使用未对齐的版本,除非由于某种原因变得更糟.

但是,如果它们通常按 8 对齐(但不知道它们是否按 16 对齐),那么 您可能只需检查 8 对齐并无分支地处理可能未对齐的对齐情况的第一次迭代,见下文。 (否则就回退到完全不对齐的情况。)

如果重叠不是问题(例如 c[] = a[]+b[],或类似 memset 的存储或其他),一个好的技术是总是先做加载/存储未对齐的向量,然后前进到第一个对齐的向量 (add rdi, 16 / and rdi, -16)。如果输入是对齐的,则不会重叠。否则,它会部分重叠,并且存储缓冲区 + L1d 缓存会有效地处理它。

这使得对齐案例的成本最小化,并避免了分支错误预测的机会。

将指针向上/向下舍入到对齐边界很便宜,只需 and,但您确实需要剥离整个循环体副本的代码大小成本。所以就启动开销而言,它并不是完全免费的,但至少这种启动开销可以与数据中的缓存未命中重叠。


但请注意,许多 SIMD 函数具有多个指针输入,这些指针输入可能会相互错位。在这种情况下,标准建议是对齐输出并继续使用movups 作为输入。虽然如果前端是瓶颈,您可能会选择达到输入的对齐边界,这样您就可以将内存源操作数折叠成 ALU 指令,如 xorps xmm0, [rdi] 并使用 movups 存储。

但如果不是前端,例如缓存或内存吞吐量,是一个瓶颈,那么你更经常想要对齐目标。英特尔的优化手册对此有一些建议。部分原因是负载吞吐量通常是存储吞吐量的 2 倍(直到 IceLake),因此负载硬件更容易吸收拆分负载的额外工作。此外,使用较少的存储存储完整的缓存行可以帮助减少行被驱逐(写回)但随后您再次存储到它并且必须被获取+弄脏并最终再次写回的情况,而不仅仅是获取。

【讨论】:

  • 我假设,OP 甚至不能保证内存至少 8 字节对齐(即重叠加载/存储无济于事)
  • @chtz:谢谢,我忘了描述这如何让你最小化分支:这意味着你只需要检查test dil, 7 来检查 8 字节对齐,然后无分支地落入你的对齐 - by-16 循环。否则,您将跳转到未对齐的循环版本。不过,拥有 2 个版本并不总是值得的,但是是的,谢谢,如果 elements 可能无法自然对齐,您需要一个后备(如果您选择完全对齐的版本)除非该操作是纯按位运算(如 XOR),否则元素边界实际上并不重要。
猜你喜欢
  • 1970-01-01
  • 2016-05-28
  • 2020-12-04
  • 2017-12-21
  • 1970-01-01
  • 2011-08-18
  • 2015-09-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多