决定何时使用 parfor
MATLAB 中的 parfor 循环
MATLAB® 中的 parfor
循环并行执行循环体中的一系列语句。MATLAB 客户端发出 parfor
命令,并与 MATLAB 工作进程协调,在并行池中的工作进程上并行执行循环迭代。客户端将 parfor
操作所需的数据发送给工作进程,大部分计算都在工作进程上执行。将结果发送回客户端并进行组合。
parfor
循环可以提供比其类似的 for
循环更好的性能,因为多个 MATLAB 工作进程可以在同一个循环上同时计算。
parfor
循环体的每次执行都是一次迭代。MATLAB 工作进程以无特定顺序且彼此独立的方式评估迭代。因为每次迭代都是独立的,所以无法保证迭代以任何方式同步,也没有必要这样做。如果工作进程数量等于循环迭代次数,则每个工作进程执行一次循环迭代。如果迭代次数多于工作进程,则一些工作进程会执行多次循环迭代;在这种情况下,工作进程可能会一次接收多次迭代以减少通信时间。
决定何时使用 parfor
如果 for
循环较慢,则 parfor
循环会很有用。如果您有以下情况,请考虑使用 parfor
:
一些循环迭代需要很长时间才能执行。在这种情况下,工作进程可以同时执行长迭代。确保迭代次数超过工作进程数量。否则,您将不会使用所有可用的工作进程。
简单计算的许多循环迭代,例如蒙特卡洛仿真或参数扫描。
parfor
将循环迭代分成几组,以便每个工作进程执行总迭代次数的一部分。多个 GPU 和您的计算使用支持 GPU 的功能。有关在
parfor
循环中使用多个 GPU 的更多信息,请参阅 在多个 GPU 上运行 MATLAB 函数。
如果有以下情况,parfor
循环可能没有用:
已将
for
循环向量化的代码。一般来说,如果您想让代码运行得更快,首先尝试对其进行向量化。有关如何执行此操作的详细信息,请参阅 向量化。向量化代码使您能够受益于许多底层 MATLAB 库的多线程特性所提供的内置并行性。然而,如果您有向量化代码,并且只能访问本地工作进程,那么parfor
-loops 的运行速度可能比for
-loops 慢。不要对代码进行去向量化以允许parfor
;一般来说,这种解决方案效果不佳。执行时间较短的循环迭代。在这种情况下,并行开销主导着您的计算。
所有循环迭代均使用同一 GPU。GPU 包含许多可以并行执行计算的微处理器,并且尝试使用
parfor
循环进一步并行化 GPU 计算不太可能加快您的代码速度。
当循环中的迭代依赖于其他迭代的结果时,您不能使用 parfor
循环。每次迭代必须独立于所有其他迭代。有关处理独立循环的帮助,请参阅 确保 parfor 循环迭代是独立的。此规则的例外是使用 归约变量 在循环中累积值。
在决定何时使用 parfor
时,请考虑并行开销。并行开销包括从客户端工作进程和返回的通信、协调和数据传输(发送和接收数据)所需的时间。如果迭代评估很快,那么这个开销可能会占到总时间的很大一部分。考虑两种不同类型的循环迭代:
for
-循环具有计算要求高的任务。这些循环通常适合转换为parfor
循环,因为计算所需的时间决定了数据传输所需的时间。for
-循环执行简单的计算任务。这些循环通常不会从转换为parfor
循环中受益,因为与计算所需的时间相比,数据传输所需的时间很长。
具有低并行开销的 parfor
示例
在此示例中,您从 for
循环内的计算要求高的任务开始。for
循环很慢,您可以使用 parfor
循环来加快计算速度。parfor
将 for
循环迭代的执行拆分到并行池中的工作进程上。
此示例计算矩阵的谱半径,并将 for
循环转换为 parfor
循环。了解如何测量最终的加速以及在并行池中的工作进程之间传输了多少数据。
在 MATLAB 编辑器中,输入以下
for
循环。添加tic
和toc
来测量计算时间。tic n = 200; A = 500; a = zeros(n); for i = 1:n a(i) = max(abs(eig(rand(A)))); end toc
运行脚本并记录经过的时间。
Elapsed time is 31.935373 seconds.
在脚本中,将
for
-loop 替换为parfor
-loop。添加ticBytes
和tocBytes
来测量在并行池中从工作进程传输的数据量。tic ticBytes(gcp); n = 200; A = 500; a = zeros(n); parfor i = 1:n a(i) = max(abs(eig(rand(A)))); end tocBytes(gcp) toc
在四个工作进程上运行新脚本,然后再次运行它。请注意,第一次运行比第二次运行慢,因为并行池需要一些时间来启动并使代码可供工作进程使用。注意第二次运行的数据传输和经过的时间。
默认情况下,MATLAB 会自动在本地机器上开启一个并行工作进程池。
在四个工作进程上运行的Starting parallel pool (parpool) using the 'Processes' profile ... connected to 4 workers. ... BytesSentToWorkers BytesReceivedFromWorkers __________________ ________________________ 1 15340 7024 2 13328 5712 3 13328 5704 4 13328 5728 Total 55324 24168 Elapsed time is 10.760068 seconds.
parfor
比相应的for
循环计算快大约三倍。该加速比对四个工作进程进行四倍加速的理想值要小。这是由于并行开销,包括将数据从客户端传输到工作进程并返回所需的时间。使用ticBytes
和tocBytes
结果来检查传输的数据量。假设数据传输所需的时间与数据的大小成正比。通过这种近似值,您可以了解数据传输所需的时间,并将您的并行开销与其他parfor
循环迭代进行比较。在此示例中,与下一个示例相比,数据传输和并行开销较小。
当前示例具有较低的并行开销,并可从转换为 parfor
循环中获益。将此示例与下一个示例中的简单循环迭代进行比较,参见 具有高并行开销的 parfor 示例。
有关具有计算要求高的任务的 parfor
循环的另一个示例,请参阅 嵌套 parfor 和 for 循环以及其他 parfor 要求
具有高并行开销的 parfor
示例
在此示例中,您编写了一个循环来创建一个简单的正弦波。将 for
循环替换为 parfor
循环不会加快计算速度。这个循环没有很多迭代,执行时间也不长,您也不会注意到执行速度的增加。此示例具有较高的并行开销,并且不会从转换为 parfor
循环中受益。
编写一个循环来创建正弦波。使用
tic
和toc
来测量经过的时间。tic n = 1024; A = zeros(n); for i = 1:n A(i,:) = (1:n) .* sin(i*2*pi/1024); end toc
Elapsed time is 0.012501 seconds.
用
parfor
循环替换for
循环。添加ticBytes
和tocBytes
来测量在并行池中从工作进程传输的数据量。tic ticBytes(gcp); n = 1024; A = zeros(n); parfor (i = 1:n) A(i,:) = (1:n) .* sin(i*2*pi/1024); end tocBytes(gcp) toc
在四个工作进程上运行脚本并再次运行代码。请注意,第一次运行比第二次运行慢,因为并行池需要一些时间来启动并使代码可供工作进程使用。注意第二次运行的数据传输和经过的时间。
请注意,四个工作进程上的串行BytesSentToWorkers BytesReceivedFromWorkers __________________ ________________________ 1 13176 2.0615e+06 2 15188 2.0874e+06 3 13176 2.4056e+06 4 13176 1.8567e+06 Total 54716 8.4112e+06 Elapsed time is 0.743855 seconds.
for
循环所用的时间比parfor
循环所用的时间小得多。在这种情况下,将for
循环转变为parfor
循环对您没有任何好处。原因是数据传输比前面的示例大得多,参见 具有低并行开销的 parfor 示例。在当前示例中,并行开销决定了计算时间。因此,正弦波迭代不会从转换为parfor
循环中受益。
这个例子说明了为什么高并行开销计算不会从转换为 parfor
循环中受益。要了解有关加速代码的更多信息,请参阅 将 for-循环转换为 parfor-循环