改善parfor
性能
可以提高性能parfor
-循环的各种方式。这包括在循环中并行创建数组;分析parfor
循环;切片数组;并在集群上运行之前在本地worker上优化代码。
在哪里创建数组
当您在客户端中创建一个大数组时parfor
-loop,并在循环中访问它,你可能会观察到代码执行缓慢。为了提高性能,告诉每个MATLAB®Worker以并行方式创建自己的数组或数组的一部分。通过要求每个worker在循环中并行地创建这些数组的自己副本,可以节省将数据从客户机传输到worker的时间。考虑改变通常的初始化变量的做法为
-loop,避免循环内部不必要的重复。您可能会发现在循环中并行创建数组可以提高性能。
性能改进取决于不同的因素,包括
数组的大小
创建数组所需的时间
对数组的全部或部分的工作者访问
每个工作人员执行的循环迭代的数量
当您考虑转换时,请考虑以下所有因素为
循环,parfor
循环。详情请参见将for-Loops转换为parfor-Loops.
作为替代,考虑parallel.pool.Constant
函数在循环之前在池工作线程上建立变量。这些变量在循环结束后仍然保留在工人上,并且仍然可供多个工人使用parfor
循环。您可以使用parallel.pool.Constant
,因为数据只传输给工人一次。
在本例中,首先创建一个大数据集D
并执行parfor
循环访问D
.然后你用D
建立一个parallel.pool.Constant
对象,该对象允许您通过复制来重用数据D
给每个工人。使用度量经过的时间抽搐
而且toc
对于每种情况,并注意区别。
函数constantDemo D = rand(1e7, 1);抽搐为I = 1:20 a = 0;parforj = 1:60 a = a + sum(D);结束结束toc tic D = parallel.pool.Constant(D);为I = 1:20 b = 0;parforj = 1:60 b = b + sum(D.Value);结束结束toc结束
>> constantDemo使用'Processes'配置文件启动并行池(parpool)…连接4个工人。运行时间为63.839702秒。运行时间为10.194815秒。
parfor
-循环,使用parallel.pool.Constant
对象。
分析parfor
循环
您可以配置parfor
-loop通过测量使用的时间抽搐
而且toc
.您还可以通过使用,来测量并行池中的工作线程之间传输了多少数据ticBytes
而且tocBytes
.注意,这与通常意义上使用MATLAB分析器分析MATLAB代码不同,请参见分析代码以提高性能.
这个例子计算了一个矩阵的谱半径,并转换了一个为
-循环成parfor
循环。测量结果的加速和传输的数据量。
在MATLAB编辑器中,输入以下内容
为
循环。添加抽搐
而且toc
测量所消耗的时间。将文件另存为MyForLoop.m
.函数a = MyForLoop(a) tic为i = 1:20 00 a(i) = max(abs(eig(rand(a))));结束toc结束
运行代码,并记录所消耗的时间。
a = MyForLoop(500);
运行时间为31.935373秒。
在
MyForLoop.m
,替换为
带a的-循环parfor
循环。添加ticBytes
而且tocBytes
度量并行池中的工作线程之间传输的数据量。将文件另存为MyParforLoop.m
.ticBytes (gcp);parfori = 1:20 00 a(i) = max(abs(eig(rand(a))));结束tocBytes (gcp)
运行新代码,然后再运行一次。请注意,第一次运行比第二次运行慢,因为必须启动并行池,并且必须使代码对工作线程可用。注意第二次运行所花费的时间。
默认情况下,MATLAB会自动在本地机器上打开并行工作池。
a = MyParforLoop(500);
使用'Processes'配置文件启动并行池(parpool)…连接到4个工人. ...BytesSentToWorkers BytesReceivedFromWorkers __________________ ________________________ 1 15340 7024 2 13328 5712 3 13328 5704 4 13328 5728 Total 55324 24168运行时间为10.760068秒。
parfor
循环。
切片数组
变量初始化在parfor
-loop,然后在parfor
-loop,它必须传递给每个评估循环迭代的MATLAB worker。只有在循环中使用的变量才会从客户端工作区传递。但是,如果该变量的所有出现都被循环变量索引,那么每个worker只接收到它需要的部分数组。
例如,首先运行一个parfor
-loop使用切片变量并测量所消耗的时间。
%切片版本M = 100;N = 1e6;data = rand(M, N);抽搐parforidx = 1:M out2(idx) = sum(data(idx,:)) ./ N;结束toc
运行时间为2.261504秒。
现在假设您意外地使用了对变量的引用数据
而不是N
在parfor
循环。这里的问题是调用大小(数据,2)
将切片变量转换为广播(非切片)变量。
%意外未切片版本clear M = 100;N = 1e6;data = rand(M, N);抽搐parforidx = 1:M out2(idx) = sum(data(idx,:)) ./ size(data, 2);结束toc
运行时间为8.369071秒。
在这种情况下,可以轻松避免使用数据
,因为结果是一个常数,并且可以在循环之外计算。通常,您可以在循环开始之前执行仅依赖于广播数据的计算,因为广播数据不能在循环内部修改。在这种情况下,计算是微不足道的,结果是一个标量结果,因此您可以从循环中取出计算。
优化本地工人和集群工人
在本地worker上运行代码可以提供测试应用程序的便利,而不需要使用集群资源。然而,使用当地工人有一定的缺点或限制。因为数据的传输不在网络上发生,所以本地工作者上的传输行为可能不能指示它通常如何在网络上发生。
对于本地工作者,因为所有的MATLAB工作者会话都运行在同一台机器上,您可能不会看到任何性能改进parfor
关于执行时间的-loop。这取决于很多因素,包括你的机器有多少个处理器和核心。这里的关键点是集群的可用内核可能比本地计算机多。如果您的代码可以通过MATLAB实现多线程,那么加快速度的唯一方法就是使用更多的核来处理问题,使用集群。
您可以试验一下,在循环之前创建数组(如下面的左图所示),是否比让每个worker在循环内部创建自己的数组(如下面的右图所示)更快。
尝试以下示例在本地运行并行池,并注意每个循环执行时间上的差异。首先打开一个本地并行池:
parpool (“过程”)
运行以下示例,并再次执行。请注意,每种情况的第一次运行都比第二次运行慢,因为必须启动并行池,并且必须使代码对工作人员可用。注意对于每个情况,第二次运行所消耗的时间。
抽搐;N = 200;M =魔术(n);R = rand(n);parfor1:n A(i) = sum(M(i,:).*R(n+1-i,:));结束toc |
抽搐;N = 200;parfori = 1:n M =魔术(n);R = rand(n);A(i) = sum(M(i,:).*R(n+1-i,:));结束toc |
在远程集群上运行时,您可能会发现不同的行为,因为工作人员可以同时创建它们的数组,从而节省传输时间。因此,针对本地工作者优化的代码可能不会针对集群工作者进行优化,反之亦然。