为什么和在何处 align 16 用于指令的 SSE 对齐?

Onu*_*mus 2 64-bit assembly sse

我正在阅读 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

...
Run Code Online (Sandbox Code Playgroud)

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

har*_*old 5

没有必要。它有时是有益的。

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

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

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