主要内容

本页采用了机器翻译。点击此处可查看英文原文。

利用消息传递法估算圆周率

本示例展示了在 spmd 语句中使用消息传递的基本原理,这与《消息传递接口》(MPI) 规范中的集体操作类似。例如,在此示例中,您通过将一个积分的计算任务分配给多个工作单元来近似计算 π 的值,然后将各工作单元的计算结果合并,从而得到 π 的值。

可以通过数值求解以下积分来近似计算 π

0141+x2dx=4(atan(1)-atan(0))=π

您可以通过将区间 [0,1] 分配给多个工作单元,并行地求解这个积分。每位工作单元对其所分配的子区间进行积分计算。然后,您可以使用消息传递来合并这些结果。

使用本地“进程”配置文件启动一个包含四个工作单元的并行池。

p = parpool("Processes",4);
Starting parallel pool (parpool) using the 'Processes' profile ...
Connected to parallel pool with 4 workers.

定义积分函数

可以通过对 4/(1 + x^2)01 的数值积分来近似 π,该积分即为 4*atan(x) 的导数。定义待积分的函数。

function y = quadpi(x)
y = 4./(1 + x.^2);
end

将时间间隔分配给工作单元

为了在工作单元之间分配工作,每位工作单元都计算该函数在 [0,1] 的某个子区间上的积分,如图所示。

您可以利用每个工作单元的 spmdIndex 值和 spmdSize 的值,将区间 [0,1] 划分为连续且互不重叠的子区间,每个工作单元分配一个子区间。spmdIndex 是介于 1spmdSize 之间的一个唯一整数,该数值表示当前 spmd 模块中正在运行的工作单元数量。在并行池运行期间,每个工作单元的 spmdIndex 值保持不变。

针对每个工作单元,根据其 spmdIndex 值,定义子区间 a 的起始点和子区间 b 的结束点。显示每个子区间。spmd 语句中的代码将在并行池中的所有工作单元上并行执行。

spmd
    a = (spmdIndex-1)/spmdSize;
    b = spmdIndex/spmdSize;
    fprintf('Subinterval: [%-4g,%-4g]\n',a,b);
end
Worker 1: 
  Subinterval: [0   ,0.25]
Worker 2: 
  Subinterval: [0.25,0.5 ]
Worker 3: 
  Subinterval: [0.5 ,0.75]
Worker 4: 
  Subinterval: [0.75,1   ]

计算积分

使用辅助函数 quadpi 中定义的正交法,在工作单元上对子区间的积分进行近似。这些工作单元都执行相同的函数,但如上图所示,它们分别在 [0,1] 的不同子区间上运行。显示每位工作单元的积分。

spmd
    myIntegral = integral(@quadpi,a,b);
    fprintf('Subinterval: [%-4g, %-4g]   Integral: %4g\n', ...
        a,b,myIntegral);
end
Analyzing and transferring files to the workers ...done.
Worker 1: 
  Subinterval: [0   , 0.25]   Integral: 0.979915
Worker 2: 
  Subinterval: [0.25, 0.5 ]   Integral: 0.874676
Worker 3: 
  Subinterval: [0.5 , 0.75]   Integral: 0.719414
Worker 4: 
  Subinterval: [0.75, 1   ]   Integral: 0.567588

汇总结果

在各工作单元计算完其本地积分后,使用 spmdPlus 函数将所有工作单元的结果相加,并在每个工作单元上存储全局和的副本。该操作模拟了 MPI 中的归约步骤,其中每个进程将其本地结果贡献到全局和中。

spmd
    piApprox = spmdPlus(myIntegral);
end

在客户端查看结果

spmd 语句中为变量赋值后,可以在客户端将该变量作为复合对象进行访问。Composite 对象类似于元胞数组,每个工作单元对应一个元素。对 Composite 进行索引会将相应的值从工作单元带回给客户端。

变量 piApprox 是一个复合对象,每个工作单元都保存着全局和的一份副本。从第一个工作单元中获取该值,并将其与 MATLAB 中 π 的值进行比较。

approx1 = piApprox{1};   % 1st element holds value on worker 1.
fprintf('pi           : %.18f\n',pi);
pi           : 3.141592653589793116
fprintf('Approximation: %.18f\n',approx1);
Approximation: 3.141592653589793560
fprintf('Error        : %g\n',abs(pi - approx1))
Error        : 4.44089e-16

将计算结果用于后续计算

假设您想让每个工作单元计算一个圆的面积,该圆的半径等于其 spmdIndex。现在,所有工作单元都可以使用每个工作单元上可用的相同 piApprox 值。

spmd
    radius = spmdIndex;
    area = piApprox*radius^2;

    fprintf("radius = %d, area = %.4f\n",radius,area);
end
Worker 1: 
  radius = 1, area = 3.1416
Worker 2: 
  radius = 2, area = 12.5664
Worker 3: 
  radius = 3, area = 28.2743
Worker 4: 
  radius = 4, area = 50.2655

另请参阅

|

主题