为什么以及在何处使用 align 16 来进行指令的 SSE 对齐?

Why and where align 16 is used for SSE alignment for instructions?

我正在阅读 Apress 的现代 x86 汇编语言书籍。对于 64 位 SSE 示例的编程,作者将 align 16 放在代码中的特定点。例如

    .code
ImageUint8ToFloat_ proc frame
_CreateFrame U2F_,0,64               ; helper macros to create prolog
_SaveXmmRegs xmm10,xmm11,xmm12,xmm13 ; helper macros to create prolog

_EndProlog  ; helper macros to create prolog

...

shrd r8d,
pxor xmm5,xmm5

align 16  ; Why this is here ?
@@:
movdqa xmm0,xmmword ptr [rdx]
movdqa xmm10,xmmword ptr [rdx+16]

movdqa xmm2,xmm0
punpcklbw xmm0,xmm5
punpckhbw xmm2,xmm5
movdqa xmm1,xmm0
movdqa xmm3,xmm2

...

作者解释说有必要放置 align 16 因为我们使用 SSE 以便指令本身对齐。没关系。我的问题是为什么作者选择将 align 16 放置到该特定位置。作为程序员,我应该如何决定 align 16 的正确位置?为什么不早点或晚点?

没有必要。偶尔有用。

现代处理器以 16(或者可能是 32,某种程度上,AMD 做的很奇怪)字节为单位获取代码。当然是对齐的。如果你在这样一个块的末尾附近跳转,你会浪费大部分的获取,并且在那个周期中你只会解码 1 条或许多 0 指令。这是一个巨大的浪费,所以最好跳到块的开头。

这并不总是重要的,例如代码是否在循环缓冲区或 µop 缓存(如果存在)中。通常几乎所有循环都适合 µops 缓存,在早于 SandyBridge 的处理器上,很容易创建一个不适合循环缓冲区的循环,这使得获取吞吐量很重要。即使循环可以放入循环缓冲区,对齐仍然对 Core2 有帮助,因为未对齐有效地使循环缓冲区变小(它基于 16 字节代码块,在预解码后缓存)。还有一些更奇怪的细节,但都是关于古老的 µarchs 的,所以我将跳过它。关键是,在像 Nehalem 和更老的 µarchs 上,你应该经常对齐循环。

虽然从片段中看不是很清楚,但看起来他们已经对齐了一个标签,它将循环返回。所以它正在对齐循环。这对现代 µarchs 并不重要。