我有一个n乘n的矩阵A,一组n系数k(n乘1)和一个称为行(1乘n)的矩阵我的目标是从A的第I行减去第I个系数k的加权行。我有三个问题:为什么for循环的性能比内置矩阵乘法好,一般来说,是什么解释了每种方法相对于下一种方法的优越性,还有比我提出的三种方法更好的方法吗?

% Define row & coefficients used in each method
k = (1:1000).';
row = 1:1000;

% Method 1 (matrix multiply) ~15 seconds
A = magic(1e3);
tic;
for z = 1:1e3
    A = A - k*row;
end
toc;
% Method 2 (for loop) ~11 seconds
B = magic(1e3);
tic;
for z = 1:1e3
    for cr = 1:1000
        B(cr,:) = B(cr,:) - k(cr)*row;
    end
end
toc;
% method 3 (bsxfun) ~ 4 seconds
C = magic(1e3);
tic;
for z = 1:1e3
    C = C - bsxfun(@times, k, row);
end
toc

isequal(A,B)
isequal(A,C)

注意,我是在一个算法中做这些行减法我简化了代码,创建了这个玩具测试用例,但计算的关键仍然存在此外,为了避免混淆,还使用了带z的for循环来增大时间。

最佳答案

简而言之:代码的更快版本是:

tic;
for z = 1:1e3
    for cr = 1:1000
        B(:,cr) = B(:,cr) - k*row(cr);
    end
end
toc;

你可能想看看我之前对this question的回答简言之,您的循环操作行,而MATLAB是基于列的这意味着列在内存中是连续的原来的循环在行上迭代,效率很低。
我的计算机上的执行时间:
% A - k*row
Elapsed time is 4.370238 seconds.
% B(cr,:) = B(cr,:) - k(cr)*row;
Elapsed time is 9.537338 seconds.
% C = C - bsxfun(@times, k, row);
Elapsed time is 3.039836 seconds.
B(:,cr) = B(:,cr) - k*row(cr);
Elapsed time is 2.028186 seconds.

解释您的第一个版本不是矩阵乘法,而是两个向量的外积,其结果是1000 x 1000的矩阵空穴计算是BLAS2秩1更新(a=alphaxy'+a是GER函数)最有可能的问题是,MATLAB不能识别它,而是按照它理解的方式运行代码,即显式地执行所有操作,包括k*行这正是这个解决方案的问题所在外部产品必须分配与矩阵大小相等的额外内存,这本身需要时间考虑一下:
内存分配-由于我们不知道MATLAB如何管理内存分配,这可能意味着a lot of overhead
读取向量k,行
结果矩阵的书写(KR)
读取KR从
阅读和写作
两个1000*1000矩阵是16MB—我怀疑您有这么多缓存这就是为什么这个版本不是最好的,实际上可能比“内存效率低下”循环慢,这取决于可用的内存带宽和CPU缓存大小。
不需要分配KR矩阵并将值显式存储在内存中—您可以在循环中计算所需的产品因此,您可以有效地将内存带宽需求减半,并消除内存分配开销!
读取向量k,行
读写
假设一个向量适合于缓存(它可以-1000*8字节不多),那么只需从内存中读取一次k和row现在,对列进行循环的算法非常有意义(这可能就是BLAS实现此计算的方式)
读取k和row并将它们保存在缓存中
将具有全内存带宽的A流传送到CPU,减去k*行乘积,再传送回内存
现在是最后的效率考虑以我的循环结构为例在每次迭代中,我们读写A,然后读取向量即每次迭代移动16MB的数据1000次迭代总共可以移动16GB的数据计算结果所需的两秒钟可提供8GB/s的有效内存带宽我的系统在使用2个CPU核时有16GB/s的流带宽,在使用1个CPU核时大约有11-12gb/s因此,这个顺序循环在一个核心上以60-70%的效率运行不错,考虑到这是一个MATLAB循环:)
还要注意的是,至少在我的计算机上,基于列的循环版本比a-krow实现(2s对4.37s)快两倍(多一点)这有力地说明krow确实是由MATLAB显式执行和构造的,并且总的内存流量是循环版本的两倍因此性能下降了两倍。
通过直接调用相应的BLAS函数,您仍然可以像在第一个算法中那样进行编辑看看this FEX contribution它允许您直接从MATLAB调用BLAS和LAPACK函数可能有用。。

09-30 15:33
查看更多