matlab中对大型非稀疏数组的有效操作

Posted

技术标签:

【中文标题】matlab中对大型非稀疏数组的有效操作【英文标题】:Efficient manipulation of large, non-sparse arrays in matlab 【发布时间】:2021-11-09 00:43:32 【问题描述】:

我有一个行向量q200 元素,还有另一个行向量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 allclear 本身就足够了。 (3) 您可以在脚本文件的底部编写函数,不再需要将每个函数写在单独的M文件中。

以上是关于matlab中对大型非稀疏数组的有效操作的主要内容,如果未能解决你的问题,请参考以下文章

多维数组-矩阵的压缩存储- 稀疏矩阵(一)

每天一个数据结构之稀疏数组

每天一个数据结构之稀疏数组

稀疏数组

如何高效存储大型稀疏数组

稀疏数组与二维数组的转换