为什么在parfor中访问2d矩阵这么慢?

use*_*619 2 performance matlab parfor

假设我有一个大矩阵A:

A = rand(10000,10000);
Run Code Online (Sandbox Code Playgroud)

以下串行代码耗时约0.5秒

tic
for i=1:5
    r=9999*rand(1);
    disp(A(round(r)+1, round(r)+1))
end
toc
Run Code Online (Sandbox Code Playgroud)

而使用parfor的以下代码耗时约47秒

tic
parfor i=1:5
    r=9999*rand(1);
    disp(A(round(r)+1, round(r)+1))
end
toc
Run Code Online (Sandbox Code Playgroud)

如何加快parfor代码的速度?

编辑:如果不是使用disp,我尝试使用以下代码计算总和

sum=0;
tic
for i=1:5000
   r=9999*rand(1);
   sum=sum+(A(round(r)+1, round(r)+1));
end
toc
Run Code Online (Sandbox Code Playgroud)

这需要0.025秒

但是parfor需要42.5秒:

tic
parfor i=1:5000
   r=9999*rand(1);
   sum=sum+(A(round(r)+1, round(r)+1));
end
toc
Run Code Online (Sandbox Code Playgroud)

Wol*_*fie 5

您的问题在于不考虑节点通信开销。

当您使用parfor使用并行计算循环,你必须要考虑这样做的小任务,客户端节点的几个工作节点的结构。

这是您提出的测试的一些问题:

  • 该功能disp是串行的,因为一次只能将一个结果显示到客户端节点。需要节点之间的通信来安排此任务。

  • 在循环外部创建求和意味着所有节点都必须将当前值传递回客户端节点。

  • A在所有示例中都是一个广播变量。从文档:

    这种类型的变量对于特定任务可能是有用的,甚至是必不可少的。但是,较大的广播变量可能导致客户端与工作人员之间的大量通信,并增加并行开销

    MATLAB编辑器对此进行警告,并使用以下工具提示在橙色变量下划线:

    整个数组或结构“ A”是广播变量。这可能会导致不必要的通信开销。


相反,我们可以预先计算一些随机索引,并将其切成A临时变量以在循环中使用。然后在循环之后进行收集操作(如对所有部分求和)。

k = 50;
sumA = zeros( k, 1 );         % Output values for each loop index
idx = randi( [1,1e4], k, 1 ); % Calculate our indices outside the loop
randA = A( idx, idx );        % Slice A outside the loop
parfor ii = 1:k
    sumA( ii ) = randA( ii ); % All that's left to do in the loop
end
sumA = sum( sumA );           % Collate results from all nodes
Run Code Online (Sandbox Code Playgroud)

我做了一个快速基准测试,使用R2017b和12个工作器将您的2个总和测试与上述代码进行比较,这是我的结果:

               Serial loop: ~ 0.001 secs      
Parallel with broadcasting: ~ 100   secs
  Parallel no broadcasting: ~ 0.1   secs
Run Code Online (Sandbox Code Playgroud)

并行循环对于此操作来说是多余的,开销没有道理,但是很明显,通过一些预分配和避免广播变量,它们至少慢了5个数量级!

看看没有广播变量的代码版本如何也使用更多的向量化,这将加快代码的使用甚至无需使用parfor。在使用并行计算之前优化代码不仅可以加快串行计算的速度,而且通常也使转换更容易!


旁注:sumi是错误的变量名,因为它们是内置函数的名称。

  • 这绝对是正确的答案!一切归结为:了解使用并行代码时的操作。将矩阵的副本发送给每个工作程序的速度很慢,并且它们是如此之大,可能甚至没有发送到本地工作程序缓存,它们都位于全局RAM中,因此访问它们的速度甚至更慢。 (2认同)