【问题标题】:What is the reason behind parfeval's time overhead compared to a serial implementation?与串行实现相比,parfeval 的时间开销背后的原因是什么?
【发布时间】:2021-07-06 21:07:03
【问题描述】:

我正在尝试并行化 Gauss-Seidel 算法中使用的一些代码,以逼近线性方程组的解。

简而言之,对于NxN 矩阵,在一次迭代期间,我正在逐一进行sqrt(N) 并行计算会话。在一个并行计算会话中,我将计算sqrt(N) 值的任务分配给可用的worker。

并行计算会话中涉及的代码是这样的:

future_results(1:num_workers) = parallel.FevalFuture;
for i = 1:num_workers
    start_itv = buck_bound+1 + (i - 1) * worker_length;
    end_itv = min(buck_bound+1 + i * worker_length - 1, ends_of_buckets(current_bucket));                 
    future_results(i) = parfeval(p, @hybrid_parallel_function, 3, A, b, x, x_last, buck_bound, n, start_itv, end_itv);
end
            
for i = 1:num_workers
    [~, arr, start_itv, end_itv] = fetchNext(future_results(i));               
    x(start_itv:end_itv) = arr;
end

parfeval调用的函数是这样的:

function [x_par, start_itv, end_itv] = hybrid_parallel_function (A, b, x, x_last, buck_bound, n, start_itv, end_itv)
    x_par = zeros(end_itv - start_itv + 1, 1);
    for i = start_itv:end_itv
        x_par(i-start_itv+1) = b(i);
        x_par(i-start_itv+1) = x_par(i-start_itv+1) - A(i, 1:buck_bound) * x(1:buck_bound);
        x_par(i-start_itv+1) = x_par(i-start_itv+1) - A(i, buck_bound+1:i-1) * x_last(buck_bound+1:i-1);
        x_par(i-start_itv+1) = x_par(i-start_itv+1) - A(i, i+1:n) * x_last(i+1:n);
        x_par(i-start_itv+1) = x_par(i-start_itv+1) / A(i, i);
    end
end

完整的代码可以在这里找到:https://pastebin.com/hRQ5Ugqz

1000x1000 矩阵的 matlab 分析器。并行代码比串行代码慢 20 到 135 倍,具体取决于所选的系数矩阵(但仍比 spmd 快得多)。

parfeval 计算可能会在第 50 行和第 57 行之间延迟拆分?尽管如此,我还是无法向自己解释为什么会有这么大的开销。这似乎与调用 parfeval 的次数有关:我确实通过降低 parfeval 调用来降低执行时间。

有什么可以进一步优化的吗?我必须求助于用 C++ 编写代码吗?

请帮忙。非常感谢!

【问题讨论】:

  • 您是如何配置并行池的?默认情况下,MATLAB 使用多个 MATLAB 会话,并在它们之间传递数据。这是为在计算集群上运行而设计的,集群中的每个节点都运行一个会话。不过,您可以将其配置为使用基于线程的并行性。无论如何,较少的大型作业比许多小型作业更好地并行化。
  • 你可能是对的,我会在更新到 R2020 后立即尝试 parpool('threads'),谢谢。

标签: matlab parallel-processing parfor spmd


【解决方案1】:

这里有几种可能性。最重要的是一个简单的事实,如果您使用'local' 集群类型,那么工作人员正在单线程代码中运行。在“串行”代码实际上利用 MATLAB 的内在多线程的情况下,您已经充分利用了可用的 CPU 硬件,而使用并行工作器无法为您带来任何好处。不确定您是否属于这种情况,但鉴于代码,我强烈怀疑它。

并行运行会产生开销,正如您所观察到的,运行较少的parfeval 调用会降低这些开销。您编写的代码将整个A 矩阵多次复制到每个工作人员。您不需要更改A,因此您可以使用parallel.pool.Constant 来避免重复复制。

虽然parfeval 更灵活,但在可以应用parfor 的情况下,它的效率往往低于parfor

是的,您可以期望工作人员在第一个 parfeval 呼叫完成后立即开始工作。

(对不起,这不是一个真正的“答案”,所以很快就会有好心人出现并删除它,但是评论太多了)。

【讨论】:

  • 您好,感谢您的回答!希望我确实正确地写下了 parallel.pool.Constant (pastebin.com/F5DX7KAR)。不幸的是,时间根本没有改善。正如你和 Cris Luengo 所说,我可能不得不尝试 parpool('threads'),它仅从 R2020a 开始可用,所以我必须等待一段时间才能更新。与此同时,我确实看过 std::thread,我想我可能有很好的机会用 C++ 编写它,即使这意味着搞砸矩阵乘法部分。
  • @catalyst:如果您要执行 C++ 实现,请不要使用 std::thread,而是使用任何更高级别的抽象。我更喜欢 OpenMP,但您也可以查看 Intel Treading Blocks 或任何数量的库来简化并行化算法的问题。在 OpenMP 中,就像将 #pragma parallel for 放在 for 循环前面一样简单。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-06-20
  • 1970-01-01
  • 1970-01-01
  • 2018-08-31
  • 2020-01-26
  • 2016-03-31
  • 1970-01-01
相关资源
最近更新 更多