Assembly 如何判断在x86-64程序集中是否输入了16字节对齐地址的循环?
对于x86-64,我是一个初学者,我正在努力做得更好,特别是在性能优化方面 我已经通读了这本书的部分内容。人们反复指出,以16字节对齐方式进入关键热点/环路是多么重要。现在我很难判断循环中的条目是否是16字节对齐的 您是否应该在循环条目之前将子例程中每条指令的字节开销相加,看看它是否可以被16整除? 我查阅了《英特尔x86-64开发人员手册》,读出来哪些指令的字节长度有问题。指令的字节大小仅仅是操作码的总和吗?因此,对于带有操作码REX.W+8C的MOV r64/m16,大小是否为2字节?(一个用于REX.W前缀,一个用于8C) 考虑以下代码,假设某个字符串作为rdi中的参数传递,该参数将在.lmanOperationLoop中操作:Assembly 如何判断在x86-64程序集中是否输入了16字节对齐地址的循环?,assembly,optimization,x86-64,memory-alignment,micro-optimization,Assembly,Optimization,X86 64,Memory Alignment,Micro Optimization,对于x86-64,我是一个初学者,我正在努力做得更好,特别是在性能优化方面 我已经通读了这本书的部分内容。人们反复指出,以16字节对齐方式进入关键热点/环路是多么重要。现在我很难判断循环中的条目是否是16字节对齐的 您是否应该在循环条目之前将子例程中每条指令的字节开销相加,看看它是否可以被16整除? 我查阅了《英特尔x86-64开发人员手册》,读出来哪些指令的字节长度有问题。指令的字节大小仅仅是操作码的总和吗?因此,对于带有操作码REX.W+8C的MOV r64/m16,大小是否为2字节?(一个
string_fun:
cmp cl, byte ptr [rdi]
jz .Lend
xor rcx, rcx
.LmanipulationLoop
*some string operation*
.Lend
ret
根据我目前的理解:
- cmp cl,字节ptr[rdi],其操作码为0x38(cmp r/m8,r8),因此为1字节
- Lend,这个操作码是0x0F 84(jz rel32),所以是2字节(我不确定这是不是正确的操作码)
- xor rcx,rcx,这个的操作码是REX.W+0x33(xor r64,r/m64),所以是2个字节
align 16
(NASM1),让汇编程序计算出需要多少填充,并发出一个或多个长NOP。(不是11个单字节的NOP,那太可怕了,因为它们必须分别解码)
和/或使用调试器或反汇编程序检查标签地址,而不是手动计算它,如果您的目标是
如果要尽量减少所需的NOP数量,了解哪些指令是什么长度是很有用的,但在这种情况下,一些尝试/错误可以很好地找到一个好的指令序列,从而使您最多需要一个长的NOP
在具有uop缓存的CPU上,并不总是需要对齐循环顶部
通常真正重要的是uop缓存线的32字节边界。或者根本不适用于CPU上具有循环缓冲区的大多数小循环(但请注意,Skylake/Kaby Lake的LSD被微码更新禁用,以修复错误)。如果可以避免从uop缓存获取前端瓶颈,那么非常关键的循环顶部的32字节对齐可能非常有用。或者对于每个迭代可以运行1个周期的小循环,将整个循环放在同一个uop缓存线中是至关重要的(否则前端每次迭代需要两个周期来获取它)
不幸的是,Skylake衍生CPU上的循环对齐的主要问题是对齐循环的底部,以绕过性能瓶颈
简单对齐示例: 我修复了源代码中的错误(标签后缺少
:
,以及将32位操作数大小设置为xor zero RCX的性能错误)。虽然在本例中,您可能希望使用xor rcx,但rcx只是为了使其更长,因为您知道需要一些NOP字节。AREX.W=0
会更好,但不是这样
我在占位符中填入了SIMD加载
.intel_syntax noprefix
.p2align 4 # align the top of the function
string_fun:
cmp cl, byte ptr [rdi]
jz .Lend
xor ecx, ecx # zeroing ECX implicitly zero-extends into RCX, saving a REX prefix
lea rsi, [rdi + 1024] # end pointer
# .p2align 4 # emit padding until a 2^4 boundary
.LmanipulationLoop: # do {
movdqu xmm0, [rdi]
# Do something like pcmpeqb / pmovmskb with the string bytes ...
add rdi, 16
cmp rdi, rsi
jb .LmanipulationLoop # }while(p < endp);
.Lend:
ret
或者在.p2align 4
未注释的情况下,汇编程序发出一个3字节的NOP:
通常你会在标签前使用
.p2align 4
,让汇编程序计算出需要多少填充,并发出一个或多个长NOP(不是11个单字节NOP,那太糟糕了)。好的,我明白了。这会在集会时发生,对吗?如果我怀疑某个地方发生了偏差,你只需要使用其他方法和更多的度量来找出?
0000000000000000 <string_fun>:
0: 3a 0f cmp cl,BYTE PTR [rdi]
2: 74 16 je 1a <.Lend>
4: 31 c9 xor ecx,ecx
6: 48 8d b7 00 04 00 00 lea rsi,[rdi+0x400]
# note address of this label,
# or without --keep-locals, of the instruction that you know is the loop top
000000000000000d <.LmanipulationLoop>:
d: f3 0f 6f 07 movdqu xmm0,XMMWORD PTR [rdi]
11: 48 83 c7 10 add rdi,0x10
15: 48 39 f7 cmp rdi,rsi
18: 72 f3 jb d <.LmanipulationLoop> # note the jump target address
000000000000001a <.Lend>:
1a: c3 ret
0000000000000000 <string_fun>:
0: 3a 0f cmp cl,BYTE PTR [rdi]
2: 74 19 je 1d <.Lend>
4: 31 c9 xor ecx,ecx
6: 48 8d b7 00 04 00 00 lea rsi,[rdi+0x400]
d: 0f 1f 00 nop DWORD PTR [rax] # This is new, note that it's *before* the jump target
0000000000000010 <.LmanipulationLoop>:
10: f3 0f 6f 07 movdqu xmm0,XMMWORD PTR [rdi]
14: 48 83 c7 10 add rdi,0x10
18: 48 39 f7 cmp rdi,rsi
1b: 72 f3 jb 10 <.LmanipulationLoop>
000000000000001d <.Lend>:
1d: c3 ret
%use smartalign
alignmode p6, 64