下面是两个几乎完全相同的程序,只是我把I和j变量调换了一下。它们运行的时间都不一样。有人能解释一下为什么会这样吗?
版本1
#include <stdio.h>
#include <stdlib.h>
main () {
int i,j;
static int x[4000][4000];
for (i = 0; i < 4000; i++) {
for (j = 0; j < 4000; j++) {
x[j][i] = i + j; }
}
}
版本2
#include <stdio.h>
#include <stdlib.h>
main () {
int i,j;
static int x[4000][4000];
for (j = 0; j < 4000; j++) {
for (i = 0; i < 4000; i++) {
x[j][i] = i + j; }
}
}
除了缓存命中的其他优秀答案之外,还有一个可能的优化差异。你的第二个循环很可能被编译器优化成类似于:
for (j=0; j<4000; j++) {
int *p = x[j];
for (i=0; i<4000; i++) {
*p++ = i+j;
}
}
对于第一个循环,这种情况不太可能发生,因为它每次都需要将指针“p”增加4000。
编辑:p++和*p++ = ..可以在大多数CPU中编译为单个CPU指令。*p = .;P += 4000不能,所以优化它的好处较少。这也比较困难,因为编译器需要知道和使用内部数组的大小。在普通代码的内部循环中并不经常出现这种情况(它只发生在多维数组中,其中最后一个索引在循环中保持不变,倒数第二个索引是阶梯式的),因此优化的优先级不那么高。
我试着给出一个一般的答案。
因为i[y][x]是C语言中*(i + y*array_width + x)的简写(试试经典的int P[3];0[P] = 0xBEEF;)
当你遍历y时,你遍历大小为array_width * sizeof(array_element)的块。如果你在你的内循环中有这个,那么你将在这些块上进行array_width * array_height迭代。
通过翻转顺序,您将只有array_height块迭代,并且在任何块迭代之间,您将只有sizeof(array_element)的array_width迭代。
而在真正老的x86- cpu上,这并不重要,现在的x86做了大量的预取和缓存数据。在较慢的迭代顺序中,可能会产生许多缓存丢失。