假设我有一个大矩阵A:
A = rand(10000,10000);
以下序列号大约花费了0.5秒
tic
for i=1:5
r=9999*rand(1);
disp(A(round(r)+1, round(r)+1))
end
toc
以下带有parfor的代码花费了大约47秒
tic
parfor i=1:5
r=9999*rand(1);
disp(A(round(r)+1, round(r)+1))
end
toc
如何加快parfor代码的速度?
编辑:如果我不使用disp
,而是尝试使用以下代码计算总和
sum=0;
tic
for i=1:5000
r=9999*rand(1);
sum=sum+(A(round(r)+1, round(r)+1));
end
toc
这需要.025秒
但是parfor
花费42.5秒:
tic
parfor i=1:5000
r=9999*rand(1);
sum=sum+(A(round(r)+1, round(r)+1));
end
toc
答案 0 :(得分:5)
您的问题是不考虑节点通信开销。
当您使用parfor
循环使用并行计算时,您必须考虑几个为客户端节点执行小任务的工作节点的结构。
以下是您提出的测试问题:
函数disp
是串行的,因为您一次只能向客户端节点显示一个结果。调度任务需要节点之间的通信。
在循环外部创建求和意味着所有节点都必须将当前值传递回客户端节点。
A
是broadcast variable。从文档中:
这种类型的变量对于特定任务可能非常有用,甚至必不可少。但是,较大的广播变量会导致客户端与工作人员之间的大量交流,并增加并行开销。
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
我做了一个快速基准测试,使用R2017b和12名工人将您的2个总和测试与上述代码进行比较,这是我的结果:
Serial loop: ~ 0.001 secs
Parallel with broadcasting: ~ 100 secs
Parallel no broadcasting: ~ 0.1 secs
此操作的并行循环过大,开销没有道理,但是很明显,通过一些预分配和避免广播变量,它们至少慢了5个数量级!
看看没有广播变量的代码版本如何也使用更多的向量化,这将加快代码的使用速度,而不必使用parfor
。在使用并行计算之前优化代码不仅可以加快串行计算的速度,而且通常也使转换更加容易!
旁注:sum
和i
是错误的变量名,因为它们是内置函数的名称。
答案 1 :(得分:0)
如果第一次循环需要0.5秒,则您的计算机速度非常慢,或者计时错误。
当您在命令行(或脚本)中键入代码时,解释器将在执行代码时对其进行解释,有时甚至在循环部分中也将解释该代码。
相反,如果您编写一个函数,则代码将在加载到内存后由JIT编译,并且每次都会快速执行。
如果将此循环放到函数中,您会发现计时显着改善。
现在,您将需要花费一秒钟的时间进行计时。使用tic,toc
测量的时间不是很精确。这种方法可以比较一分钟与三分钟,或者1秒与5秒的比较,但是在这种情况下,时间太短,可能会受到计算机上运行的其他各种因素的影响。请改用timeit
。它将多次运行该函数,并为您提供平均运行时间的良好估计。
答案 2 :(得分:-1)
所以有几个主要原因,
简单地说,“我如何加快这个parfor代码的速度?”
你不