Column- vs. Row-major Layout Array
前两天在逛知乎的时候,在一篇回答的下面看到了一个回答:“[在MATLAB中,] 先列后行比先行后列快很多”1 。也就是说,在不得以循环遍历矩阵中的每一个数组的元素时,“先遍历列元素,再遍历行元素”所花费的时间更短。于是,今天我就简单地测试了一下,采用的测试程序是“100次循环10e4
阶矩阵的元素自增1”:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
clc,clear,close all
%%First row, second column
tic
A = ones(10^4);
for t = 1:100
for columns = 1:width(A)
for rows = 1:height(A)
A(rows,columns) = A(rows,columns)+1;
end
end
end
toc
%% First column, second row
clear all
tic
B = ones(10^4);
for t = 1:100
for rows = 1:height(B)
for columns = 1:width(B)
B(rows,columns) = B(rows,columns)+1;
end
end
end
toc
1
2
Elapsed time is 24.729285 seconds.
Elapsed time is 263.073480 seconds.
可以看到,的确“先列后行”是更快的,并且速度差异是很明显的。
这种差异性是由MATLAB存储数组(Array)的方式导致的2。
根据数组的存储方式,数组的布局(Array layout,order,format或representation)分为column-major layout和row-major layout。对于column-major layout的数组,列中的元素在内存中是连续的(contiguous);对于row-major layout的数组,行中的元素在内存中是连续的。数组的布局方式对于代码的集成(integration),可用性(usability)和性能(performance)是非常重要的。某些算法对于特定的array layout执行得更好。
在MATLAB的编程语言和编程环境中,所有的数据都是默认采用单一的column-major layout。
常见的采用column-major layout(有时也被称为”Fortran” style ordering3,Fortran-style contiguous array4)的编程语言:MATLAB,Fortran(许多流体力学的仿真采用的是这种语言),R。常见的采用row-major layout(有时也被称为”C” style ordering,或者C-style contiguous array)的编程语言:C,C++,Python NumPy package45。
对于MATLAB采用column-major layout这一特性在下面的矩阵索引或运算的过程中可以得到一定的反映。
(1)矩阵索引
1
2
3
A = [1,2,3,4;...
5,6,7,8];
A(3)
1
2
ans =
2
(2)矩阵展开
1
2
3
A = [1,2,3,4;...
5,6,7,8];
A(:)
1
2
3
4
5
6
7
8
9
ans =
1
5
2
6
3
7
4
8
(3)素数判断
最近我在尝试计算某一个大数下素数的个数,其中涉及到使用MATLAB的isprime
函数来判断某个数是否是素数 6。isprime
函数首先会将输入展开成一个列向量:
为了测试使用行向量和列向量的区别,我将isprime
函数复制了一份,并将这一语句修改为X=X(:)'
,其余部分不变,定义为一个新的函数helperIsprime
:
注:这里的herlperIsprimes
函数并没有进行博客6 中提到的优化。
之后,测试了一下两个函数“重复20次判断1:2e7
内所有元素是否为素数”所花费的时间:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
clc,clear,close all
tic
for i = 1:20
result_builtin = isprime(1:2e7);
end
toc
clear all
tic
for i = 1:20
result_helper = helperIsprime(1:2e7);
end
toc
1
2
Elapsed time is 2931.111347 seconds.
Elapsed time is 3875.116026 seconds.
结果表明,将列向量更改为行向量后,的确降低了代码遍历元素的速度,也从侧面验证了MATLAB采用的是column-major layout数组。