主要内容

改善parfor性能

你可以改进的性能parfor-以各种方式循环。这包括在循环中并行创建数组;分析parfor循环;切片数组;以及在集群上运行之前在本地工作人员上优化代码。

创建数组的位置

当您在您的客户端之前创建一个大数组parfor-loop,并在循环中访问它,您可能会观察到代码执行缓慢。为了提高性能,告诉每个MATLAB®Worker并行地创建自己的数组或其中的一部分。通过要求每个工作人员在循环中并行地创建自己的这些数组的副本,可以节省从客户机向工作人员传输数据的时间。考虑改变你在a-loop,避免循环内部不必要的重复。您可能会发现,在循环中并行创建数组可以提高性能。

性能改进取决于不同的因素,包括

  • 数组的大小

  • 创建数组所需的时间

  • 工作人员访问全部或部分数组

  • 每个工作人员执行的循环迭代次数

当您考虑转换时,请考虑此列表中的所有因素循环,parfor循环。有关详细信息,请参见转换for-Loops为parfor-Loops

作为另一种选择,考虑parallel.pool.Constant函数在循环之前在池工作程序上建立变量。在循环结束后,这些变量仍然在worker上,并且对于多个变量都是可用的parfor循环。您可以使用它来提高性能parallel.pool.Constant,因为数据只传输一次给工人。

在本例中,首先创建一个大数据集D和执行parfor循环访问D.然后使用D建立一个parallel.pool.Constant对象,该对象允许您通过复制来重用数据D每个工人。使用抽搐toc每一种情况,并注意差异。

函数常量D = rand(1e7, 1);抽搐I = 1:20 a = 0;parfora = a + sum(D);结束结束toc tic D = parallel.pool.Constant(D);I = 1:20 b = 0;parforj = 1:20 0 b = b + sum(D.Value);结束结束toc结束
使用“local”配置文件启动并行池(parpool)…连接到4个工人。运行时间为63.839702秒。运行时间为10.194815秒。
在第二种情况下,只发送一次数据。可以提高性能parfor使用parallel.pool.Constant对象。

分析parfor循环

你可以对parfor-loop通过测量使用时所经过的时间抽搐toc.您还可以通过使用ticBytestocBytes.注意,这与使用MATLAB分析器分析MATLAB代码的通常意义不同,参见分析代码以提高性能

这个例子计算一个矩阵的光谱半径,并转换循环到一个parfor循环。测量由此产生的加速和传输的数据量。

  1. 在MATLAB编辑器中,输入以下内容循环。添加抽搐toc来测量经过的时间。将文件保存为MyForLoop.m

    函数a = MyForLoop(ai = 1:20 00 a(i) = max(abs(eig(rand(a))));结束toc结束
  2. 运行代码,并注意所消耗的时间。

    一个= MyForLoop (500);
    运行时间为31.935373秒。

  3. MyForLoop.m,替换循环用parfor循环。添加ticBytestocBytes测量有多少数据被传输到并行池中的工作人员或从工作人员那里。将文件保存为MyParforLoop.m

    ticBytes (gcp);parfori = 1:20 00 a(i) = max(abs(eig(rand(a))));结束tocBytes (gcp)

  4. 运行新代码,并再次运行它。注意,第一次运行比第二次运行慢,因为必须启动并行池,并且必须使代码对工作人员可用。注意第二次运行所消耗的时间。

    默认情况下,MATLAB会自动打开本地机器上的并行工作池。

    一个= MyParforLoop (500);
    使用“local”配置文件启动并行池(parpool)…连接到4个工人. ...bytesenttoworkers BytesReceivedFromWorkers __________________ ________________________ 1 15340 7024 2 13328 5712 3 13328 5704 4 13328 5728 Total 55324 24168 Elapsed time is 10.760068 seconds。
    串行运行时间为31.9秒,并行运行时间为10.8秒,这表明该代码从转换为parfor循环。

切片数组

如果变量在aparfor-循环,然后在parfor-loop,它必须被传递给每个MATLAB工作人员评估循环迭代。只有那些在循环中使用的变量才从客户端工作空间传递。但是,如果该变量的所有出现都由循环变量索引,则每个worker只接收到它需要的数组的一部分。

例如,首先运行parfor-loop使用一个切片的变量并测量经过的时间。

%切片版本M = 100;N = 1 e6;数据= rand(M, N);抽搐parforidx = 1:M out2(idx) = sum(data(idx,:)) ./ N;结束toc
运行时间为2.261504秒。

现在假设您不小心使用了对变量的引用数据而不是Nparfor循环。这里的问题是大小(数据,2)将切片的变量转换为广播(非切片)变量。

%意外未切片版本clear M = 100;N = 1 e6;数据= rand(M, N);抽搐parforidx = 1:M out2(idx) = sum(data(idx,:)) ./ size(data (idx,:));结束toc
运行时间为8.369071秒。
注意,对于意外广播变量,经过的时间要大一些。

在这种情况下,您可以很容易地避免不切片地使用数据,因为结果是一个常量,可以在循环外部计算。通常,在循环开始之前,您可以执行仅依赖于广播数据的计算,因为广播数据不能在循环内部修改。在本例中,计算很简单,结果是标量结果,因此将计算从循环中取出对您有利。

优化本地工人和集群工人

在本地工作人员上运行代码可以方便地测试应用程序,而不需要使用集群资源。然而,使用当地工人也有一定的缺点或限制。因为数据的传输不会在网络上发生,所以本地工作人员的传输行为可能不能说明它通常如何在网络上发生。

对于本地工作人员,因为所有的MATLAB工作人员会话都运行在同一台机器上,您可能看不到任何性能改进parfor关于执行时间的循环。这可能取决于许多因素,包括您的机器有多少处理器和核心。这里的关键是,集群可能拥有比本地机器更多的可用内核。如果您的代码可以通过MATLAB实现多线程,那么提高速度的唯一方法就是使用更多的内核来解决问题,使用集群。

您可以进行实验,看看是否在循环之前创建数组更快(如下图所示),而不是让每个工作人员在循环中创建自己的数组(如图所示)。

尝试下面的示例在本地运行并行池,注意每个循环执行时间的差异。首先打开一个本地并行池:

parpool (“本地”

运行以下示例,并再次执行。注意,每种情况的第一次运行都比第二次运行慢,因为必须启动并行池,并且必须使代码对工作人员可用。请注意每种情况下第二次运行所消耗的时间。

抽搐;n = 200;M =魔法(n);R =兰德(n);parfori = 1:n A(i) = sum(M(i,:).*R(n+1-i,:));结束toc
抽搐;n = 200;parfori = 1:n M = magic(n);R =兰德(n);一笔(我)= (M(我:)。* R (n +我,:));结束toc

在远程集群上运行时,您可能会发现不同的行为,因为工作人员可以同时创建它们的数组,从而节省传输时间。因此,为本地工作器优化的代码可能不会为集群工作器优化,反之亦然。

另请参阅

相关的话题