当前位置: 首页 > 知识库问答 >
问题:

如何确定在x86-64程序集中是否使用16字节对齐的地址进入循环?

邹高懿
2023-03-14

我已经通读了Agner优化手册第2卷的部分内容。人们反复声明,以16字节对齐方式进入关键热点/循环是多么重要。现在我很难确定循环中的一个条目是否是16字节对齐的。

你是不是应该在进入循环之前把子程序中每个指令的字节开销加起来,看看它是否可以被16除?我查阅了Intel x86-64开发人员手册,但很难从中读出哪些指令有哪些字节长度。指令的字节大小仅仅是操作码的总和吗?那么,在MOV R64/M16和操作码REX.W+8C的情况下,大小会是2字节吗?(一个代表rex.w前缀,一个代表8C)。

考虑下面的代码,假设一些字符串在rdi中作为参数传递,并在。lmerkinationloop中进行操作:

string_fun:
   cmp cl, byte ptr [rdi]
   jz .Lend
   xor rcx, rcx

.LmanipulationLoop
  *some string operathtml" target="_blank">ion*

.Lend
  ret
    null

共有1个答案

王英彦
2023-03-14

你不需要手动做这件事,汇编程序可以为你做这件事。只有当您想要比仅仅用NOP填充更聪明地在插入填充的点之后对齐一些东西时,手动计算才有用。

通常,您会在标签之前使用.p2align4(GAS)或align16(NASM1)来让汇编器计算出需要多少填充,并发出一个或多个长nop。(不是11个单字节NOP,这将是可怕的,因为它们必须分别解码)。

和/或使用调试器或反汇编器来检查标签地址,而不是手动计算它,如果您的目标是使用什么方法可以有效地延长现代x86上的指令长度?

实际上最重要的是uop缓存行的32字节边界。对于有循环缓冲区的CPU上的大多数小循环,或者根本没有(但请注意,Skylake/Kaby Lake的LSD被微代码更新禁用,以修复勘误)。如果可以避免从uop缓存中获取前端瓶颈,那么对一个非常关键的循环顶部的32字节对齐可能很有用。或者对于每次迭代可以运行1个循环的小循环,将整个循环放在同一uop缓存行中是必要的(否则前端每次迭代需要两个循环来获取它)。

不幸的是,在Skylake派生的CPU上,循环对齐的主要问题是对齐循环的底部,以绕过一个性能坑洞,在这个坑洞中,JCC或宏融合的compare+分支触及32字节边界会禁用该行的uop缓存。

我修复了源代码中的bug(标签后面缺少:,以及使用32位操作数大小为XOR为零的RCX的性能bug)。虽然在本例中,您可能希望异或rcx,但rcx只是为了使它更长,因为您知道将需要一些NOP字节。rex.w=0会更好,而且不会影响Silvermont的性能。

.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

使用gcc-wa、--keep-locals-c foo.s作为--keep-locals foo.s.
--keep-locals使.l标签在对象文件的符号表中可见。

然后使用objdump-drwc-Mintel foo.o反汇编:

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    

反汇编.o对象文件不会显示对外部函数调用的正常地址;它还没有链接,所以rel32位移没有被填充。但是-r将显示重新定位信息。并且源文件中的跳转在汇编时得到完全解析

%use smartalign
alignmode p6, 64
 类似资料:
  • 我在不同的地方读到过这样做是出于“性能原因”,但我仍然想知道这种16字节对齐方式在哪些特定情况下提高了性能。或者,无论如何,选择它的原因是什么。 编辑:我认为我写这个问题的方式有误导性。我不是在问为什么处理器使用16字节对齐的内存会更快,这在文档中随处都有解释。相反,我想知道的是,强制的16字节对齐如何优于在需要时让程序员自己对齐堆栈。我这样问是因为根据我在汇编方面的经验,堆栈强制有两个问题:它只

  • 这是一个用于x86处理器的简单dos汇编程序。这是一个简单的helloworld程序。 我不明白的是图像中下面的连续内存地址。程序似乎从十六进制中的地址0100开始,即256。下一个地址是258。差异似乎是2个字节。是不是这样指令(操作码地址)是2个字节? 然后再往下-mov dx指令似乎占用3个字节(0117-011A),而mov ah指令占用2个字节。 我以为指令(操作码地址)应该在内存中占用

  • 问题内容: 在Python中,确定IP地址(例如或)是否在专用网络上的最佳方法是什么?该代码听起来并不难编写。但是边缘情况可能比立即出现的情况还多,并且需要考虑对IPv6的支持,等等。是否有现有的库可以做到这一点? 问题答案: 签出IPy模块。如果具有的功能似乎可以满足您的要求:

  • 在回答中,我说过长期以来(在x86/x86_64上)未对齐的访问速度几乎与对齐的访问速度相同。我没有任何数字来支持这个说法,所以我为它创建了一个基准。 你看到这个基准有什么缺陷吗?你能改进它吗(我的意思是,增加GB/秒,以便更好地反映真相)?

  • 问题内容: 如何确定控制器中给定请求的IP地址?例如(快递): 问题答案: 您的对象中有一个称为的属性,它是一个对象。net.Socket对象具有一个property ,因此您应该可以通过以下调用获取IP: 请参阅http和net的文档 编辑 正如@juand在评论中指出的那样,如果服务器位于代理之后,则获取远程IP的正确方法是

  • 我想模拟x86/x86_64上禁止未对齐内存访问的系统。是否有一些调试工具或特殊模式来执行此操作? 当使用为SPARC或其他类似CPU设计的软件(C/C)时,我想在几台x86/x86_64PC上运行许多(CPU密集型)测试。但是我对Sparc的访问是有限的。 正如我所知,Sparc总是检查内存读写的对齐是否正常(从任何地址读取一个字节,但仅当地址可被4整除时才允许读取一个4字节的字)。 可能是Va