我们的计算机科学老师曾经说过,出于某种原因,倒计时比倒计时更有效。例如,如果您需要使用For循环,而循环索引并未在某些地方使用(例如在屏幕上打印一行N*),我的意思是代码如下:
for (i = N; i >= 0; i--)
putchar('*');
比:
for (i = 0; i < N; i++)
putchar('*');
这是真的吗?如果是这样的话,有人知道为什么吗?
发布于 2010-05-13 06:01:23
以下是一些硬件上可能发生的情况,这取决于编译器可以推断出您正在使用的数字的范围:对于递增循环,您必须在每次循环中测试i<N
。对于递减版本,进位标志(设置为减法的副作用)可能会自动告诉您是否为i>=0
。这样循环一次就可以节省一次测试。
实际上,在现代流水线处理器硬件上,这种东西几乎肯定是无关紧要的,因为从指令到时钟周期不存在简单的1-1映射。(虽然我可以想象,如果你正在做一些事情,比如从微控制器生成精确定时的视频信号,它就会出现。但是,无论如何你都要用汇编语言来写。)
发布于 2010-05-13 06:18:56
在英特尔x86指令集中,构建向下计数到零的循环通常比计数到非零退出条件的循环需要更少的指令就可以完成。具体而言,ECX寄存器传统上用作x86 asm中的循环计数器,并且英特尔指令集有一个特殊的jcxz跳转指令,用于测试ECX寄存器是否为零,并根据测试结果进行跳转。
但是,除非您的循环对时钟周期计数非常敏感,否则性能差异将可以忽略不计。与向上计数相比,倒数到零可能会将循环的每次迭代减少4到5个时钟周期,因此它实际上更像是一种新奇的东西,而不是一种有用的技术。
此外,如今一个优秀的优化编译器应该能够将count up循环源代码转换为count down to 0机器码(取决于您如何使用循环索引变量),因此没有任何理由以奇怪的方式编写循环,只是为了在这里或那里压缩一两个循环。
发布于 2010-05-13 07:12:09
在C中转换为psudo-assembly:
for (i = 0; i < 10; i++) {
foo(i);
}
变成了
clear i
top_of_loop:
call foo
increment i
compare 10, i
jump_less top_of_loop
而:
for (i = 10; i >= 0; i--) {
foo(i);
}
变成了
load i, 10
top_of_loop:
call foo
decrement i
jump_not_neg top_of_loop
请注意,在第二个psudo-assembly中缺少比较。在许多架构上,有通过算术运算(加、减、乘、除、增、减)设置的标志,您可以使用这些标志进行跳转。这些函数通常给出的结果实质上是与0的比较。事实上,在许多架构上
x = x - 0
在语义上与
compare x, 0
此外,与我的示例中的10进行比较可能会导致更糟糕的代码。10可能必须住在寄存器中,所以如果它们供不应求,那么就会产生额外的代码来移动东西,或者每次通过循环重新加载10。
编译器有时可以重新排列代码以利用这一点,但这通常很困难,因为它们通常无法确保通过循环反转方向在语义上是等价的。
https://stackoverflow.com/questions/2823043
复制相似问题