利用消息传递法估算圆周率
本示例展示了在 spmd 语句中使用消息传递的基本原理,这与《消息传递接口》(MPI) 规范中的集体操作类似。例如,在此示例中,您通过将一个积分的计算任务分配给多个工作单元来近似计算 的值,然后将各工作单元的计算结果合并,从而得到 的值。
可以通过数值求解以下积分来近似计算 :
您可以通过将区间 [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) 从 0 到 1 的数值积分来近似 ,该积分即为 4*atan(x) 的导数。定义待积分的函数。
function y = quadpi(x) y = 4./(1 + x.^2); end
将时间间隔分配给工作单元
为了在工作单元之间分配工作,每位工作单元都计算该函数在 [0,1] 的某个子区间上的积分,如图所示。

您可以利用每个工作单元的 spmdIndex 值和 spmdSize 的值,将区间 [0,1] 划分为连续且互不重叠的子区间,每个工作单元分配一个子区间。spmdIndex 是介于 1 和 spmdSize 之间的一个唯一整数,该数值表示当前 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