matlab 中内存限制矩阵乘法时的策略

问题描述 投票:0回答:2

我需要计算

sum(x(:)*y,2)
,其中
x
是一维向量(1x1000),
y
是一维向量(1x1e8),两者都不稀疏。为了提高效率,我将向量设为单精度数组。另一个可能重要也可能不重要的信息是
x
只是一个单调递增的向量,例如
x=linspace(0,1,1000)
.

试图

A=sum(x(:)*y,2)
我遇到了记忆问题。所以我想我会做一个for循环。

for i=1:length(y)
    A=A+sum(x(:)*y(i),2);
end

这是低效的并且需要很长时间,所以我想将总和分解成几个大小相似的块(除了最后一个大小可能略有不同):

        nn = 10 ;% # of chunks 
        idN=length(y);
        splitn = floor(idN/nn);
        split = [1:splitn:idN];

        for i=1:nn-2
            A=A+sum(x(:)*y(split(ni): split(ni)+splitn-1),2)   
        end

        A = A +sum(x(:)*y(split(nn-1):end),2);

这需要很多时间,但不会永远(~45 分钟)。除了购买新电脑之外,我还能做些什么来加快速度?我不认为

parfor
会有帮助,因为无论如何只有一定数量的内存。只要每次迭代都包含在内存中,块的数量是否重要? 任何改进这种乘法的策略或建议都会很棒。

performance matlab vectorization matrix-multiplication
2个回答
0
投票

您使用的是哪个版本的 MATLAB,在什么机器上使用什么?我构造了和你一样大小的向量

x = linspace(0,1,1000);
y = rand(1,1e8);

果然,如果我尝试直接计算,我会得到一个内存错误

>> A=sum(x(:)*y,2)
Error using  * 
Requested 1000x100000000 (745.1GB) array exceeds maximum array size preference (31.7GB). This might cause MATLAB to
become unresponsive.

Related documentation

所以我试试你的循环

tic
Asplit=0;
for i=1:length(y)
    Asplit=Asplit+sum(x(:)*y(i),2);
end
toc

这在 74 秒内完成。 比您引用的 45 分钟快得多。 我在第 11 代英特尔上使用 MATLAB 2023a beta。 Core(TM) i7-11700 @ 2.50GHz。

我还可以通过使用基于线程的 parpool 和 parfor 循环来进一步降低速度

parpool('threads');

tic
Asplit=0;
parfor i=1:length(y)
    Asplit=Asplit+sum(x(:)*y(i),2);
end
toc

在我的 8 核系统上减少到 15.5 秒。 8 个 CPU 加速 5 倍。不是线性缩放,但我会接受它。

另请注意,我在这里使用双精度。你说你使用的是单精度。让我们看看它长什么样

%try again using single precision
x = single(linspace(0,1,1000));
y = rand(1,1e8,'single');

parpool('threads');

tic
Asplit=0;
parfor i=1:length(y)
    Asplit=Asplit+sum(x(:)*y(i),2);
end
toc

12.8 秒。更快但值得损失精度吗?我想这取决于你的应用程序。

最后,让我们使用@beaker 和其他人在评论中讨论的技巧

A=x(:)*sum(y)
。将结果与我们到目前为止一直在看的 split 方法进行比较。

x = linspace(0,1,1000);
y = rand(1,1e8);

parpool('threads');

tic
Asplit=0;
parfor i=1:length(y)
    Asplit=Asplit+sum(x(:)*y(i),2);
end
toc

tic
Aalt = x(:)*sum(y);
toc

max(abs(Aalt-Asplit))

x(:)*sum(y)
耗时 0.026 秒 两种方法之间的最大绝对差异约为 1e-6.


0
投票

1.-内存瓶颈问题

无论您对完整的表格

x
y
做什么:如果您推送问题中显示的此类数据和操作,45分钟等待或内存不足警告肯定会出现。

2.- 选项 1:增加 RAM

一个人可以走到口袋深处或钱包里装满。祝你好运。

3.- 选项 2:并行池

也祝你好运。

4.- 我的建议:将大变量分解成可处理的块

所以这是:

  • 分解堵塞记忆的变量

  • 一次操作/处理每个块

  • 将结果存储在磁盘中

使用我最喜欢的 MATLAB 命令之一执行此操作的一种方法:全天候,强大且经过多次战斗强化

evalin
(此处为喇叭)

我试着分解

y
,直到我发现100个它都有效。可能更大的碎片也可能起作用,但只有 4 或 10 块甚至可能无法启动。

close all;clear all;clc

x=randi([-1e3 1e3],1,1e3);
y=randi([-1e3 1e3],1,1e8);

N=1e2

A1=zeros(numel(x),numel(y)/N);
A2=zeros(numel(x),numel(y)/N);
A3=zeros(numel(x),numel(y)/N);
A4=zeros(numel(x),numel(y)/N);


k1=2;
L1=['A' num2str(k1) '=zeros(numel(x),numel(y)/N);'];
evalin('base',L1)

tic

for k1=1:N
    
    for k2=1:length(x)/N
        a1=x(k2);
        a2=a1*y(k2);
        L1=['A' num2str(k1) '=zeros(numel(x),numel(y)/N);'];
        evalin('base',L1);
        L2=['A' num2str(k1) '(k2,:)=sum(a2);'];
        evalin('base',L2);
        
        % WRITE Ak1 to FILE
    end

end

% WRITE Ak1 to FILE
行替换为类似

的内容
L3=['']   % build command line into string
evalin('base',L3);  % invoke evalin

MATLAB 有一系列不同的命令来写入和读取文件,格式多种多样,例如 .txt 和 Excel,或者使用命令直接写入二进制文件

fwrite
.

要从文件中读取,您还必须构建字符串并使用

evalin
调用它们,但如上所示,构建如此简洁的字符串很容易,因为所做的只是用代码构建变量,并且必须在变量中嵌入可变索引此类变量的声明,它已被分解成的 y 部分。

简而言之:使用可以合理地进出 RAM 的变量,而不会像最近的苏伊士运河交通堵塞那样导致船只阻塞其他船只。

© www.soinside.com 2019 - 2024. All rights reserved.