【问题标题】:Matlab repeated matrix multiplication - loop vs built-in performancesMatlab重复矩阵乘法 - 循环与内置性能
【发布时间】:2016-04-13 01:35:45
【问题描述】:

给定一个矩阵A,我需要与其他n 向量Bi(即i=1...n)相乘。 A 的大小可以类似于5000x5000,因此Bi 可以类似于5000x1

如果我按以下方式评估产品:

for i=1:n
    product=A*Bi;
    % do something with product
end

结果是比计算产品慢的方式(数量级),例如:

%assume that S is a matrix that contains the vectors Bi as columns, i.e. S(:,i)=Bi, then:
results=A*S;   %stores all the products in matrix form
% do something with results

问题是向量Bi 的数量n 可能太大而无法存储在内存中,例如n=300000,所以我需要使用循环方法,每次评估产品时,使用然后丢弃向量Bi

为什么这种方法比直接乘法慢,有没有办法克服这个问题?

【问题讨论】:

  • 关于这个主题的好读物是Why is MATLAB so fast in matrix multiplication?
  • 说真的,mathworks 应该对此做一个适当的基准测试,并在某处用大的霓虹绿字母打印出来。这个问题已经被问过很多次了,而且还在被问到。显然,网络上的答案对某些人来说还不够好,那么为什么 mathworks(对源代码有更好的洞察力)不尝试这样做呢? @xarz 没有双关语问。如果网络上的答案不令人满意,那么这个问题显然没有足够好的答案。
  • @patrik 也许你是对的,但我查看了 stackoverflow 并没有找到处理这个确切问题的主题。顺便说一句,如果你可以在这里链接一些处理这个确切问题的参考资料,它们可能对未来的读者有用。谢谢。

标签: performance matlab matrix matrix-multiplication memory-efficient


【解决方案1】:

为了将每个数组与矩阵相乘,只需将矩阵乘以一个矩阵,该矩阵将以您想要的数组作为列。

所以如果你想检查这个

如果

size(a)=3,3

然后

 a*b==horzcat(a*b(:,1),a*b(:,2),a*b(:,3)) 

是真的

这样可以节省大量的循环时间

【讨论】:

    【解决方案2】:

    除了@Dan's answer,您还可以尝试并行化,前提是您有足够的内核和足够大的操作以使其盈利(有关parfor 内存消耗的更多详细信息,请参阅this answer):

    parfor ii = 0:(n/k)-1
        product = A*S(:,(ii*k+1):(ii+1)*k)
    end
    

    我在docs on mtimes* 运算符)中看不到它是否是隐式多线程的,但我想值得一试。

    【讨论】:

    • 我建议不要使用parfor,而是使用更大的批处理大小,直到达到内存限制。这样会更快。
    • @xarz 那么丹尼尔所说的确实如此;矩阵乘法速度非常快,因此最好尽可能大批量处理而不是并行处理。
    【解决方案3】:

    例如,您可以尝试对批次进行循环

    for i = 0:(n/k)-1
        product = A*S(:,(i*k+1):(i+1)*k)
    end
    

    并调整 k 以找到适合您的速度和内存的最佳平衡点。

    MATLAB 的循环很慢,因为它是一种解释型语言。所以它必须在飞行中解决很多事情。这些天来,由于 JIT 编译器,循环得到了极大的改进,但与用 C 编写和编译的内置函数相比,它们仍然很慢。此外,它们使用了非常先进的超快速矩阵乘法算法,相比之下使用您通过循环实现的相当幼稚的算法,这也有助于您体验到的加速。

    【讨论】:

    • 在这种情况下,您也可以并行处理,只需使用parfor。考虑到足够的核心和计算的复杂性(或大小),可能会显着提高速度。
    • @Adriaan 可能值得将其添加为另一个答案。虽然如果我没记错的话,* 运算符已经被并行化了,所以很难预测预期会有什么样的加速,而且它也无助于内存限制。
    • 切片有个bug,它以k开头,每k个元素有两次。
    • @Daniel 是的,我实际上并没有解决它,它更像是一个插图。不过我会尝试纠正它
    • 我使用了这样的方法,我认为它是最好的。我写了一个方法,将一批 50000 个向量 Bi 相乘,然后存储结果以允许外部函数检索每个产品。当缓冲区为空时,它会再次填充它。基本上就是你建议的!
    【解决方案4】:

    为简单起见,我的答案将假设一个 n×n 方阵 A,但对于非方阵也是如此。

    您的循环方法使用矩阵向量乘法。朴素的解决方案也是最著名的,导致运行时间为 O(n^2),重复 n 次。您最终的总运行时间为 O(n^3)。

    对于矩阵乘法,有更好的方法。最有名的算法只需要很少少于 O(n^2.4) 的运行时间,这使得它在处理大量数据时要快得多。

    使用矩阵乘法一次将多个向量 Bi 相乘时,您将获得更好的运行时间。这不会达到纯矩阵乘法的性能,但使用更大的 b 切片可能是最快的内存效率解决方案。

    不同讨论方法的一些代码:

    n=5000;
    k=100;
    A=rand(n,n);
    S=rand(n,n);
    workers=matlabpool('size');
    %for a parfor solution, the batch size must be smaller because multiple batches are stred in memory at once
    kparallel=k/workers;
    disp('simple loop:');
    tic;
    for i = 1:n
        product = A*S(:,n);
    end
    toc
    disp('batched loop:');
    tic;
    for i = 1:(n/k)
        product = A*S(:,(i-1)*k+1:(i)*k);
    end
    toc
    disp('batched parfor loop:');
    tic;
    parfor i = 1:(n/kparallel)
        product = A*S(:,(i-1)*kparallel+1:(i)*kparallel);
    end
    toc
    disp('matrix multiplication:');
    tic;
    A*S;
    toc
    

    【讨论】:

    • 谢谢,对这些算法的运行时间非常有趣的评论,这解释了问题
    • matlabpool('size') 在 R2015a 中不再工作,您必须使用 parpool
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-24
    • 2019-11-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多