Matlab 为什么调度程序让工人闲置?

Matlab 为什么调度程序让工人闲置?,matlab,parallel-processing,parfor,Matlab,Parallel Processing,Parfor,我有一个相当长的parfor循环(假设100000次迭代,每次迭代大约需要一分钟),我用36个内核运行。我注意到,在工作接近尾声时,大量的内核处于空闲状态,而一些内核完成了我认为每个工作线程的多次迭代。这会导致浪费大量的计算时间,等待一个工人完成几个工作,而其他工人则无所事事 以下脚本显示了问题(使用文件交换实用程序Par.m): 在我看来,每个工人都应该很忙,直到队列空了,然后所有工人都闲坐着。但在这里,这看起来并没有发生——有480次迭代,我得到了6-20次迭代,在另一个工人闲置了一个完整的

我有一个相当长的
parfor
循环(假设100000次迭代,每次迭代大约需要一分钟),我用36个内核运行。我注意到,在工作接近尾声时,大量的内核处于空闲状态,而一些内核完成了我认为每个工作线程的多次迭代。这会导致浪费大量的计算时间,等待一个工人完成几个工作,而其他工人则无所事事

以下脚本显示了问题(使用文件交换实用程序
Par.m
):

在我看来,每个工人都应该很忙,直到队列空了,然后所有工人都闲坐着。但在这里,这看起来并没有发生——有480次迭代,我得到了6-20次迭代,在另一个工人闲置了一个完整的周期之后,在一个工人身上开始。这个数字似乎与循环迭代次数成线性比例,循环迭代次数接近总次数的2%。在有限的测试中,这似乎与Matlab 2016b和2014b一致


是否有任何原因表明这是预期的行为,或者这只是
parfor
实现中编写得很差的调度器?如果是这样的话,我该如何组织这一切,这样我就不会和无所事事的工人坐在一起那么久了

我想这就解释了你所观察到的情况

如果迭代次数多于worker,则一些worker执行多个循环迭代;在这种情况下,工作人员可能一次接收多个迭代以减少通信时间。(从“开始”)


在循环结束时,两个工作人员可能会在几乎相同的时间完成他们的迭代。如果只剩下一组迭代需要分配,那么一个工人将获得所有迭代,而另一个工人将保持空闲。这听起来像是预期的行为,可能是因为底层实现试图降低与工作池相关的通信成本。我查看了web和Matlab设置,似乎没有办法调整通信策略。

parfor调度器尝试对迭代时间不一致的循环进行负载平衡。不幸的是,正如您所观察到的,这可能会导致工人在循环结束时变得空闲。使用
parfor
,您无法控制工作分工;但是你可以使用
parfeval
将你的工作分成几块,这可能会给你更好的利用率。或者,您甚至可以将
spmd
与循环结合使用。

如何使用
spmd
进行显式调度?我想我写了一个例子,但它似乎遇到了与
parfor
相同的问题。
for drange
构造只是将循环迭代划分为大小相等的块,这就是我的意思。如果每次迭代花费相同的时间,这是很好的。
% Set up parallel pool
nLoop = 480;
p1 = gcp;

% Run a loop 
pclock = Par(nLoop);
parfor iLoop = 1:nLoop
  Par.tic;
  pause(0.1);
  pclock(iLoop) = Par.toc;  
end
stop(pclock);
plot(pclock);

% Process the timing info: 
runs = [[pclock.Worker]' [pclock.ItStart]' [pclock.ItStop]'];
nRuns = arrayfun(@(x) sum(runs(:,1) == x), 1:max(runs));
starts = nan(max(nRuns), p1.NumWorkers);
ends = nan(max(nRuns), p1.NumWorkers);
for iS = 1:p1.NumWorkers
  starts(1:nRuns(iS), iS) = sort(runs(runs(:, 1) == iS, 2));
  ends(1:nRuns(iS), iS) = sort(runs(runs(:, 1) == iS, 3));
end

firstWorkerStops = min(max(ends));
badRuns = starts > firstWorkerStops;
nBadRuns = sum(sum(badRuns)) - (p1.NumWorkers-1);

fprintf('At least %d (%3.1f%%) iterations run inefficiently.\n', ...
  nBadRuns, nBadRuns/nLoop * 100);