在MATLAB中使用parfor时避免竞争条件

时间:2014-09-17 17:42:31

标签: matlab concurrency parallel-processing

我正在并行循环并在满足条件时更改变量。超级惯用的代码,我相信每个人都写了一百遍:

trials = 100;
greatest_so_far = 0;
best_result = 0;

for trial_i = 1:trials
    [amount, result] = do_work();

    if amount > greatest_so_far
        greatest_so_far = amount;
        best_result = result;
    end
end

如果我想将for替换为parfor,那么在检查是否应该替换greatest_so_far时,如何确保没有竞争条件?有没有办法将此变量锁定在支票之外?也许就像:

trials = 100;
greatest_so_far = 0;
best_result = 0;

parfor trial_i = 1:trials
    [amount, result] = do_work();

    somehow_lock(greatest_so_far);
    if amount > greatest_so_far
        greatest_so_far = amount;
        best_result = result;
    end
    somehow_unlock(greatest_so_far);
end

3 个答案:

答案 0 :(得分:2)

回答错误。它并不能完全解决您的问题,但它可能会帮助您避免它。

如果你能负担得起内存将do_work()的输出存储在某些向量中,那么你只需在此函数上运行parfor,存储结果,然后在结束(在循环之外):

amount = zeros( trials , 1 ) ;
result = zeros( trials , 1 ) ;

parfor trial_i = 1:trials
    [amount(i), result(i)] = do_work();
end

[ greatest_of_all , greatest_index ] = max(amount) ;
best_result = result(greatest_index) ;

编辑/评论 :( 想把它放在你的问题的评论中,但它太长了,抱歉)。
我熟悉.net并完全理解您的锁定/解锁请求。我自己尝试过多次尝试在很长的parfor循环中实现一种进度指示......但无济于事。

如果我正确理解了Matlab classification of variable,那么您分配greatest_so_far(在greatest_so_far=amount中)这一事实会让Matlab将其视为临时变量,将在每次循环迭代开始时清除并重新初始化(因此无法用于您的目的)。

因此,一个简单的锁定变量可能不是我们现在可以简单实现的概念。一些复杂的类事件或文件写入/检查可能会有所作为,但我担心时间会受到很大影响。如果每次迭代都需要很长时间才能执行,那么开销可能是值得的,但是如果你使用parfoor来加速大量的短执行迭代,那么复杂的解决方案会让你失速超过帮助......

您可以查看此堆叠交换问题,您可能会发现您感兴趣的内容:Semaphores and locks in MATLAB

答案 1 :(得分:2)

Hoki的解决方案是解决问题的正确方法。但是,当您询问有关竞争条件并在循环迭代相互依赖时阻止它们时,您可能需要调查spmd and the various lab* functions

答案 2 :(得分:1)

您需要使用SPMD来执行此操作 - SPMD允许工作人员之间的通信。像这样:

bestResult = -Inf;
bestIndex  = NaN;
N = 97;
spmd
  % we need to round up the loop range to ensure that each
  % worker executes the same number of iterations
  loopRange = numlabs * ceil(N / numlabs);
  for idx = 1:numlabs:loopRange
    if idx <= N
      local_result = rand(); % obviously replace this with your actual function
    else
      local_result = -Inf;
    end
    % Work out which index has the best result - use a really simple approach
    % by concatenating all the results this time from each worker
    % allResultsThisTime will be 2-by-numlabs where the first row is all the
    % the results this time, and the second row is all the values of idx from this time
    allResultsThisTime = gcat([local_result; idx]);
    % The best result this time - consider the first row
    [bestResultThisTime, labOfBestResult] = max(allResultsThisTime(1, :));
    if bestResultThisTime > bestResult
      bestResult = bestResultThisTime;
      bestIndex  = allResultsThisTime(2, labOfBestResult);
    end
  end
end
disp(bestResult{1})
disp(bestIndex{1})