实际上,这个例子是完全错误的。虽然等级 0 从 0..M-1 开始,但该循环正在迭代 0..N-1。除非M==N,否则您将阅读错误的元素。
目标是通过操作循环的顺序让您的循环迭代地访问内存中物理上相邻的位置。
每当您的程序读取一个值时,CPU 都会从缓存控制器请求它。如果它不在缓存中,则该值 - 及其附近的值 - 将从内存中检索并存储在缓存中。
如果您随后读取下一个元素,它应该(通常)已经在缓存中,因此到下一个缓存或主机 RAM 的往返不会很慢。
如果您的循环在整个地方走动而不是利用空间局部性,那么您将面临更多缓存未命中的风险,这会使事情变慢。
简而言之:从缓存中获取内容很快,从 RAM 中获取内容很慢,并且对循环进行排序以使其接触相邻位置有助于保持缓存的正常运行。
在图形中,我们通常这样做:
int a[M*N*N];
for(int offset=0; offset < M*N*N; ++offset)
{
//int y = offset / cols;
//int x = offset % rows;
sum += a[offset];
}
如果你需要一个元素,它是 X,Y,只是
offset = Y * cols + X;
int val = a[offset];
或用于 3D
offset = Z*N*N + Y*N + X
或
offset = Z * rows * cols + Y * cols + X;
... 并跳过所有的多维数组愚蠢。
就个人而言,我会这样做:
int *p = &a[0][0][0]; // could probably just do int* p=a, but for clarity...
//... array gets populated somehow
for(int i=0;i<M*N*N;++i)
{
sum += p[i];
}
... 但假设该数组是一个常规方形数组,而不是指针数组或指针数组的数组。