我发现x86 CPU在处理二进制操作指令(如SHR、BT、BTR、ROL等)时速度非常慢,这与它的二进制/双状态性质相反。
例如,我从某个地方读到,比特移位/旋转超过1个位置被认为是缓慢的(具有高延迟、性能损失和那些可怕的东西)。当操作数在内存中时,情况会更糟(内存不是双状态外设吗?)
shl eax,1 ;ok
shl eax,7 ;slow?
那么是什么让它们变慢了呢?具有讽刺意味的是,像CPU这样的二进制机器在比特操作上很慢,而这种操作本应是自然的。它给人的印象是二进制CPU很难将位移位到位!
编辑:现在再看一眼手册中的SHL条目,它确实涉及到一些沉重的微码逻辑!
从英特尔shl
第2卷手册。。。
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的表,其中显示了几乎所有位篡改指令的延迟为1。
一些篡改指令比较慢,下面是一些例子:
bt
、btr
、bts
和btc
在与内存操作数一起使用时较慢,因为它们执行(a)读取-修改-写入操作和(b)位串索引- 具有大于
1
的旋转量的rcr
是缓慢的,因为该指令几乎从不需要,因此没有被优化 pdep
和pext
在Intel上稍慢,在AMD上慢得多,可能是因为它们的实现非常复杂,并且将实现拆分会更容易
在旧的处理器(比如8086)上,CPU将占用与移位量一样多的周期,每个周期进行一次移位。这种实现方式允许ALU在没有任何额外硬件的情况下用于移位,从而减少了处理器所需的门的数量。据我所知,没有一个现代CPU具有这种性能行为。
只是一个注释。
shl eax,1 ; opcode: d1 e0
shl eax,7 ; opcode: c1 e0 07
实际上是具有不同操作码的不同指令,其可能由ALU的不同逻辑块处理。它们在汇编中使用相同的助记符,这可能会混淆,但从CPU的角度来看,它们是具有不同操作码和编码的不同指令。