如果 CPU 是一个二进制机器,为什么它在位操作上很慢?

If CPU is a binary machine, why is it slow on bit manipulations?

我发现与其二进制/双态性质相反,x86 CPUs 在处理二进制操作指令(例如 SHR、BT、BTR、ROL 和类似指令)时非常慢。

例如,我从某个地方读到,位移/旋转超过 1 个位置被认为是缓慢的(具有高延迟、性能损失和那些可怕的东西)。当操作数在内存中时更糟(内存双态外围设备不也是吗?)

shl eax,1  ;ok
shl eax,7  ;slow?

那么是什么让它们变慢了?具有讽刺意味的是,像 CPU 这样的二进制机器在位操作上很慢,而这种操作本应是自然的。它给人的印象是二进制 CPU 很难将位移位到位!

编辑:现在在再次查看手册中的 SHL 条目后,它确实涉及一些繁重的微代码逻辑!

Intel's vol.2 manual for shl...

Operation
TemporaryCount = Count & 0x1F;
TemporaryDestination = Destination;
while(TemporaryCount != 0) {
    if(Instruction == SAL || Instruction == SHL) {
        CF = MSB(Destination);
        Destination = Destination << 1;
    }
    //instruction is SAR or SHR
    else {
        CF = LSB(Destination);
        if(Instruction == SAR) Destination = Destination / 2; //Signed divide, rounding toward negative infinity
        //Instruction is SHR
        else Destination = Destination / 2; //Unsigned divide
    }
    TemporaryCount = TemporaryCount - 1;
}
//Determine overflow
if(Count & 0x1F == 1) {
    if(Instruction == SAL || Instruction == SHL) OF = MSB(Destination) ^ CF;
    else if(Instruction == SAR) OF = 0;
    //Instruction == SHR
    else OF = MSB(TemporaryDestination);
}
else OF = Undefined;

难以置信地看到如此简单的布尔代数变成了实现的噩梦。

这只是指令的伪代码,具体说明了指令的作用。该指令实际上并不是这样实现的。实际上,所有现代 CPU 都有桶形移位器或类似的东西,允许它们在单个周期内移动任意量。例如,请参阅 Agner Fog's tables,其中显示几乎所有位摆弄指令的延迟为 1。

一些位摆弄指令比较慢,这里有一些例子:

    由于 (a) 读-修改-写操作和 ( b) 他们做的位串索引
  • rcr 旋转量大于 1 的速度很慢,因为几乎从不需要这条指令,因此没有优化
  • pdeppext 在 Intel 上稍微慢一些,在 AMD 上慢得多,可能是因为它们的实现非常复杂,将实现分开会更容易。

在旧处理器(例如 8086)上,CPU 将花费与移位量一样多的周期,每个周期进行一次移位。这种实现方式允许 ALU 用于移位而无需任何额外的硬件,从而减少了处理器所需的门数。我所知道的现代 CPU 没有这种性能行为。

请注意。

shl eax,1 ; opcode: d1 e0
shl eax,7 ; opcode: c1 e0 07

实际上是具有不同操作码的不同指令,可能由 ALU 的不同逻辑块处理。它们在汇编中使用相同的助记符,这可能会造成混淆,但从 CPU 的角度来看,它们是具有不同操作码和编码的不同指令。