我已经读过,例如,如果我有一个for
在矩阵索引上运行的双循环,那么将列运行索引放在外部循环中会更有效。例如:
a=zeros(1000);
for j=1:1000
for i=1:1000
a(i,j)=1;
end
end
如果我有三个或更多for
循环,最有效的编码方式是什么?
例如:
a=zeros(100,100,100);
for j=1:100
for i=1:100
for k=1:100
a(i,j,k)=1;
end
end
end
我已经读过,例如,如果我有一个for
在矩阵索引上运行的双循环,那么将列运行索引放在外部循环中会更有效。例如:
a=zeros(1000);
for j=1:1000
for i=1:1000
a(i,j)=1;
end
end
如果我有三个或更多for
循环,最有效的编码方式是什么?
例如:
a=zeros(100,100,100);
for j=1:100
for i=1:100
for k=1:100
a(i,j,k)=1;
end
end
end
Answers:
简短的答案是,您想在最里面的循环上找到最左边的索引。在您的示例中,循环索引将为k,j,i,数组索引将为i,j,k。这与MATLAB如何在内存中存储不同维度有关。有关更多信息,请参阅此reddit帖子的#13 。
更长的答案可以解释为什么最左边的索引变化最快的效率更高。您需要了解两个关键事项。
首先,MATLAB(以及Fortran,但不是C和大多数其他编程语言)将数组以“列主要顺序”存储在内存中。例如,如果A是2 x 3 x 10矩阵,则条目将按以下顺序存储在内存中
A(1,1,1)
A(2,1,1)
A(1,2,1)
A(2,2,1)
A(1,3,1)
A(2,3,1)
A(1,1,2)
A(2,1,2)
...
A(2,3,10)
列主要顺序的这种选择是任意的-我们可以轻松地采用“行主要顺序”的约定,实际上,这就是用C和其他一些编程语言完成的工作。
您需要了解的第二件事是,现代处理器不会一次访问一个位置的内存,而是加载和存储64甚至128个连续字节(8或16个双精度浮点数)的“缓存行”一次从内存中取出。这些数据块被临时存储在快速内存缓存中,并根据需要写回。(实际上,高速缓存体系结构现在已经非常复杂,最多有3或4个级别的高速缓存,但是基本思想可以用我较年轻时使用的那种单级高速缓存来解释。)
如果循环是嵌套的,以便最内层的循环更新行下标,则将按A(1,1),A(2,1),A(3,1),...的顺序访问数组项。在访问第一个条目A(1,1)时,系统会将包含A(1,1),A(2,1),...,A(8,1)的高速缓存行从主内存中带入高速缓存。最内层循环的接下来的8次迭代将对此数据进行处理,而无需进行任何其他主存储器传输。
如果在另一种情况下,我们对循环进行结构化,以使列索引在最内部的循环中变化,那么将按以下顺序访问A的条目:A(1,1),A(1,2),A(1,3) ),...在这种情况下,第一次访问会将A(1,1),A(2,1),...,A(8,1)从主内存中移入高速缓存,但是这些条目将不会被使用。然后,在第二次迭代中访问A(1,2)将会从主存储器中再引入8个条目,依此类推。等到代码开始在矩阵的第2行上工作时,A(2,1)条目很可能会从缓存中清除出来,以便为其他所需数据腾出空间。结果,该代码产生的流量是所需数量的8倍。
一些优化的编译器能够自动重构循环,以避免出现此问题。
可以对许多用于矩阵乘法和分解的数值线性代数算法进行优化,以根据编程语言有效地使用行优先或列优先排序方案。错误地执行此操作可能会对性能产生重大负面影响。
For
在MATLAB中循环很慢。您应尽可能避免在MATLAB中进行显式循环。相反,通常可以用矩阵/矢量运算来表达问题。那就是MATLAB的方式。还有很多内置的函数可以初始化矩阵,等等。例如,有一个函数ones(),它将把矩阵的所有元素设置为1(扩展名,乘以任意值(标量)乘以全一矩阵))。它也适用于3-D数组(我认为这里涵盖了示例)。