为什么 Matlab parfor 调度程序让工作人员闲置?
Why is the Matlab parfor scheduler leaving workers idle?
我有一个相当长的 运行 parfor
循环(假设 100,000 次迭代,每次迭代大约需要一分钟),我是 运行 36 核。我注意到,在工作接近尾声时,大量内核闲置,而少数内核完成了我认为每个工作人员必须进行多次迭代的工作。这导致大量的计算时间浪费在等待一名工人完成几项工作,而其他工人却闲着。
以下脚本显示了该问题(使用文件交换实用程序 Par.m
):
% 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);
按照我的看法,每个工作人员都应该忙到队列为空,之后所有工作人员都闲着。但在这里看起来这并没有发生——在 480 次迭代中,我得到了 6-20 次迭代,这些迭代是在另一个工作人员闲置 一个完整周期 之后从一个工作人员开始的。这个数字似乎与接近总数 2% 的循环迭代次数成线性比例。通过有限的测试,这似乎在 Matlab 2016b 和 2014b 中是一致的。
是否有任何理由表明这是预期的行为,或者这只是 parfor
实现中的一个写得不好的调度程序?如果是这样,我该如何构建它,这样我就不会和闲置的工人坐在一起这么久?
我认为这可以解释您所观察到的情况。
If there are more iterations than workers, some workers perform more than one loop iteration; in this case, a worker might receive multiple iterations at once to reduce communication time. (From "When to Use parfor")
在一个循环即将结束时,两个工作人员可能会同时完成他们的迭代。如果只剩下一组迭代要分配,那么一个工作人员将获得所有迭代,而另一个将保持空闲状态。这听起来像是预期的行为,这可能是因为底层实现试图减少与工作池相关的通信成本。我查看了网络和 Matlab 设置,似乎没有办法调整通信策略。
parfor
调度程序尝试 load-balance for 循环,其中迭代不会花费统一的时间量。不幸的是,正如您所观察到的,这可能导致工作人员在循环结束时变得闲置。使用parfor
,您无法控制分工;但您可以使用 parfeval
将您的工作分成均匀的块 - 这可能会给您更好的利用率。或者,您甚至可以将 spmd
与 for-drange
循环结合使用。
我有一个相当长的 运行 parfor
循环(假设 100,000 次迭代,每次迭代大约需要一分钟),我是 运行 36 核。我注意到,在工作接近尾声时,大量内核闲置,而少数内核完成了我认为每个工作人员必须进行多次迭代的工作。这导致大量的计算时间浪费在等待一名工人完成几项工作,而其他工人却闲着。
以下脚本显示了该问题(使用文件交换实用程序 Par.m
):
% 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);
按照我的看法,每个工作人员都应该忙到队列为空,之后所有工作人员都闲着。但在这里看起来这并没有发生——在 480 次迭代中,我得到了 6-20 次迭代,这些迭代是在另一个工作人员闲置 一个完整周期 之后从一个工作人员开始的。这个数字似乎与接近总数 2% 的循环迭代次数成线性比例。通过有限的测试,这似乎在 Matlab 2016b 和 2014b 中是一致的。
是否有任何理由表明这是预期的行为,或者这只是 parfor
实现中的一个写得不好的调度程序?如果是这样,我该如何构建它,这样我就不会和闲置的工人坐在一起这么久?
我认为这可以解释您所观察到的情况。
If there are more iterations than workers, some workers perform more than one loop iteration; in this case, a worker might receive multiple iterations at once to reduce communication time. (From "When to Use parfor")
在一个循环即将结束时,两个工作人员可能会同时完成他们的迭代。如果只剩下一组迭代要分配,那么一个工作人员将获得所有迭代,而另一个将保持空闲状态。这听起来像是预期的行为,这可能是因为底层实现试图减少与工作池相关的通信成本。我查看了网络和 Matlab 设置,似乎没有办法调整通信策略。
parfor
调度程序尝试 load-balance for 循环,其中迭代不会花费统一的时间量。不幸的是,正如您所观察到的,这可能导致工作人员在循环结束时变得闲置。使用parfor
,您无法控制分工;但您可以使用 parfeval
将您的工作分成均匀的块 - 这可能会给您更好的利用率。或者,您甚至可以将 spmd
与 for-drange
循环结合使用。