节省时间和内存使用PARFOR?节省时间和内存使用PARFOR?(Saving time and m

2019-05-10 15:33发布

考虑prova.mat通过以下方式获得MATLAB

for w=1:100
    for p=1:9    
        A{p}=randn(100,1); 
    end
    baseA_.A=A;

    eval(['baseA.A' num2str(w) '= baseA_;'])

end

save(sprintf('prova.mat'),'-v7.3', 'baseA')

有在我的数据的实际尺寸的想法中, 1x9 cellA1由以下组成9数组: 904x5, 913x5, 1722x5, 4136x5, 9180x5, 3174x5, 5970x5, 4455x5, 340068x5 。 其他Aj的具有相似的组合物。

考虑下面的代码

clear all
load prova
tic
parfor w=1:100
       indA=sprintf('A%d', w);
       Aarr=baseA.(indA).A;
       Boot=[];
       for p=1:9
           C=randn(100,1).*Aarr{p};
           Boot=[Boot; C];  
       end
       D{w}=Boot;
end
toc

如果我运行parfor带环4当地工人在我的MacBook Pro需要1.2秒。 更换parforfor它需要0.01秒。

与我的实际数据,所述时间差是31秒与7秒[创建矩阵的C也比较复杂。

如果理解正确的问题是,计算机有送baseA到每个本地工人,这需要时间和内存。

你可以建议一个解决方案,能够使parfor比更方便for ? 我认为,储蓄在所有细胞中baseA是一种通过在开始装载一次,以节省时间,但也许我错了。

Answer 1:

一般信息

基本上, parfor建议在两种情况下:大量的迭代在循环(即类似1e10 ),或者如果每次迭代需要很长的时间(例如, eig(magic(1e4)) 在第二种情况下,你可能要考虑使用spmd (慢parfor在我的经验)。 究其原因parfor比慢for回路短距离或快速迭代是正确地管理所有工人,而不是仅仅做了计算所需要的开销。

也有很多的功能,具有隐含的多线程内置 ,使得parfor循环没有更有效的,当使用这些功能,比串行for循环,因为已经被使用的所有核心。 parfor实际上是在这种情况下损害,因为它具有分配开销,同时又是并行的,你要使用的功能。

检查这个问题放到不同的工人之间的分割数据信息。

标杆

请看下面的例子来看看的行为for ,而不是说的parfor 。 首先打开平行池,如果你没有这样做:

gcp; % Opens a parallel pool using your current settings

然后执行几个大循环:

n = 1000; % Iteration number
EigenValues = cell(n,1); % Prepare to store the data
Time = zeros(n,1);
for ii = 1:n
tic
    EigenValues{ii,1} = eig(magic(1e3)); % Might want to lower the magic if it takes too long
Time(ii,1) = toc; % Collect time after each iteration
end

figure; % Create a plot of results
plot(1:n,t)
title 'Time per iteration'
ylabel 'Time [s]'
xlabel 'Iteration number[-]';

然后做同样的parfor ,而不是for 。 你会发现,每次迭代的平均时间上升(0.27s到0.39s我的情况)。 一定要明白不过了parfor使用所有可用的工人,因此,总的时间( sum(Time) )必须在您的计算机的内核数量来划分。 所以对于我的情况下,总的时间去了各地270S到49S,因为我有一个八核心处理器。

所以,虽然做的每个单独迭代时间的推移了使用parfor相对于使用for ,总时间下降明显。

结果

图为正如我刚才跑了我家的电脑上测试的结果。 我用n=1000eig(500) ; 我的电脑有四个核的2.66GHz的I5-750处理器,运行MATLAB R2012a。 正如你所看到的并行测试的平均徘徊在0.29s有很多蔓延,而串行代码是相当稳定的周围0.24s。 总的时间,但是,下楼从234S到72S,这是3.25倍加速。 究其原因,这是不完全的4内存开销,作为额外的时间每次迭代需要表达。 内存开销是由于MATLAB无需检查了每个核心在做,并确保每个循环迭代只有一次,该数据被放入正确的存储位置进行。



Answer 2:

切片广播数据到一个单元阵列

下面的方法也适用于这是由一群循环的数据。 没关系分组变量是什么,只要它是在循环之前确定。 速度优势是巨大的。

这样的简化示例data是以下,用含有分组变量的第一列:

ngroups = 1000;
nrows   = 1e6;
data    = [randi(ngroups,[nrows,1]), randn(nrows,1)];
data(1:5,:)
ans =
          620     -0.10696
          586      -1.1771
          625       2.2021
          858      0.86064
           78       1.7456

现在,假设为简单起见,我感兴趣的sum()在第二列中由所述值的基团。 我可以按组回路,指数的兴趣和元素总结起来。 我将与执行此任务for循环,一个普通的parforparfor切片数据,并会比较计时。

请记住,这是一个玩具例子,我不感兴趣,喜欢另类的解决方案bsxfun()这不是分析的点。

结果

从借用的同一类型的图阿德里安 ,我先确认关于平原同一个结果parfor VS for 。 其次,这两种方法都完全由优于 parfor上切片数据,这需要位2秒以上在数据集中完成1000万行(在切片操作被包括在定时)。 平原parfor需要24S完成和for几乎两倍的时间量(我在Win7的64,R2016a和i5-3570与4芯)。

在开始之前,切片数据的要点parfor是为了避免:

  • 从整体数据的开销广播给工人,
  • 索引操作为不断增长的数据集。

代码

ngroups = 1000;
nrows   = 1e7;
data    = [randi(ngroups,[nrows,1]), randn(nrows,1)];

% Simple for
[out,t] = deal(NaN(ngroups,1));
overall = tic;
for ii = 1:ngroups
    tic
    idx     = data(:,1) == ii;
    out(ii) = sum(data(idx,2));
    t(ii)   = toc;
end
s.OverallFor = toc(overall);
s.TimeFor    = t;
s.OutFor     = out;

% Parfor
try parpool(4); catch, end
[out,t] = deal(NaN(ngroups,1));
overall = tic;
parfor ii = 1:ngroups
    tic
    idx     = data(:,1) == ii;
    out(ii) = sum(data(idx,2));
    t(ii)   = toc;
end
s.OverallParfor = toc(overall);
s.TimeParfor    = t;
s.OutParfor     = out;

% Sliced parfor
[out,t] = deal(NaN(ngroups,1));
overall = tic;
c       = cache2cell(data,data(:,1));
s.TimeDataSlicing = toc(overall);
parfor ii = 1:ngroups
    tic
    out(ii) = sum(c{ii}(:,2));
    t(ii)   = toc;
end
s.OverallParforSliced = toc(overall);
s.TimeParforSliced    = t;
s.OutParforSliced     = out;

x = 1:ngroups;
h = plot(x, s.TimeFor,'xb',x,s.TimeParfor,'+r',x,s.TimeParforSliced,'.g');
set(h,'MarkerSize',1)
title 'Time per iteration'
ylabel 'Time [s]'
xlabel 'Iteration number[-]';
legend({sprintf('for          : %5.2fs',s.OverallFor),...
        sprintf('parfor       : %5.2fs',s.OverallParfor),...
        sprintf('parfor_sliced: %5.2fs',s.OverallParforSliced)},...
        'interpreter', 'none','fontname','courier')

你可以找到cache2cell()在我的github回购 。

简单对切片数据

你可能不知道,如果我们运行简单的会发生什么for对切片的数据? 对于这个简单的玩具例子,如果我们通过分割数据带走的索引操作,我们删除代码的唯一的瓶颈,以及for实际上将slighlty parfor

然而,这是其中内循环的成本被完全采取的索引操作玩具的例子。 因此,对于parfor是值得的,内环应该更加复杂和/或展开。

节省内存的切片PARFOR

现在,假设你的内部循环更加复杂和简单for循环速度慢,让我们来看看我们是多么地节省内存,避免与4名工人PARFOR和数据集50万行(广播数据,在RAM约760 MB )。

正如你可以看到,近3 GB的额外内存被发送到工人。 切片操作需要一定内存来完成,但仍比广播操作少得多,并且可以在原则上覆盖了初始数据集,因此可以忽略不计的轴承RAM成本一次完成。 最后, parfor对切片数据将只使用的存储器小部分 ,即对应于正在使用的切片的量。

切成细胞

原始数据是通过组切片,并且每个部分存储到细胞中。 由于单元阵列是引用数组我们基本上隔开的连续的data在存储器分成独立块。

虽然我们的样本data看起来像这样

data(1:5,:)
ans =
          620     -0.10696
          586      -1.1771
          625       2.2021
          858      0.86064
           78       1.7456

出片c样子

c(1:5)
ans = 
    [ 969x2 double]
    [ 970x2 double]
    [ 949x2 double]
    [ 986x2 double]
    [1013x2 double]

其中c{1}

c{1}(1:5,:)
ans =
            1      0.58205
            1      0.80183
            1     -0.73783
            1      0.79723
            1       1.0414


文章来源: Saving time and memory using parfor?