Boojum 是正确的 - IF 你的编译器有一个很好的优化器并且你已经启用了它。如果不是这种情况,或者您对数组的使用不是顺序的并且易于优化,那么使用数组偏移量可能会慢得多。
这是一个例子。回到大约 1988 年,我们在 Mac II 上实现了一个带有简单电传打字界面的窗口。这包括 24 行 80 个字符。当您从代码中获得新行时,您向上滚动前 23 行并在底部显示新行。当电传打字机上有些东西时,它不是一直都是,它以 300 波特的速度输入,串行协议开销约为每秒 30 个字符。所以我们说的根本不是应该对 16 MHz 68020 征税的东西!
但是写这篇文章的人是这样做的:
char screen[24][80];
并使用二维数组偏移量来滚动字符,如下所示:
int i, j;
for (i = 0; i < 23; i++)
for (j = 0; j < 80; j++)
screen[i][j] = screen[i+1][j];
这样的六个窗口让机器瘫痪了!
为什么?因为当时的编译器很笨,所以在机器语言中,内循环赋值的每个实例screen[i][j] = screen[i+1][j] 看起来有点像这样(Ax 和 Dx 是 CPU 寄存器);
Fetch the base address of screen from memory into the A1 register
Fetch i from stack memory into the D1 register
Multiply D1 by a constant 80
Fetch j from stack memory and add it to D1
Add D1 to A1
Fetch the base address of screen from memory into the A2 register
Fetch i from stack memory into the D1 register
Add 1 to D1
Multiply D1 by a constant 80
Fetch j from stack memory and add it to D1
Add D1 to A2
Fetch the value from the memory address pointed to by A2 into D1
Store the value in D1 into the memory address pointed to by A1
因此,我们为 23x80=1840 次内部循环迭代中的每一个讨论 13 条机器语言指令,总共 23920 条指令,包括 3680 条 CPU 密集型整数乘法。
我们对 C 源代码进行了一些更改,所以它看起来像这样:
int i, j;
register char *a, *b;
for (i = 0; i < 22; i++)
{
a = screen[i];
b = screen[i+1];
for (j = 0; j < 80; j++)
*a++ = *b++;
}
还有两个机器语言乘法,但是它们在外循环中,所以只有 46 个整数乘法而不是 3680。而内循环 *a++ = *b++ 语句只包含两个机器语言操作。
Fetch the value from the memory address pointed to by A2 into D1, and post-increment A2
Store the value in D1 into the memory address pointed to by A1, and post-increment A1.
鉴于有 1840 次内部循环迭代,总共有 3680 条 CPU 廉价指令 - 减少了 6.5 倍 - 并且没有整数乘法。在此之后,我们没有在六个电传打字机窗口中死去,而是无法拉起足够的速度来使机器陷入困境——我们首先用完了电传打字机数据源。还有一些方法可以进一步优化这一点。
现在,现代编译器将为您进行这种优化 - IF 您要求他们这样做,并且 IF 您的代码的结构允许它.
但仍然存在编译器无法为您执行此操作的情况 - 例如,如果您在数组中执行非顺序操作。
所以我发现尽可能使用指针而不是数组引用对我很有帮助。性能肯定不会更差,而且通常会好得多。