考虑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 cell
在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
如果我运行parfor
带环4
当地工人在我的MacBook Pro需要1.2秒。 更换parfor
与for
它需要0.01秒。
与我的实际数据,所述时间差是31秒与7秒[创建矩阵的C
也比较复杂。
如果理解正确的问题是,计算机有送baseA
到每个本地工人,这需要时间和内存。
你可以建议一个解决方案,能够使parfor
比更方便for
? 我认为,储蓄在所有细胞中baseA
是一种通过在开始装载一次,以节省时间,但也许我错了。
一般信息
基本上, 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=1000
和eig(500)
; 我的电脑有四个核的2.66GHz的I5-750处理器,运行MATLAB R2012a。 正如你所看到的并行测试的平均徘徊在0.29s有很多蔓延,而串行代码是相当稳定的周围0.24s。 总的时间,但是,下楼从234S到72S,这是3.25倍加速。 究其原因,这是不完全的4内存开销,作为额外的时间每次迭代需要表达。 内存开销是由于MATLAB无需检查了每个核心在做,并确保每个循环迭代只有一次,该数据被放入正确的存储位置进行。
切片广播数据到一个单元阵列
下面的方法也适用于这是由一群循环的数据。 没关系分组变量是什么,只要它是在循环之前确定。 速度优势是巨大的。
这样的简化示例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
循环,一个普通的parfor
和parfor
的切片数据,并会比较计时。
请记住,这是一个玩具例子,我不感兴趣,喜欢另类的解决方案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