参考例子:按行遍历和按列遍历哪一个更快一些?_Master.Jiang的博客-CSDN博客_行遍历和列遍历
行优先与列优先遍历opencv中Mat数据类型的效率差异_liu_jie_bin的博客-CSDN博客_行优先遍历与列优先遍历命中率
对于C来说,访问二维数组的顺序不同,时间消耗也是不同的。行优先遍历和列优先遍历进行对比,行优先更佳。
编写一个简单程序进行测试:
#include
#include
int a[20005][20005]={0};
int main()
{
double s1,s2;
int i,j;
s1=clock();
for(i=1;i<=10;i++){
for(j=1;j<=10;j++){
a[i][j]=1;
}
}
s2=clock();
printf("10*10数组按行遍历运行时间:%.12f s\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=10;i++){
for(j=1;j<=10;j++){
a[j][i]=1;
}
}
s2=clock();
printf("10*10数组按列遍历运行时间:%.12f s\n\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=100;i++){
for(j=1;j<=100;j++){
a[i][j]=1;
}
}
s2=clock();
printf("100*100数组按行遍历运行时间:%.12f s\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=100;i++){
for(j=1;j<=100;j++){
a[j][i]=1;
}
}
s2=clock();
printf("100*100数组按列遍历运行时间:%.12f s\n\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=500;i++){
for(j=1;j<=500;j++){
a[i][j]=1;
}
}
s2=clock();
printf("500*500数组按行遍历运行时间:%.12f s\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=500;i++){
for(j=1;j<=500;j++){
a[j][i]=1;
}
}
s2=clock();
printf("500*500数组按列遍历运行时间:%.12f s\n\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=1000;i++){
for(j=1;j<=1000;j++){
a[i][j]=1;
}
}
s2=clock();
printf("1000*1000数组按行遍历运行时间:%.12f s\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=1000;i++){
for(j=1;j<=1000;j++){
a[j][i]=1;
}
}
s2=clock();
printf("1000*1000数组按列遍历运行时间:%.12f s\n\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=2000;i++){
for(j=1;j<=2000;j++){
a[i][j]=1;
}
}
s2=clock();
printf("2000*2000数组按行遍历运行时间:%.12f s\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=2000;i++){
for(j=1;j<=2000;j++){
a[j][i]=1;
}
}
s2=clock();
printf("2000*2000数组按列遍历运行时间:%.12f s\n\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=3000;i++){
for(j=1;j<=3000;j++){
a[i][j]=1;
}
}
s2=clock();
printf("3000*3000数组按行遍历运行时间:%.12f s\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=3000;i++){
for(j=1;j<=3000;j++){
a[j][i]=1;
}
}
s2=clock();
printf("3000*3000数组按列遍历运行时间:%.12f s\n\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=4000;i++){
for(j=1;j<=4000;j++){
a[i][j]=1;
}
}
s2=clock();
printf("4000*4000数组按行遍历运行时间:%.12f s\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=4000;i++){
for(j=1;j<=4000;j++){
a[j][i]=1;
}
}
s2=clock();
printf("4000*4000数组按列遍历运行时间:%.12f s\n\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=5000;i++){
for(j=1;j<=5000;j++){
a[i][j]=1;
}
}
s2=clock();
printf("5000*5000数组按行遍历运行时间:%.12f s\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=5000;i++){
for(j=1;j<=5000;j++){
a[j][i]=1;
}
}
s2=clock();
printf("5000*5000数组按列遍历运行时间:%.12f s\n\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=6000;i++){
for(j=1;j<=6000;j++){
a[i][j]=1;
}
}
s2=clock();
printf("6000*6000数组按行遍历运行时间:%.12f s\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=6000;i++){
for(j=1;j<=6000;j++){
a[j][i]=1;
}
}
s2=clock();
printf("6000*6000数组按列遍历运行时间:%.12f s\n\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=7000;i++){
for(j=1;j<=7000;j++){
a[i][j]=1;
}
}
s2=clock();
printf("7000*7000数组按行遍历运行时间:%.12f s\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=7000;i++){
for(j=1;j<=7000;j++){
a[j][i]=1;
}
}
s2=clock();
printf("7000*7000数组按列遍历运行时间:%.12f s\n\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=8000;i++){
for(j=1;j<=8000;j++){
a[i][j]=1;
}
}
s2=clock();
printf("8000*8000数组按行遍历运行时间:%.12f s\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=8000;i++){
for(j=1;j<=8000;j++){
a[j][i]=1;
}
}
s2=clock();
printf("8000*8000数组按列遍历运行时间:%.12f s\n\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=9000;i++){
for(j=1;j<=9000;j++){
a[i][j]=1;
}
}
s2=clock();
printf("9000*9000数组按行遍历运行时间:%.12f s\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=9000;i++){
for(j=1;j<=9000;j++){
a[j][i]=1;
}
}
s2=clock();
printf("9000*9000数组按列遍历运行时间:%.12f s\n\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=10000;i++){
for(j=1;j<=10000;j++){
a[i][j]=1;
}
}
s2=clock();
printf("10000*10000数组按行遍历运行时间:%.12f s\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=10000;i++){
for(j=1;j<=10000;j++){
a[j][i]=1;
}
}
s2=clock();
printf("10000*10000数组按列遍历运行时间:%.12f s\n\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=12000;i++){
for(j=1;j<=12000;j++){
a[i][j]=1;
}
}
s2=clock();
printf("12000*12000数组按行遍历运行时间:%.12f s\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=12000;i++){
for(j=1;j<=12000;j++){
a[j][i]=1;
}
}
s2=clock();
printf("12000*12000数组按列遍历运行时间:%.12f s\n\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=14000;i++){
for(j=1;j<=14000;j++){
a[i][j]=1;
}
}
s2=clock();
printf("14000*14000数组按行遍历运行时间:%.12f s\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=14000;i++){
for(j=1;j<=14000;j++){
a[j][i]=1;
}
}
s2=clock();
printf("14000*14000数组按列遍历运行时间:%.12f s\n\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=16000;i++){
for(j=1;j<=16000;j++){
a[i][j]=1;
}
}
s2=clock();
printf("16000*16000数组按行遍历运行时间:%.12f s\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=16000;i++){
for(j=1;j<=16000;j++){
a[j][i]=1;
}
}
s2=clock();
printf("16000*16000数组按列遍历运行时间:%.12f s\n\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=18000;i++){
for(j=1;j<=18000;j++){
a[i][j]=1;
}
}
s2=clock();
printf("18000*18000数组按行遍历运行时间:%.12f s\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=18000;i++){
for(j=1;j<=18000;j++){
a[j][i]=1;
}
}
s2=clock();
printf("18000*18000数组按列遍历运行时间:%.12f s\n\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=20000;i++){
for(j=1;j<=20000;j++){
a[i][j]=1;
}
}
s2=clock();
printf("20000*20000数组按行遍历运行时间:%.12f s\n",(s2-s1)/CLOCKS_PER_SEC);
s1=clock();
for(i=1;i<=20000;i++){
for(j=1;j<=20000;j++){
a[j][i]=1;
}
}
s2=clock();
printf("20000*20000数组按列遍历运行时间:%.12f s\n\n",(s2-s1)/CLOCKS_PER_SEC);
return 0;
}
其运行结果ubuntu16虚拟机:
u16@ubuntu:~/test_cpp$ ./build.sh
-- Host Architecture = x86_64
10*10数组按行遍历运行时间:0.000011000000 s
10*10数组按列遍历运行时间:0.000000000000 s100*100数组按行遍历运行时间:0.000113000000 s
100*100数组按列遍历运行时间:0.000005000000 s500*500数组按行遍历运行时间:0.000994000000 s
500*500数组按列遍历运行时间:0.000163000000 s1000*1000数组按行遍历运行时间:0.001463000000 s
1000*1000数组按列遍历运行时间:0.001618000000 s2000*2000数组按行遍历运行时间:0.005460000000 s
2000*2000数组按列遍历运行时间:0.020687000000 s3000*3000数组按行遍历运行时间:0.010454000000 s
3000*3000数组按列遍历运行时间:0.050492000000 s4000*4000数组按行遍历运行时间:0.015124000000 s
4000*4000数组按列遍历运行时间:0.093347000000 s5000*5000数组按行遍历运行时间:0.019124000000 s
5000*5000数组按列遍历运行时间:0.139494000000 s6000*6000数组按行遍历运行时间:0.028529000000 s
6000*6000数组按列遍历运行时间:0.194977000000 s7000*7000数组按行遍历运行时间:0.035542000000 s
7000*7000数组按列遍历运行时间:0.251729000000 s8000*8000数组按行遍历运行时间:0.053196000000 s
8000*8000数组按列遍历运行时间:0.310974000000 s9000*9000数组按行遍历运行时间:0.064045000000 s
9000*9000数组按列遍历运行时间:0.381296000000 s10000*10000数组按行遍历运行时间:0.076950000000 s
10000*10000数组按列遍历运行时间:0.439071000000 s12000*12000数组按行遍历运行时间:0.104241000000 s
12000*12000数组按列遍历运行时间:0.591645000000 s14000*14000数组按行遍历运行时间:0.135862000000 s
14000*14000数组按列遍历运行时间:0.767986000000 s16000*16000数组按行遍历运行时间:0.175874000000 s
16000*16000数组按列遍历运行时间:1.055006000000 s18000*18000数组按行遍历运行时间:0.207204000000 s
18000*18000数组按列遍历运行时间:1.295405000000 s20000*20000数组按行遍历运行时间:0.228332000000 s
20000*20000数组按列遍历运行时间:1.554783000000 s
当数组大小较小时,按行遍历和按列遍历的时间差不多;随着数组大小的增大,二者的运行时间均有增长,但是增长幅度不同。显然按行遍历的增长幅度要小于按列遍历。 PS,原文也对比了matlab,可以看到对于计算来说,C++的速度明显快于Matlab。
因为在C++中,存储的二维数组是以行优先的,我们正常使用的逻辑也是如此。
是因为cpu在访问内存地址的时候,首先访问的是虚拟内存,检查TLB,映射成对应的物理地址进行数据访问。如果命中,会得到其物理地址,之后会访问cache缓存,如果cache中有要访问的数据,那么本次访问就结束,如果没有,就到内存中寻找,并更新cache缓存;如果TLB不命中,那么那么系统内核会调用缺页异常处理程序去处理,这个过程中会进行页替换等操作,最终取得要访问的数据。而内存的物理地址中,二维数组是以行优先的顺序存储,测试图像数据大小为4096X1200字节,假设内存页为4096字节,那么按行优先遍历,遍历完一行则中断一次缺页,整个过程只需要中断1200次缺页异常,进行页替换。而按列优先遍历,每遍历一个元素便中断一次缺页异常。
opencv使用二维数组实现cv::Mat数据结构,通常一张图片为2000*2000大小,若以行遍历通常会比列遍历的快10ms级别。
在内存中,数组是连续分布的。数组在内存中是按行优先存储的,在虚存环境下,如果整个数组没有在内存中的话可以比列优先减少内存换进换出的次数。就算整个数组都在内存中,列优先访问a[i][j]还要计算一次乘法,行优先只需加一即可,换言之,乘法的“代价” 远大于加法。
opencv 的Mat类
原文链接:https://blog.csdn.net/u012058778/article/details/90764430
如果使用Mat类,我们得到的好处是:
cv::Mat A = cv::imread("erode.jpg");
cv::Mat B(A);
cv::Mat C = A;
printf("A.data = %p\nB.data = %p\nC.data = %p\n", A.data, B.data, C.data);
三个矩阵的数据是指向同一块矩阵数据。
遍历Mat的数据时,和上面描述的相同,按照行优先,通过指针的方式读取最快。
#include
int height = 5;
int width = 5;
cv::Mat image = cv::Mat::zeros(height, width, CV_16UC2);
cv::Mat image1 = cv::Mat::zeros(height, width, CV_16UC2);
cv::Mat image2 = cv::Mat::zeros(height, width, CV_16UC2);
chrono::steady_clock::time_point t1 = chrono::steady_clock::now();
for (int row = 0; row < height; row++)
{
//指向矩阵当前行的指针
Vec *data_Ptr = image.ptr> (row);
//可以将data_Ptr看出一个数组,data_Ptr[col]代表了第几个元素
//每个元素的数据类型是,包含了2个无符号整型的元素
for (int col = 0; col < width; col++)
{
data_Ptr[col][0] = 5;
data_Ptr[col][1] = 2;
}
}
chrono::steady_clock::time_point t2 = chrono::steady_clock::now();
chrono::duration time_used = chrono::duration_cast>(t2-t1);
cout << "The first method cost time is "<>(row,col)[0] = 5;
image1.at>(row,col)[1] = 2;
}
}
chrono::steady_clock::time_point t3 = chrono::steady_clock::now();
time_used = chrono::duration_cast>(t3-t2);
cout << "The second method cost time is "<