use*_*148 18 parallel-processing optimization matlab parfor
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')
Run Code Online (Sandbox Code Playgroud)
为了了解我的数据中的实际维度,1x9 cellin A1由以下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
Run Code Online (Sandbox Code Playgroud)
如果我在我的Macbook Pro中parfor使用4本地工作人员运行循环,则需要1.2秒.parfor用for它替换需要0.01秒.
根据我的实际数据,时间差为31秒对7秒[矩阵的创建C也更复杂].
如果已正确理解问题是计算机必须发送baseA给每个本地工作人员,这需要时间和内存.
您能否提出一个能够parfor比方便更方便的解决方案for?我认为保存所有单元格baseA是一种通过在开始时加载一次来节省时间的方法,但也许我错了.
Adr*_*aan 35
基本上,parfor建议在两种情况下:循环中的大量迭代(即,类似for),或者每次迭代需要很长时间(例如parfor).在第二种情况下,您可能需要考虑使用parfor(比1e10我的经验慢).原因eig(magic(1e4))是spmd短距离循环慢或快速迭代是正确管理所有工作人员所需的开销,而不是仅仅进行计算.
此外,许多函数都内置了隐式多线程,因此parfor在使用这些函数时,循环不会比串行parfor循环更高效,因为所有核心都已被使用.for在这种情况下实际上是有害的,因为它具有分配开销,同时与您尝试使用的功能并行.
有关在不同工作人员之间拆分数据的信息,请查看此问题.
请考虑以下示例,以查看for与之相反的行为parfor.首先打开并行池,如果你还没有这样做:
gcp; % Opens a parallel pool using your current settings
Run Code Online (Sandbox Code Playgroud)
然后执行几个大循环:
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[-]';
Run Code Online (Sandbox Code Playgroud)
然后用parfor相反的方法做同样的事情for.您会注意到每次迭代的平均时间会增加(对于我的情况,为0.27s至0.39s).然而,要意识到已parfor使用的所有可用工作程序,因此总时间(sum(Time))必须除以计算机中的核心数.所以对于我的情况,总时间从大约270s下降到49s,因为我有一个octacore处理器.
因此,虽然每次单独迭代的时间parfor随着使用而增加for,但总时间显着下降.
这张照片显示了我在家用电脑上运行测试的结果.我曾经n=1000和eig(500); 我的电脑有一个I5-750 2.66GHz处理器,带有四个内核,运行MATLAB R2012a.正如你所看到的那样,并行测试的平均值大约在0.29s左右徘徊,而且序列代码相当稳定在0.24s左右.然而,总时间从234秒下降到72秒,这是3.25倍的加速.这不是4的原因是内存开销,如每次迭代所花费的额外时间所表示的那样.内存开销是由于MATLAB必须检查每个内核正在做什么,并确保每次循环迭代只执行一次,并且数据被放入正确的存储位置.
Ole*_*leg 12
以下方法适用于按组循环的数据.分组变量是什么并不重要,只要它在循环之前确定即可.速度优势是巨大的.
这样的简化示例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
Run Code Online (Sandbox Code Playgroud)
现在,为简单起见,我想对sum()第二列中的值组感兴趣.我可以按组循环,索引感兴趣的元素并总结它们.我将与执行此任务for循环,一个普通的parfor和parfor与切片数据,并会比较计时.
请记住,这是一个玩具示例,我对替代解决方案不感兴趣bsxfun(),这不是分析的重点.
借用Adriaan的相同类型的情节,我首先确认了关于普通parforvs 的相同发现for.其次,这两种方法都完全优于由parfor上切片数据,这需要位2秒以上在数据集中完成1000万行(在切片操作被包括在定时).平原parfor需要24秒才能完成,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')
Run Code Online (Sandbox Code Playgroud)
你可以cache2cell()在我的github回购中找到.
您可能想知道如果我们for在切片数据上运行简单会发生什么?对于这个简单的玩具示例,如果我们通过切片数据来取消索引操作,我们就会删除代码的唯一瓶颈,并且for实际上比实际上更快parfor.
然而,这是一个玩具示例,其中内循环的成本完全由索引操作获得.因此,为了parfor值得,内环应该更复杂和/或展开.
现在,假设您的内部循环更复杂并且简单的for循环更慢,让我们看看我们通过避免4个工作人员和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
Run Code Online (Sandbox Code Playgroud)
切出来的c样子
c(1:5)
ans =
[ 969x2 double]
[ 970x2 double]
[ 949x2 double]
[ 986x2 double]
[1013x2 double]
Run Code Online (Sandbox Code Playgroud)
这里c{1}是
c{1}(1:5,:)
ans =
1 0.58205
1 0.80183
1 -0.73783
1 0.79723
1 1.0414
Run Code Online (Sandbox Code Playgroud)