位移O(1)还是O(n)?

时间:2012-01-31 17:05:43

标签: language-agnostic big-o cpu hardware bit-shift

轮班操作O(1)O(n)

计算机通常需要更多操作来移动31个位置而不是移动1个位置才有意义吗?

或者,无论我们需要转移多少个地方,转移所需的操作次数常数是否有意义?

PS:想知道硬件是否是合适的标签..

7 个答案:

答案 0 :(得分:15)

barrel shifter允许在O(log n)次传递中执行转换 - 这可以在同一时钟周期内完成,从而转移O(1)操作。

答案 1 :(得分:12)

某些指令集限制为每条指令一位移位。并且一些指令集允许您指定在一条指令中移位的任意位数,这通常在现代处理器上需要一个时钟周期(现代是有意模糊的字)。请参阅dan04's answer关于桶形移位器的信息,该电路在一次操作中移位多于一位。

这一切都归结为逻辑算法。结果中的每个位都是基于输入的逻辑功能。对于单个右移,算法将类似于:

  • 如果指令是[右移]且输入的第1位为1,则结果的第0位为1,否则第0位为0.
  • 如果指令是[右移],则位1 =位2。
  • 等。

但逻辑方程式可以很容易:

  • 如果指令为[右移]且操作数的数量为1,则结果位0 =移位输入位1.
  • 如果金额为2,则位0 =位2。
  • 等等。

逻辑门是异步的,可以在一个时钟周期内完成所有这些操作。然而,如果您要比较的是这两种指令,那么单一移位确实可以实现更快的时钟周期和更少的门限。或者替代方案是需要更长时间才能解决,因此指令需要2或3个时钟或其他任何时间,逻辑计数为3然后锁存结果。

例如,MSP430只有单位向右旋转指令(因为你可以执行单位移位或向左旋转另一条指令,我将留给读者弄清楚。)

ARM指令集允许基于立即和基于寄存器的多位旋转,算术移位和逻辑移位。我认为只有一个实际的旋转指令,另一个是别名,因为左旋转1与右旋转32相同,你只需要一个方向的桶形移位器来实现多位旋转。

x86中的SHL允许每条指令多个位,但过去需要多个时钟。

等等,您可以轻松地检查那里的任何指令集。

你的问题的答案是它没有修复。有时它是一个操作,一个循环,一个指令。有时它是一个指令多个时钟周期。有时它是多个指令,多个时钟周期。

编译器经常针对这些事情进行优化。假设您有一个带有交换字节指令的16位寄存器指令集和带有立即数的AND指令,但只有一个位移位。您可能认为移位8位需要8个移位指令周期,但您可以只交换字节(一条指令)然后将下半部分与零交叉(可能需要两条指令,或者可能是两个字的可变字长指令,或者它可能编码为单个指令)所以它只需要2或3个指令/时钟周期而不是8个。对于9位的移位,你可以做同样的事情并添加一个移位,使其成为9个时钟对3或4个此外,在某些体系结构中,乘以256比移动8等更快等等。每个指令集都有其自身的局限和技巧。

即使大多数指令集对单个位提供多位或大多数限制也是如此。属于“计算机”类别的处理器,如X86,ARM,PowerPC和MIPS,将倾向于一个转换操作。扩展到所有处理器,但不一定是今天常用的“计算机”,并且它转向另一种方式,我会说它们更多是单比特而不是多比特,因此需要多个操作来执行多比特移位。

答案 2 :(得分:8)

如前所述,桶形移位器可以在恒定时间内将操作数移动任意距离。然而,桶形移位器在CPU芯片上占用相当大的空间,因此它们不包含在所有CPU设计中。

仅举一个众所周知的例子,英特尔奔腾III包括一个桶形移位器 - 但奔腾IV确实。为奔腾III编写的代码假设有一个桶形移位器,有时在Pentium IV上放慢了相当多的速度。我有一些加密代码(包括许多移位和旋转),在1.2 GHz奔腾III上运行速度比在2.8 GHz奔腾IV上快4倍。

答案 3 :(得分:7)

几乎每个当前处理器上的位移都是O(1)。

例如,看看x86“shrw”指令。第一个操作数(在AT& T语法中)是要移位的位数。 编译器如何实现移位取决于编译器,但是当处理器一次性移位N位时,将移位置于循环中是愚蠢的。

附录: 回复:“他们需要更多的操作才能向左移31?” 有不同类型的移位(如果您想知道为什么,请考虑如何处理从寄存器移出的位),但大多数处理器可以执行与GPR可以存储的位数相同的单指令移位。要在32位寄存器上进行40位移位,需要在多个寄存器之间进行移位(这假设在2个32位寄存器中存储了64位数字),这在我知道的每个处理器上都需要更多指令。它仍然是O(1),可能不是1个时钟。 作为一个有趣的侧面说明,Pentium IV处理器的位移速度非常慢。这具有讽刺意味,因为英特尔历来建议通过位移优化^ 2除法和乘法。如果感兴趣,请参阅:this PDF和Google以获取更多信息。

答案 4 :(得分:2)

对于普通硬件,无论您移动多少个位置,固定大小都会保持不变。

另请注意,O符号的使用在这里非常奇怪,您通常会使用它来表示基于数字的算法复杂度,而不是要移位的位数。

答案 5 :(得分:1)

哎呀,出于对c#好奇的考验,得到了有趣的结果。

var sw = Stopwatch.StartNew();
long l = 1;
for (long i = 0; i < 20000000; i++) {
    l = l << 60; l = l >> 60;
    l = l << 60; l = l >> 60;
    l = l << 60; l = l >> 60;
    //...
    // 50 of ^them^ total

}
Console.WriteLine(l + " " + sw.Elapsed);

我的电脑需要1.2秒。但如果我更换

l = l << 60; l = l >> 60;

l = l << 1; l = l >> 1;

然后时间增加到2.0秒。不知道这里有什么样的优化,但看起来很奇怪。

答案 6 :(得分:0)

根据表C-17,作为一个具体示例。 《 英特尔®64和IA-32架构优化参考手册》 的通用说明

SAL/SAR/SHL/SHR reg, imm   1 cycle latency
SAL/SAR/SHL/SHR reg, cl    1.5 cycles latency

所以这仍然是一个常数,并且O(1.5)= O(1)。可能有更简单的微体系结构作为异常值,但通常为O(1)。