【问题标题】:Matlab efficiency: small for-loopMatlab效率:小for-loop
【发布时间】:2021-05-06 12:24:35
【问题描述】:

我编写了一些 Matlab 代码,目前可以使用,但是在运行大矩阵时非常慢。我将代码的瓶颈确定为 for 循环。

代码应该做的是查看 H 矩阵的一列 (ic),与至少有一个与 ic 相同的 1 条目的其他列进行比较,并将它们的列添加到 ic 向量中。这种情况一直持续到 ic 向量不再发生变化为止。

这是一个小例子:

H = ( 1 0 1 ; 1 1 0 ; 0 0 1 ) 和 ic = ( 1 1 0 )^T (H 的第一列)。然后代码以 ic 的第一个 1 为例,在其行中比较另一列是否也有 1 作为其第一个条目,找到第三列 ( 1 0 1 )^T 然后将所有这些添加到 ic 向量在找到的列中,在这种情况下是第三个条目,因此 ic = ( 1 1 1 )^T。

一开始我用了两个for循环:

H 是一个 (500 x 1000) 稀疏矩阵(但当代码运行得更快时应该是 (5000 x 10 000))。

ic 是一个 (500 x 1) 向量。

i 是代码开头的 ic 向量的索引。

代码1(旧版):

for c = find(ic).'
     for v = find(H(c,:))
          if v ~= i
              ic = ic | H(:,v);
          end
     end
end

然后我尝试改进我的代码并通过矢量化使其更快:

代码2(新版本):

for c = find(ic).'
     ic( sum(H(:,find(H(c,:))'),2) > 0 ) = 1;
end

第二行代码2被调用了1769302次,占用了97.4%的时间,也就是30.568s。它被调用了很多次,因为 ic 向量必须在构造 H 矩阵时确定,并且几乎在 H 矩阵中添加任何 1 之前被调用。 与代码 1 相比,时间已经有所改善,其中第二行耗时 20.209 秒,第四行耗时 31.950 秒。我的目标是使用 (5000 x 10 000) H 矩阵运行我的代码,并且不超过 3 分钟。

H 矩阵是在代码运行时构建的。在几乎每个添加到矩阵的 1 之前,都会调用其中包含上述代码的函数。然后 ic 向量在 while 循环中更新,直到它不再变化或 ic 向量中不再有零。 while 循环如下所示:

代码 3(上下文):

ic2 = ic;

while true

     for c = find(ic).'
          ic( sum(H(:,find(H(c,:))'),2) > 0 ) = 1;
     end
     
     if isequal(ic0,ic)
          return;
     end
     ic0 = ic;

     if sum(ic(:,1)) == size(H,1)
          return;
     else
          ic2 = ic;
     end

end

ic0 的唯一目的是在迭代之间比较 ic

ic2 保存 ic 在它只包含一个之前的状态。

我非常感谢任何可以帮助我进一步改进代码运行时的答案,对于在 Matlab 编程方面没有非常丰富的经验,我深表歉意。非常感谢您的任何回答!

【问题讨论】:

  • 如果H 非常大(并且是5,000x10,000),那么转置操作'(或.')将非常昂贵。在不知道更多细节的情况下,我无法提供具体细节。如果您有并行计算工具箱,还值得研究的是使用parfor 循环。有时可以通过使用逻辑索引来摆脱 find 命令 - 看看你的其余代码,看看你是否可以实现它。
  • if v ~= i 中的i 是什么?
  • 如果您能解释所需的输出是什么(很难从基于循环的代码中理解),这将有所帮助。也许举一个输入和期望输出的小例子
  • 第二行代码如何调用 1,769,302 次,当循环运行在 [500x1] 向量 ic 的非零元素上时? ic 中的非零元素肯定不能超过 500 个。
  • 非常感谢您的 cmets,我编辑了我的问题并希望澄清它。

标签: performance matlab for-loop vectorization


【解决方案1】:

我想这取决于您是否想在添加一个之后再次检查整个 ic 向量。因为如果您在向量中的某个点添加一个,您已经处理过,您可能会与其他列有一些新的连接。但是,如果您不想再次检查,在将一个添加到 ic 向量后,您可以使用以下内容:

H = [ 1 0 1 ; 
      1 1 0 ; 
      0 0 1 ];
  
ic = [1; 
      1; 
      0];

Htemp = H+ic;

ic = any(H(:, any(Htemp>1)),2);
  

要检查直到 ic 不再变化,您可以尝试以下操作:

ic = H(:,1);
a = true;
s = 0;
ic2 = ic;
while a
    Htemp   = H+ic;
    ic      = any(H(:, any(Htemp>1)),2);
    Htemp   = H+ic;
    if s == sum(ic)
        a = false;
    else
        s = sum(ic);
        a = true;
    end
    if sum(ic(:,1)) == size(H,1)
        return;
    else
        ic2 = ic;
    end
end

对于我的随机矩阵,这不会通过 while 循环进行两次以上的迭代,但它应该完全按照您上面的代码所做的,只是更快。计算大约需要 0.9 秒。

【讨论】:

  • 非常感谢您的回答!它可以工作并且速度提高了约 26%。不幸的是,当我使用大矩阵运行它时,我的代码仍然需要很长时间。非常感谢您提供进一步的建议来提高我的代码效率。
  • 这很有趣,我只是用你给的矩阵大小试了一下,计算大约需要 0.5 秒...
  • 问题是我在代码运行期间构造了 H 矩阵,并且在我添加到矩阵中的几乎每个 1 之前,我都需要确定 ic。因此,对于 (500 x 1000) 矩阵,这些行被调用 11574 次,大约需要 12 秒。我的目标是使用 (5000 x 10 000) 矩阵运行代码。
  • 我仍然不明白您如何归档 11574 调用 500x1000 矩阵。如果可能的话,也许分享更多你的代码,或者告诉我们你是如何构建你的矩阵的,这可能有助于理解你的问题。
  • 我编辑了我的问题,希望这有助于理解我的问题。非常感谢您的反馈。
猜你喜欢
  • 2014-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-10
  • 2021-04-19
  • 1970-01-01
  • 2020-06-03
  • 2014-01-11
相关资源
最近更新 更多