为什么LOOP这么慢?

时间:2014-02-04 22:38:09

标签: assembly

这让我很吃惊,因为我一直认为loop应该有一些内部优化。

以下是我今天做的实验。我使用的是Microsoft Visual Studio 2010.我的操作系统是64位Windows 8.我的问题就在最后。

第一个实验:

平台:Win32
模式:调试(禁用优化)

begin = clock();
_asm
{
    mov ecx, 07fffffffh
start:
    loop start
}
end = clock();
cout<<"passed time: "<<double(end - begin)/CLOCKS_PER_SEC<<endl;

输出:passed time: 3.583
(每次运行时,这个数字会有一点变化,但它在道德上的大小相同。)

第二次实验:

平台:Win32
模式:调试

begin = clock();
_asm
{
    mov ecx, 07fffffffh
start:
    dec ecx
    jnz start
}
end = clock();
cout<<"passed time: "<<double(end - begin)/CLOCKS_PER_SEC<<endl;

输出:passed time: 0.903

第三和第四个实验:

只需将平台更改为x64即可。由于VC ++不支持64位内联汇编,我必须将循环放在另一个*.asm文件中。但最后结果是一样的。

从这一点开始,我开始使用我的大脑 - loopdec ecx, jnz start慢4倍,它们之间唯一的区别是AFAIK,dec ecx改变了标志{ {1}}并非如此。为了模仿这种标志,我做了

第五个实验:

平台:Win32(以下我总是假设平台对结果没有影响)
模式:调试

loop

输出:begin = clock(); _asm { mov ecx, 07fffffffh pushf start: popf ; do the loop here pushf dec ecx jnz start popf } end = clock(); cout<<"passed time: "<<double(end - begin)/CLOCKS_PER_SEC<<endl;

这是可以理解的,因为passed time: 22.134pushf必须使用内存。但是,让我们说,例如,寄存器popf不应保留在循环的末尾(这可以通过更好地安排寄存器来实现),并且标志{{1循环中不需要这个(这简化了事情,因为eax不在OF的低8位),那么我们可以使用OFflag来保持标志,所以我做了

第六个实验:

平台:Win32
模式:调试

lahf

输出:sahf

这比直接使用begin = clock(); _asm { mov ecx, 07fffffffh lahf start: sahf ; do the loop here lahf dec ecx jnz start sahf } end = clock(); cout<<"passed time: "<<double(end - begin)/CLOCKS_PER_SEC<<endl; 要好得多,对吗?

我做的最后一个实验是尝试保留passed time: 1.933标志。

第七次实验:

平台:Win32
模式:调试

loop

输出:OF

这个结果是最坏的情况,即每个循环都没有设置begin = clock(); _asm { mov ecx, 07fffffffh start: inc al sahf ; do the loop here lahf mov al, 0FFh jo dec_ecx mov al, 0 dec_ecx: dec ecx jnz start } end = clock(); cout<<"passed time: "<<double(end - begin)/CLOCKS_PER_SEC<<endl; 。它与直接使用passed time: 3.612几乎相同......

所以我的问题是:

  1. 我是对的,使用循环的唯一好处是它会处理标志(实际上只有OF对其中的5个有效)?

  2. 是否有loopdec的更长形式也会移动lahf,以便我们可以完全摆脱sahf

1 个答案:

答案 0 :(得分:6)

从历史上看,在8088和8086处理器上,LOOP是一个优化,因为它只比条件分支花了一个周期,而在分支之前放置一个DEC CX需要花费三到四个周期(取决于预取队列的状态。)

然而,今天的处理器与8086的工作方式截然不同。对于几代处理器,即使制造商已经制造出能够正确处理8088/8086或其后代所拥有的所有文档指令的机器,他们将精力集中在仅增强最有用指令的性能上。由于各种原因,英特尔或AMD必须添加到现代CPU以使LOOP指令与DEC CX/JNZ一样有效工作的电路数量可能会超过整个 8086中的电路总量,可能是一个巨大的差距。制造商不是增加高性能CPU的复杂性,而是包括一个更简单但更慢的处理单元,它可以处理“模糊”的指令。虽然高性能CPU需要大量电路来允许执行多个指令重叠,除非后面的指令需要早期计算的结果(并且必须等到它们可用),“模糊指令处理单元”可以避免只需一次执行一条指令就需要这种电路。