在 spmd、parfor 和 parfeval 之间选择
要并行运行计算,可以使用 spmd、parfor、parfeval 或 parfevalOnAll 函数。每个函数都依赖于不同的并行编程概念。
何时使用 parfor
如果您有一个具有慢速循环迭代或许多循环迭代的 parfor 循环,则 for 循环会很有用。每次迭代必须独立于所有其他迭代。有关何时使用 parfor 的更多帮助,请参阅 决定何时使用 parfor。
何时使用 spmd
运行通信并行代码
如果在计算过程中需要细粒度的工作单元到工作单元通信和工作单元之间的协作,请使用 spmd。parfor、parfeval 和 parfevalOnAll 不允许工作单元之间的通信。使用 spmd 的计算可以涉及使用 spmdSend、spmdReceive 和 spmdSendReceive 函数的工作单元之间的通信。
如果您不确定,请问自己以下问题:在我的并行代码中,每个计算是否可以在没有任何工作单元之间通信的情况下完成?如果是,请使用 parfor 或 parfeval。否则,使用 spmd。
在分布式数组上运行并行和自定义代码
如果您的计算涉及分布在各个工作单元上的大型数组,请使用 spmd。您可以对所有工作单元执行同时计算,也可以对特定工作单元执行自定义计算。
当工作单元运行 spmd 代码块时,每个工作单元被分配一个唯一的索引,即 spmdIndex。这使您可以指定仅在某些工作单元和分布式数组的目标部分上运行的代码。
同步和异步工作
在 parfor、parfeval 和 spmd 之间进行选择时,请考虑您的计算是否需要与客户端同步。
parfor 和 spmd 需要同步,因此阻止您在 MATLAB® 客户端上运行任何新的计算。parfeval 不需要同步,因此您可以继续使用客户端。
使用其他功能
spmd 和 parfeval 具有其他功能,您可以在提交计算后使用。
使用
spmd,您可以收集spmd语句内部计算的结果,而无需将结果传输到客户端。从客户端以Composite对象的形式访问spmd语句内分配的变量的值。有关详细信息,请参阅使用 Composite 访问工作单元变量。当您提交一个
parfeval任务时,MATLAB 会将该任务安排为异步运行,并在提交的任务运行完成之前返回Future对象。Future对象代表 MATLAB 调度的任务。您可以通过不同的方式与Future对象进行交互:
比较 parfor、parfeval 和 spmd 的性能
使用 spmd 可能比使用 parfor 循环或 parfeval 更慢或更快,具体取决于计算的类型。开销会影响 parfor 循环、parfeval 和 spmd 的相对性能。
对于一组任务,在以下条件下,parfor 和 parfeval 通常比 spmd 表现更好:
每个任务所花费的计算时间是不确定的。
每个任务所花费的计算时间并不一致。
每个任务返回的数据很少。
在以下情况下使用 parfeval:
您想在后台运行计算。
每个任务都依赖于其他任务。
在此示例中,您将检查使用 parfor 循环、parfeval 和 spmd 时软件执行矩阵运算的速度。
首先,创建一个进程工作单元的并行池 p。
p = parpool("Processes");Starting parallel pool (parpool) using the 'Processes' profile ... Connected to parallel pool with 6 workers.
计算随机矩阵
检查软件使用 parfor 循环、parfeval 和 spmd 生成随机矩阵的速度。设置试验次数 (n) 和矩阵大小(对于 m×m 矩阵)。增加试验次数可以改善后续分析中使用的统计数据,但不会影响计算本身。
m =1500; n =
20;
然后,使用 parfor 循环为每个工作单元执行一次 rand(m)。对每次 n 试验进行计时。
parforTime = zeros(n,1); for i = 1:n tic; mats = cell(1,p.NumWorkers); parfor N = 1:p.NumWorkers mats{N} = rand(m); end parforTime(i) = toc; end
接下来,使用 parfeval 为每个工作单元执行一次 rand(m)。对每次 n 试验进行计时。
parfevalTime = zeros(n,1); for i = 1:n tic; f(1:p.NumWorkers) = parallel.FevalFuture; for N = 1:p.NumWorkers f(N) = parfeval(@rand,1,m); end mats = fetchOutputs(f); parfevalTime(i) = toc; clear f end
最后,使用 spmd 为每个工作单元执行一次 rand(m)。您可以使用 spmdCat 将每个工作单元上的 mat 的值连接成数组 mats 并将其存储在工作单元 1 上。有关工作单元的详细信息以及如何使用 spmd 对它们执行命令,请参阅 在多个数据集上运行单个程序。对每次 n 试验进行计时。
spmdTime = zeros(n,1); for i = 1:n tic; spmd mat = rand(m); mats = spmdCat({mat}, 1, 1); end allMats = mats{1}; spmdTime(i) = toc; end
使用 rmoutliers 从每次试验中删除异常值。然后,使用 boxplot 来比较时间。
% Hide outliers boxData = rmoutliers([parforTime parfevalTime spmdTime]); % Plot data boxplot(boxData, 'labels',{'parfor','parfeval','spmd'}, 'Symbol','') ylabel('Time (seconds)') title('Make n Random Matrices (m-by-m)')

通常,spmd 每次评估所需的开销比 parfor 或 parfeval 要大。因此,在这种情况下,使用 parfor 循环或 parfeval 更有效。
计算随机矩阵的和
接下来,计算随机矩阵的和。您可以通过使用带有 parfor 循环的归约变量、带有 parfeval 的计算后的总和或带有 spmdPlus 的 spmd 来实现此目的。再次设置试验次数 (n) 和矩阵大小(对于 m×m 矩阵)。
m =1500; n =
20;
然后,使用 parfor 循环为每个工作单元执行一次 rand(m)。使用归约变量来计算总和。对每次 n 试验进行计时。
parforTime = zeros(n,1); for i = 1:n tic; result = 0; parfor N = 1:p.NumWorkers result = result + rand(m); end parforTime(i) = toc; end
接下来,使用 parfeval 为每个工作单元执行一次 rand(m)。使用 fetchOutputs 获取所有矩阵,然后使用 sum。对每次 n 试验进行计时。
parfevalTime = zeros(n,1); for i = 1:n tic; f(1:p.NumWorkers) = parallel.FevalFuture; for N = 1:p.NumWorkers f(N) = parfeval(@rand,1,m); end result = sum(fetchOutputs(f)); parfevalTime(i) = toc; clear f end
最后,使用 spmd 为每个工作单元执行一次 rand(m)。使用 spmdPlus 对所有矩阵求和。要将结果仅发送给第一个工作单元,请将可选的目标工作单元参量设置为 1。对每次 n 试验进行计时。
spmdTime = zeros(n,1); for i = 1:n tic; spmd r = spmdPlus(rand(m), 1); end result = r{1}; spmdTime(i) = toc; end
使用 rmoutliers 从每次试验中删除异常值。然后,使用 boxplot 来比较时间。
% Hide outliers boxData = rmoutliers([parforTime parfevalTime spmdTime]); % Plot data boxplot(boxData, 'labels',{'parfor','parfeval','spmd'}, 'Symbol','') ylabel('Time (seconds)') title('Sum of n Random Matrices (m-by-m)')

对于这个计算,spmd 比 parfor 循环或 parfeval 更快。当在 parfor 循环中使用归约变量时,每个工作单元都会执行本地缩减,然后将其部分结果发送回客户端以计算最终结果。
相比之下,spmd 仅调用一次 spmdPlus 来执行全局缩减操作,需要的开销较少。因此,计算减少部分的开销对于 spmd 来说是 ,对于 parfor 来说是 。



