使用 spmdReduce
实现 MPI_Allreduce 功能
在这个示例中,我们研究 spmdReduce
函数及其构建的函数:spmdPlus
和 spmdCat
。这些看似简单的函数实际上是并行编程中非常强大的工具。
spmdReduce
函数允许我们对所有工作单元上定义的变量执行任何关联二元运算。这样,我们不仅可以对所有工作单元的变量求和,还可以在所有工作单元中找到它的最小值和最大值,将它们连接起来,并执行许多其他有用的操作。
本示例中显示的代码可以在以下函数中找到:
function paralleltutorial_gop
简介
在进行并行编程时,我们经常会遇到在所有工作单元上定义一个变量的情况,并且我们想要对所有工作单元上存在的变量执行操作。例如,如果我们输入 spmd 语句并定义
spmd x = spmdIndex; end
对于所有工作单元,我们可能想要计算所有工作单元的 x
值的总和。这正是 spmdPlus
操作所做的,它对各个工作单元 的 x
求和,并将结果复制到所有工作单元上:
spmd s = spmdPlus(x); end
Spmd 语句内部分配的变量在客户端上表示为 Composite。我们可以通过对 Composite 进行索引,将工作单元得到的结果值传送给客户端,就像对元胞数组进行索引一样:
s{1} % Display the value of s on worker 1. All workers store the same value.
ans = 21
此外,spmdReduce
、spmdPlus
和 spmdCat
允许我们指定应返回函数输出的单个工作单元,并且它们对其他工作单元返回一个空向量。
spmd s = spmdPlus(x, 1); end s{1}
ans = 21
此示例展示了如何在所有工作单元中执行主机类似于加法的操作。在 MPI 中,这些被称为集体操作,例如 MPI_SUM、MPI_PROD、MPI_MIN、MPI_MAX 等。
为我们的示例创建输入数据
我们在所有示例中使用的数据都非常简单:一个 1×2 的变体数组,它只比我们一开始定义的 x
稍微复杂一点:
spmd x = spmdIndex + (1:2) end
使用 spmdPlus
和 spmdCat
现在,我们已将向量 x
初始化为各个工作单元上的不同值,我们可以提出一些问题,例如各个工作单元进程上的 x
的每个元素的值之和是多少?那么乘积、最小值和最大值又如何呢?正如我们介绍的那样,
spmd s = spmdPlus(x); end s{1}
ans = 27 33
返回 x
的值的逐元素的加法。然而,spmdPlus
只是 spmdReduce
运算的一个特例。spmdReduce
函数允许我们对变体数组的元素执行跨工作单元的任何关联操作。结合运算的最基本示例是加法;它具有结合性,因为加法与所使用的分组无关:
(a + b) + c = a + (b + c)
在 MATLAB® 中,加法可以用 @plus
函数句柄表示,因此我们也可以将 spmdPlus(x)
写为
spmd s = spmdReduce(@plus, x); end s{1}
ans = 27 33
我们可以使用 x
函数在各个工作单元之间连接向量 spmdCat
,并且可以选择要连接的维度。
spmd y1 = spmdCat(x, 1); % Concatenate along rows. y2 = spmdCat(x, 2); % Concatenate along columns. end y1{1} y2{1}
ans = 2 3 3 4 4 5 5 6 6 7 7 8 ans = 2 3 3 4 4 5 5 6 6 7 7 8
spmdReduce
的其他基本用途
计算各个工作单元的 x
值的逐元素乘积很简单:
spmd p = spmdReduce(@times, x); end p{1}
ans = 5040 20160
我们还可以找到所有工作单元中 x
的逐元素最大值:
spmd M = spmdReduce(@max, x); m = spmdReduce(@min, x); end M{1} m{1}
ans = 7 8 ans = 2 3
逻辑运算
MATLAB 有更多内置的结合运算。逻辑 AND、OR 和 XOR 运算由 @and
、@or
和 @xor
函数句柄表示。例如,查看逻辑数组
spmd y = (x > 4) end
然后我们可以轻松地在各个工作单元上对 y
的元素执行以下逻辑运算:
spmd yand = spmdReduce(@and, y); yor = spmdReduce(@or, y); yxor = spmdReduce(@xor, y); end yand{1} yor{1} yxor{1}
ans = 1×2 logical array 0 0 ans = 1×2 logical array 1 1 ans = 1×2 logical array 1 0
按位运算
为了结束我们对 MATLAB 内置的结合运算的介绍,我们来看一下按位 AND、OR 和 XOR 运算。这些由 @bitand
、@bitor
和 @bitxor
函数句柄表示。
spmd xbitand = spmdReduce(@bitand, x); xbitor = spmdReduce(@bitor, x); xbitxor = spmdReduce(@bitxor, x); end xbitand{1} xbitor{1} xbitxor{1}
ans = 0 0 ans = 7 15 ans = 1 11
查找最小值和最大值的位置
我们只需进行一点点编程就可以找到与各个工作单元中逐元素的 spmdIndex
最大值相对应的 x
。我们只需几行代码就可以做到这一点:
type pctdemo_aux_gop_maxloc
function [val, loc] = pctdemo_aux_gop_maxloc(inval) %PCTDEMO_AUX_GOP_MAXLOC Find maximum value of a variant and its spmdIndex. % [val, loc] = pctdemo_aux_gop_maxloc(inval) returns to val the maximum value % of inval across all workers. The spmdIndex where this maximum value % resides is returned to loc. % Copyright 2007 The MathWorks, Inc. out = spmdReduce(@iMaxLoc, {inval, spmdIndex*ones(size(inval))}); val = out{1}; loc = out{2}; end function out = iMaxLoc(in1, in2) % Calculate the max values and their locations. Return them as a cell array. in1Largest = (in1{1} >= in2{1}); maxVal = in1{1}; maxVal(~in1Largest) = in2{1}(~in1Largest); maxLoc = in1{2}; maxLoc(~in1Largest) = in2{2}(~in1Largest); out = {maxVal, maxLoc}; end
当该函数被实现后,它可以像任何内置操作一样轻松地应用:
spmd [maxval, maxloc] = pctdemo_aux_gop_maxloc(x); end [maxval{1}, maxloc{1}]
ans = 7 8 6 6
类似地,我们只需要几行代码就可以找到 spmdIndex
,其中 x
在所有工作单元中逐元素最小值出现的位置:
type pctdemo_aux_gop_minloc
function [val, loc] = pctdemo_aux_gop_minloc(inval) %PCTDEMO_AUX_GOP_MINLOC Find minimum value of a variant and its spmdIndex. % [val, loc] = pctdemo_aux_gop_minloc(inval) returns to val the minimum value % of inval across all workers. The spmdIndex where this minimum value % resides is returned to loc. % Copyright 2007 The MathWorks, Inc. out = spmdReduce(@iMinLoc, {inval, spmdIndex*ones(size(inval))}); val = out{1}; loc = out{2}; end function out = iMinLoc(in1, in2) % Calculate the min values and their locations. Return them as a cell array. in1Smallest = (in1{1} < in2{1}); minVal = in1{1}; minVal(~in1Smallest) = in2{1}(~in1Smallest); minLoc = in1{2}; minLoc(~in1Smallest) = in2{2}(~in1Smallest); out = {minVal, minLoc}; end
然后我们可以轻松地用 spmdReduce
找到最小值:
spmd [minval, minloc] = pctdemo_aux_gop_minloc(x); end [minval{1}, minloc{1}]
ans = 2 3 1 1
另请参阅
spmd
| spmdReduce
| spmdPlus
| spmdCat