相关疑难解决方法(0)

如何安排x86 uops?

现代x86 CPU将传入的指令流分解为微操作(uops 1),然后在输入准备就绪时将这些uop 无序调度.虽然基本思路很清楚,但我想了解准备好指令的具体细节,因为它会影响微优化决策.

例如,采取以下玩具循环2:

top:
lea eax, [ecx + 5]
popcnt eax, eax
add edi, eax
dec ecx
jnz top
Run Code Online (Sandbox Code Playgroud)

这基本上实现了循环(具有以下对应关系:) eax -> total, c -> ecx:

do {
  total += popcnt(c + 5);
} while (--c > 0);
Run Code Online (Sandbox Code Playgroud)

通过查看uop细分,依赖链延迟等,我熟悉优化任何小循环的过程.在上面的循环中,我们只有一个携带的依赖链:dec ecx.环路(前三指令lea,imul,add)是开始新鲜每个环一个依赖关系链的一部分.

决赛decjne融合.因此,我们总共有4个融合域uop,以及一个仅循环携带的依赖链,延迟为1个周期.因此,基于该标准,似乎循环可以在1个周期/迭代时执行.

但是,我们也应该关注港口压力:

  • lea能够在端口1和5执行
  • popcnt可以在端口1上执行
  • add可以在端口0,1,5和6执行
  • 预测采用jnz在端口6上执行

因此,要进行1次循环/迭代,您几乎需要执行以下操作:

  • popcnt 必须在端口1上执行(它可以执行的唯一端口)
  • lea 必须 …

optimization performance x86 intel cpu-architecture

32
推荐指数
2
解决办法
2907
查看次数

在某些CPU的紧密循环中出现ADC/SBB和INC/DEC问题

我在Delphi中编写一个简单的BigInteger类型.它主要由TLimb的动态数组组成,其中TLimb是32位无符号整数,32位大小字段,它还保存BigInteger的符号位.

要添加两个BigIntegers,我创建一个适当大小的新BigInteger然后,在一些簿记之后,调用以下过程,将三个指针传递给左右操作数和结果的数组的相应开始,以及左右肢的数量分别为.

普通代码:

class procedure BigInteger.PlainAdd(Left, Right, Result: PLimb; LSize, RSize: Integer); 
asm
// EAX = Left, EDX = Right, ECX = Result
        PUSH    ESI
        PUSH    EDI
        PUSH    EBX
        MOV     ESI,EAX                 // Left
        MOV     EDI,EDX                 // Right
        MOV     EBX,ECX                 // Result
        MOV     ECX,RSize               // Number of limbs at Left
        MOV     EDX,LSize               // Number of limbs at Right
        CMP     EDX,ECX
        JAE     @SkipSwap
        XCHG    ECX,EDX                 // Left and LSize should be largest
        XCHG    ESI,EDI                 // so swap
@SkipSwap:
        SUB     EDX,ECX                 // …
Run Code Online (Sandbox Code Playgroud)

delphi x86 assembly

15
推荐指数
2
解决办法
919
查看次数