【问题标题】:x86 assembly idiomsx86 汇编习语
【发布时间】:2011-02-08 12:16:12
【问题描述】:

我一直在努力掌握 x86 汇编语言,并想知道是否存在与 movl $1, %eax 相当的快捷方式。那时我认为列出该语言中经常使用的习语可能是个好主意。

这可能包括优先使用xorl %eax, %eax 而不是movl $0, %eax,或testl %eax, %eax 而不是cmpl $0, %eax

哦,请在每个帖子中发布一个示例!

【问题讨论】:

  • movl $1, %eax 非常快速和简短。在某些处理器上,xorl %eax, %eax 实际上比movl $0, %eax 慢。在其他情况下,incl %eaxaddl $1, %eax 慢。如果你在 2010 年遇到了编写汇编的麻烦,你应该知道你正在编写什么架构,然后选择你的“方言”(与语言隐喻保持一致)。
  • @Pascal Cuoq,您能否解释一下影响这种性能差异的因素是什么?我对incl %eaxaddl $1, %eax 慢感到特别困惑。此外,如果您能指出一些详细说明此类行为的链接,我将不胜感激!
  • 对于 2010 年 xor eax 中的所有 x86 架构,eax 更快或等效,无论如何它更短。看看stackoverflow.com/questions/1396527/…。这几乎是自 486 年以来的事了。
  • 投票结束太广泛了。提到的个别例子已经在其他帖子中提出。

标签: assembly x86 idioms


【解决方案1】:

关于除以不变量整数的最早参考,不仅仅是逆乘法,这里是: Torbjörn Granlund of The Royal Institue of Technology in Stockholm。查看他的出版物

【讨论】:

    【解决方案2】:

    你不妨看看如何在汇编中进行优化。然后你必须问你在优化什么:大小还是速度?无论如何,这是我的“成语”,替代xchg

    xor eax, ebx
    xor ebx, eax
    xor eax, ebx
    

    【讨论】:

    • 警告: 如果 eax == ebx - 两者都将归零!
    • 你确定吗? 42 ^ 42 = 0 ; 42 ^ 0 = 42 ; 0 ^ 42 = 42
    【解决方案3】:

    这是另一个有趣的“成语”。希望每个人都知道,即​​使与乘法相比,除法也是一个很大的时间槽。使用一点数学,可以乘以常数的倒数而不是除以它。这超出了 shr 技巧。例如,除以 5:

    mov eax, some_number
    mov ebx, 3435973837    // 32-bit inverse of 5
    mul ebx
    

    现在 eax 已除以 5 而不使用慢速 div 操作码。这是从http://blogs.msdn.com/devdev/archive/2005/12/12/502980.aspx盗取的除法无耻有用常量列表

    3   2863311531
    5   3435973837
    7   3067833783
    9   954437177
    11  3123612579
    13  3303820997
    15  4008636143
    17  4042322161
    

    对于不在列表中的数字,您可能需要事先进行移位(除以 6,shr 1,然后乘以 3 的倒数)。

    【讨论】:

      【解决方案4】:

      在循环中...

        dec     ecx 
        cmp     ecx, -1       
        jnz     Loop              
      

        dec     ecx  
        jns     Loop 
      

      更快更短。

      【讨论】:

      • 循环 .Loop 不是更简单吗?
      • @Hasan Saad:可以,但 ti 较慢,不推荐在 x86 中使用循环。
      • 非常感谢 :) 我对此一无所知,因此感谢您提供的信息。高度赞赏:)
      【解决方案5】:

      扩展我的评论:

      对于像 Pentium Pro 这样的普通处理器,xorl %eax, %eax 似乎依赖于%eax,因此必须等待该寄存器的值可用。后来的处理器实际上有额外的逻辑来识别该指令没有任何依赖关系。

      指令incldecl 设置了一些标志,但保持其他标志不变。如果出于指令重新排序的目的将标志建模为单个寄存器,这是最糟糕的情况:任何在incldecl 之后读取标志的指令都必须被视为取决于incldecl(如果它正在读取该指令设置的标志之一)以及设置标志的前一条指令(如果它正在读取该指令未设置的标志之一)。一个解决方案是将标志寄存器分成两部分,并考虑与这种更细粒度的依赖关系......但 AMD 有一个更好的主意,并将这些指令完全从他们几年前提出的 64 位扩展中删除。

      关于链接,我在英特尔手册中发现了这一点

      【讨论】:

        【解决方案6】:

        不知道这是否算作成语,但在 i7 之前的大多数处理器上

        movq xmm0, [eax]
        movhps xmm0, [eax+8]
        

        或者,如果 SSE3 可用,

        lddqu xmm0, [eax]
        

        从未对齐的内存位置读取比

        更快
        movdqu xmm0, [eax]
        

        【讨论】:

          【解决方案7】:

          另一个(除了xor)用于

          mov eax, 0   ; B800000000h
          

          sub eax, eax ; 29C0h
          

          理由:更小的操作码

          【讨论】:

            【解决方案8】:

            使用 SHLSHR 进行 2 的乘/除

            【讨论】:

            • 它也可以扩展到其他数字。例如,y*320 = (y << 8) + (y << 6)。不过,这可能并不总是比简单的乘法更快。取决于您的处理器。
            【解决方案9】:

            使用LEA 表示例如乘法,例如:

            lea eax, [ecx+ecx*4]   
            

            对于 EAX = 5 * ECX

            【讨论】:

            • 顺便说一句:这在​​ NetBurst 上很慢,因为英特尔移除了桶形移位器以便能够获得更高的时钟速度。具有讽刺意味的是,在 P4 面世时,这仍然记录在英特尔的优化手册中。
            • 感谢您的评论。速度。我意识到成语不一定与优化相同。然而,作为一个习语,我认为 LEA 已被广泛(ab)使用。
            • 嗯,它一种优化。它甚至被英特尔官方推荐。只是,在官方推荐了 15 年之后,他们突然发布了一个新的 CPU,它的速度很慢,因此本质上需要重新编译 每个曾经编写的程序。值得庆幸的是,NetBurst 死得快而痛苦,并且所有当前的微架构都是 Pentium III 的演进,而不是 Pentium4,因此所有当前的 CPU 再次具有桶形移位器。基本上,自 80385 以来的 所有 Intel CPU 和所有 Athlon 都有它,只有 Pentium4 没有。
            【解决方案10】:

            在 x64 上:

            xor eax, eax 
            

            xor rax, rax
            

            (第一个也隐式清除了rax的上半部分,但操作码更小)

            【讨论】:

              猜你喜欢
              • 2023-03-25
              • 1970-01-01
              • 1970-01-01
              • 2015-04-05
              • 1970-01-01
              • 1970-01-01
              • 2017-01-03
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多