给出矩阵A[i][j]
。如果我们想要添加矩阵的元素,哪种方法更好,为什么?
从我的观点来看,行方式更好,因为数组表示元素存储在连续的内存位置,因此访问它们所花费的时间更少。但是因为在RAM中获取每个位置需要相同的时间,这是否重要?
答案 0 :(得分:32)
在C中,矩阵存储在r ow-major order中。因此,如果您访问元素a[i][j]
,则对元素a[i][j+1]
的访问可能会访问缓存。不会访问主内存。缓存比主内存快,因此访问模式很重要。
当然,必须考虑更多因素,例如写访问/读访问,写策略(直写,写回/写分配,无写分配),多级缓存等。但这似乎对这个问题有点过分了。
使用分析工具(例如cachegrind)获得一些乐趣,并亲自查看。
例如,考虑一个访问4MB矩阵的虚拟程序。查看每种访问模式的未命中率之间的差异。
列访问权限
$ cat col_major.c
#include <stdio.h>
int main(){
size_t i,j;
const size_t dim = 1024 ;
int matrix [dim][dim];
for (i=0;i< dim; i++){
for (j=0;j <dim;j++){
matrix[j][i]= i*j;
}
}
return 0;
}
$ valgrind --tool=cachegrind ./col_major
==3228== D refs: 10,548,665 (9,482,149 rd + 1,066,516 wr)
==3228== D1 misses: 1,049,704 ( 968 rd + 1,048,736 wr)
==3228== L2d misses: 1,049,623 ( 893 rd + 1,048,730 wr)
==3228== D1 miss rate: 9.9% ( 0.0% + 98.3% )
==3228== L2d miss rate: 9.9% ( 0.0% + 98.3% )
行访问
$ cat row_major.c
#include <stdio.h>
int main(){
size_t i,j;
const size_t dim = 1024 ;
int matrix [dim][dim];
for (i=0;i< dim; i++)
for (j=0;j <dim;j++)
matrix[i][j]= i*j;
return 0;
}
$ valgrind --tool=cachegrind ./row_major
==3524== D refs: 10,548,665 (9,482,149 rd + 1,066,516 wr)
==3524== D1 misses: 66,664 ( 968 rd + 65,696 wr)
==3524== L2d misses: 66,583 ( 893 rd + 65,690 wr)
==3524== D1 miss rate: 0.6% ( 0.0% + 6.1% )
==3524== L2d miss rate: 0.6% ( 0.0% + 6.1% )
答案 1 :(得分:2)
如果阵列很小,那就不重要了。如果它们很大,那么读取时间可能会受到影响。最大的问题是缓存。如果您不能指望将完整矩阵一次加载到缓存中,那么您希望最大限度地减少遇到的缓存未命中数,因为处理缓存未命中相对耗时。
如果数组真的很大,那么你可以通过引起更多的页面交换来获得更大的性能命中率。
答案 2 :(得分:0)
对于C,处理多维数组的最佳方法是:
int a[MAX_I][MAX_J];
for (i = 0; i < MAX_I; ++i) {
for (j = 0; j < MAX_J; ++j) {
/* process a[i][j] */
}
}
原因是C语言将数组作为具有偏移量的指针处理,请参阅:The C Programming Language。