Assembly 如何判断在x86-64程序集中是否输入了16字节对齐地址的循环?

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字节?(一个

对于x86-64,我是一个初学者,我正在努力做得更好,特别是在性能优化方面

我已经通读了这本书的部分内容。人们反复指出,以16字节对齐方式进入关键热点/环路是多么重要。现在我很难判断循环中的条目是否是16字节对齐的

您是否应该在循环条目之前将子例程中每条指令的字节开销相加,看看它是否可以被16整除? 我查阅了《英特尔x86-64开发人员手册》,读出来哪些指令的字节长度有问题。指令的字节大小仅仅是操作码的总和吗?因此,对于带有操作码REX.W+8C的MOV r64/m16,大小是否为2字节?(一个用于REX.W前缀,一个用于8C)

考虑以下代码,假设某个字符串作为rdi中的参数传递,该参数将在.lmanOperationLoop中操作:

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个字节
总的来说(假设我是对的)总共有5个字节。这是否意味着在.LmanipulationLoop之前我需要11个nop来确保进入循环的条目对齐?

您不需要手动执行此操作,汇编程序可以为您完成此操作。手动计算仅在您希望比仅使用NOP填充更聪明的情况下才有用,以便在插入填充点之后对齐某些内容

通常,您会在标签之前使用(GAS)或
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字节。A
REX.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