matlab中对大型非稀疏数组的有效操作
Posted
技术标签:
【中文标题】matlab中对大型非稀疏数组的有效操作【英文标题】:Efficient manipulation of large, non-sparse arrays in matlab 【发布时间】:2021-11-09 00:43:32 【问题描述】:我有一个行向量q
和200
元素,还有另一个行向量dij
,它是pdist
函数的输出,当前有48216200
元素,但我想成为能够走得更高。我要做的操作本质上是:
t=sum(q'*dij,2);
但是,由于它试图分配一个200x48211290
数组,它抱怨这需要 70GB 的内存。因此我这样做:
t = zeros(numel(q),1);
for i=1:numel(q)
qi = q(i);
factor = qi*dij;
t(i)=sum(factor);
end
但是,这需要太多时间。太多时间,我的意思是大约需要36s
,这比pdist
函数所需的时间长几个数量级。有没有一种方法可以在不显式分配这么多内存的情况下加快此操作?我在这里假设,如果第一种方式可以分配内存,(作为向量操作)它会更快。
【问题讨论】:
尝试使用高阵列。 mathworks.com/help/matlab/tall-arrays.html @LuisMendo 是的,很好。 用dij
100万个元素运行你的代码,矢量化解耗时0.58秒,循环耗时0.13秒。显然循环是更有效的解决方案,因为这种差异只会随着更大的数组而增加。请在假设矢量化代码更快之前为您的代码计时!
@L.ScottJohnson 不太好用,因为在 gather()
步骤中它给出了相同的内存错误。
【参考方案1】:
就加法而言,只需使用乘法的distributive property:
t = q'*sum(dij);
【讨论】:
除此之外,您还可以将数据转换为单个数据,例如single(dij),将内存减少一半。 好吧,所以在我试图让这个问题成为一个最小可重现的例子时,我把它做得太简单了。我需要计算的全部内容是sin(q*dij)./(q*dij)
。我将继续接受这个答案,因为它是正确的,但是如果您对带有 sin
的版本有任何想法,我会全力以赴。
@sodiumnitrate 你还缺少转置吗?反正sine破坏了分配性质,想不出办法来简化它
是的,应该是q'
。感谢您的尝试。
@sodiumnitrate 如果删除循环代码中的一些中间变量,可以加快速度:t(i)=sum(q(i)*dij);
。 sin
的版本也是如此。将事物放在一条线上允许 JIT 以不同的方式优化计算。我敢打赌,对sin
的调用是您计算中更昂贵的部分,并且在矢量化操作中也不会好多少。【参考方案2】:
为了测试 Cris 在第一条评论中所说的内容,我创建了 3 个“.m”文件,如下所示:
vec.m:
res=sum(sin(d.*q')./(d.*q'));
forloop.m
for i=1:200
res(i)=sum(sin(d.*q(i))./(d.*q(i)));
end
和 test.m:
clc
clear all
d=rand(4e6,1);
q=rand(200,1);
res=zeros(1,200);
forloop;
vec;
forloop;
vec;
forloop;
vec;
然后我使用了 matlab 运行和时间分析器, 结果非常令人惊讶! :
3 calls to forloop : ~10.5 S
3 call to vec : 15.5 S !!!
另外,当我将数据转换为单一数据时,结果是:
... forloop : 7.5 S
... vec : 8.5 S
我不知道为什么在这些情况下 for 循环更快,但至于你的问题,你可以通过在循环中生成较少的变量并使用垂直向量来加快速度(我认为)。最后将您的数据转换为单个值:
q=single(rand(200,1));
...
【讨论】:
循环在 MATLAB 中不再慢。他们很久没来了。 15 年前,用矢量化代码替换循环将使计算速度提高 100 倍。现在在好的情况下可能是 2 倍。相比之下,使用大型中间数组(向量化时经常需要,就像这里的情况一样)效率低下:缓存没有得到有效使用。这导致比 15 年前的等效循环快得多的矢量化代码比今天的循环慢得多。 您的代码中的一些 cmets: (1) 始终使用timeit
来计时代码,分析器不是比较执行时间的最佳方式,它旨在找到代码中较慢的部分。探查器每隔一段时间就会探查正在运行的代码,查看正在运行的代码行。 timeit
只是计算运行一段代码所需的时间,运行代码足够长的时间以获得良好的时间,忽略“预热时间”等。 (2) clear all
从内存中删除函数。这会导致它们第一次的执行速度变慢。您通常不需要clear all
,clear
本身就足够了。
(3) 您可以在脚本文件的底部编写函数,不再需要将每个函数写在单独的M文件中。以上是关于matlab中对大型非稀疏数组的有效操作的主要内容,如果未能解决你的问题,请参考以下文章