Matlab并行计算工具箱,在parfor循环中动态分配工作

时间:2012-03-30 04:55:11

标签: matlab parallel-processing load-balancing

我正在使用matlab中长时间运行的parfor循环。

parfor iter=1:1000
   chunk_of_work(iter);
end

每次运行通常有大约2-3个定时异常值。也就是说,每执行1000次大量的工作,其中2-3次比其余工作长约100倍。当循环接近完成时,评估异常值的工人继续运行,而其余工人没有计算负荷。

这与parfor循环静态分配工作一致。这与并行计算工具箱found here的文档形成对比:

  

“工作分配是动态的。而不是分配固定的   迭代范围,工作者只有在之后才会被分配一个新的迭代   他们完成了当前迭代的处理,从而产生了一个   甚至是工作负荷分配。“

有关正在发生的事情的任何想法?

4 个答案:

答案 0 :(得分:5)

我认为你引用的文档有一个非常好的描述,它被认为是静态的工作分配:每个工作者被分配一个固定的迭代范围"。对于4名工人,这意味着第一个被分配iter 1:250,第二个被iter 251:500,...或者第一个为1:4:100,2:4:1000对于第二个等等。

您没有准确说出您所观察到的内容,但您所描述的内容与动态工作负载分配完全一致:首先,四个(示例)工作者分别在一个iter上工作,第一个完成的工作在第五个,下一个完成(如果前四个中的三个需要更长时间,可能会相同)在第六个上工作,依此类推。现在,如果您的异常值按照MATLAB选择处理循环迭代的顺序为数字20,850和900,并且每次迭代需要100倍,这仅意味着第二次到第320次迭代将由四个工作者中的三个解决,而一个是忙于第20次(到320将完成,现在假设大致均匀分布非离群值计算时间)。然而,被分配第850次迭代的工作人员将继续运行,即使在另一次解决了#1000之后,#900也是如此。事实上,如果有大约1100次迭代,那么#900工作的那个应该大致在其他的时候完成。

[已编辑,因为原始的措辞暗示MATLAB仍然会按1到1000的顺序分配parfor循环的迭代,这不应该被假定为]

这么长的故事,除非你找到一种方法来首先处理你的异常值(这当然要求你先验知哪些是异常值,并找到一种方法让MATLAB用这些开始parfor循环处理) ,仅靠动态工作负载分配无法避免您观察到的影响。

补充:但是,我认为你观察到,当循环接近完成时,评估异常值的工人* s *继续运行"似乎暗示至少有下列之一

  1. 异常值是MATLAB开始处理的最后一次迭代
  2. 你有很多工人,按迭代次数的顺序
  3. 您对异常值数量的估计(2-3)或您对计算时间罚分的估计(因子100)太低

答案 1 :(得分:3)

PARFOR的工作分配在某种程度上是确定性的。您可以通过让每个工作人员记录到磁盘的方式来准确地观察发生了什么,但基本上事实证明,PARFOR以确定的方式将您的循环划分为块,但是动态地将它们置换掉。不幸的是,目前无法控制这种组块。

但是,如果你无法预测1000个案例中的哪一个会成为异常值,那么很难想象一个有效的分配工作方案。

如果你可以预测你的异常值,你可以利用这样一个事实,即粗略地说,PARFOR以相反的顺序执行循环迭代,所以你可以把它们放在循环的“结束”,所以工作从它们开始立即

答案 2 :(得分:3)

@ arne.b的回答很好地描述了你所遇到的问题,我没有任何补充。

但是,并行计算工具箱确实包含用于分解job into tasks以进行独立执行的函数。从您的问题来看,不可能得出结论认为这是合适的,或者这不适合您的应用。如果是这样,一般策略是将工作分解为某种规模的任务,并让每个处理器处理任务,完成后返回到未完成任务的堆栈并从另一个开始。

您可能能够分解您的问题,以便一个任务替换一个循环迭代(许多任务,管理计算但最佳负载平衡的大量开销)或者一个任务替换N个循环迭代(更少的任务,开销越小,负载均衡越差)。与parfor相比,工作和任务实施起来有点棘手。

答案 3 :(得分:0)

作为PARFOR的替代方案,在R2013b及更高版本中,您可以使用PARFEVAL并以您认为合适的方式划分工作。如果合适,你甚至可以在取得足够的结果后取消“时间异常值”。当然,将现有循环划分为1000个单独的远程PARFEVAL调用时会产生开销。也许这是一个问题,也许不是。这就是我想象的那种:

for idx = 1:1000
     futures(idx) = parfeval(@chunk_of_work, 1, idx);
end
done = false; numComplete = 0;
timer = tic();
while ~done
    [idx, result] = fetchNext(futures, 10); % wait up to 10 seconds
    if ~isempty(idx)
        numComplete = numComplete + 1;
        % stash result
    end
    done = (numComplete == 1000) || (toc(timer) > 100);
end
% cancel outstanding work, has no effect on completed futures
cancel(futures);