我想知道是否有一种无需使用MUL或DIV指令即可执行任何乘法或除法的方法,因为它们需要大量的CPU周期。我可以针对该目标利用SHL或SHR指令吗?如何实现汇编代码?

最佳答案

就像汇编中的所有其他内容一样,有很多方法可以进行乘法和除法。

  • multiplying by the reciprocal值除。
  • 使用移位和加/减而不是乘法。
  • 使用lea的地址计算选项(仅乘法)。

  • 神话破坏


    MULIMUL在现代CPU上非常快,请参见:http://www.agner.org/optimize/instruction_tables.pdfDIVIDIV一直非常缓慢。

    英特尔Skylake的示例(第217页):



    请注意,这是最大延迟要乘以两个64!位值。
    如果CPU要做的只是乘法,则每个CPU周期可以完成这些乘法之一。
    如果您认为上述示例使用移位并加乘以7的延迟时间为4个周期(使用lea则为3个周期)。没有真正的方法可以击败现代CPU上的普通乘法。

    乘以倒数

    根据Agner Fog's asm lib instruction page 12:



    当您需要除以常数或连续多次除以相同的变量时,乘以倒数非常有效。
    您可以在Agner Fog's assembly library中找到展示该概念的非常酷的汇编代码。

    移位和添加/订阅
    右移是除以两个shr-( R 得出)。
    左移是两个shl的乘积-( L arger)。
    您可以在进行加和减运算时校正非2的幂。
    //Multiply by 7
    mov ecx,eax
    shl eax,3    //*8
    sub eax,ecx  //*7
    

    使用该方法的除以2的幂以外的除法很快变得复杂。
    您可能想知道为什么我会以奇怪的顺序进行操作,但是我试图使dependency chain尽可能短,以使可以并行执行的指令数量最大化。

    使用Lea
    Lea是计算地址偏移量的指令。
    它可以在一条指令中计算2、3、4、5、8和9的倍数。
    像这样:
                          //Latency on AMD CPUs (K10 and later, including Jaguar and Zen)
                          //On Intel all take 1 cycle.
    lea eax,[eax+eax]     //*2     1 cycle
    lea eax,[eax*2+eax]   //*3     2 cycles
    lea eax,[eax*4]       //*4     2 cycles   more efficient: shl eax,2 (1 cycle)
    lea eax,[eax*4+eax]   //*5     2 cycles
    lea eax,[eax*8]       //*8     2 cycles   more efficient: shl eax,3 (1 cycle)
    lea eax,[eax*8+eax]   //*9     2 cycles
    

    但是请注意,带乘数(比例因子)的lea在从K10到Zen的AMD CPU上被视为“复杂”指令,并且具有2个CPU周期的延迟。在较早的AMD CPU(k8)上,即使使用简单的lea[reg+reg]寻址模式,[reg+disp8]始终具有2个周期的延迟。

    AMD
    Agner Fog的指令表不适用于AMD Zen:根据InstLatx64(http://instlatx64.atw.hu/),三分量或缩放索引LEA在Zen上仍为2个周期(每个时钟吞吐量只有2个周期,而不是4个)。同样,像早期的CPU一样,在64位模式下,lea r32, [r64 + whatever]具有2个周期的延迟。因此,在AMD CPU上使用lea rdx, [rax+rax]而不是lea edx, [rax+rax]实际上更快,这与Intel的将结果截断为32位是免费的不同。

    * 4和* 8可以使用shl更快地完成,因为简单的移位仅需一个周期。

    从好的方面来说,lea不会更改标志,它允许自由移动到另一个目标寄存器。
    因为lea只能向左移位0、1、2或3位(也就是乘以1、2、4或8),所以这是您得到的唯一中断。

    英特尔
    在Intel CPU(Sandybridge系列)上,任何2组件LEA(仅一个+)都具有单周期延迟。因此,lea edx, [rax + rax*4]具有单周期延迟,但是lea edx, [rax + rax + 12]具有3个周期延迟(并且吞吐量更差)。在C++ code for testing the Collatz conjecture faster than hand-written assembly - why?中详细讨论了这种折衷的示例。

    关于assembly - 程序集8086-在没有MUL和DIV指令的情况下实现任何乘法和除法,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/27922579/

    10-13 02:28