正如其他人所说,问题是存储到数组中的内存位置:x[i][j]
。以下是一些见解的原因:
您有一个二维数组,但是计算机中的内存本质上是一维的。因此,当您想象这样的数组时:
0,0 | 0,1 | 0,2 | 0,3
----+-----+-----+----
1,0 | 1,1 | 1,2 | 1,3
----+-----+-----+----
2,0 | 2,1 | 2,2 | 2,3
您的计算机将其作为一行存储在内存中:
0,0 | 0,1 | 0,2 | 0,3 | 1,0 | 1,1 | 1,2 | 1,3 | 2,0 | 2,1 | 2,2 | 2,3
在第二个示例中,您将通过首先循环第二个数字来访问数组,即:
x[0][0]
x[0][1]
x[0][2]
x[0][3]
x[1][0] etc...
这意味着您正在按顺序击打它们。现在来看第一个版本。你在做:
x[0][0]
x[1][0]
x[2][0]
x[0][1]
x[1][1] etc...
由于C在内存中布置2-d数组的方式,您要求它在整个位置跳转。但是,现在开始讨论:为什么这很重要?所有内存访问都一样,对吗?
否:由于缓存。来自内存的数据会以小块(称为“缓存行”)的形式带入CPU,通常为64个字节。如果您有4字节的整数,这意味着您将在一个整齐的小束中获得16个连续的整数。获取这些大块内存实际上相当慢。您的CPU可以在加载单个缓存行时花费大量时间。
现在回头看一下访问顺序:第二个示例是(1)抓取16个int块,(2)修改所有int,(3)重复4000 * 4000/16次。这样既好又快速,并且CPU总是需要处理一些事情。
第一个示例是(1)抓取16个int的块,(2)仅修改其中一个,(3)重复4000 * 4000次。这将需要从内存中“获取”次数的16倍。实际上,您的CPU必须花时间坐在那里等待内存显示,而当它坐在周围时,您正在浪费宝贵的时间。
重要的提示:
现在您有了答案,这里有一个有趣的注释:没有第二个例子必须是快速的例子的内在原因。例如,在Fortran中,第一个示例将很快,而第二个示例将很慢。这是因为Fortran并未像C那样将其扩展为概念性的“行”,而是将其扩展为“列”,即:
0,0 | 1,0 | 2,0 | 0,1 | 1,1 | 2,1 | 0,2 | 1,2 | 2,2 | 0,3 | 1,3 | 2,3
C的布局称为“行优先”,而Fortran的布局称为“列主”。如您所见,了解您的编程语言是行优先还是列优先非常重要!这是更多信息的链接:http : //en.wikipedia.org/wiki/Row-major_order