x86 SIMD 指令汇编中的 16 字节对齐没有 C 内在函数

问题描述

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

我知道您可以使用以下方法检查 16 字节对齐:

loading="lazy"

如果它不是 16 字节对齐的,还检查它是否是 8 字节对齐的(这意味着它是 8 的奇数倍)是否有益或有用?

test dil,0xf        ; rdi stores address of array

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

它是这样工作的吗?

解决方法

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

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

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

这使对齐情况的成本保持最小,并避免了分支预测错误的可能性。

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


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

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

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...