主要内容

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

使用 spmdReduce 来实现 MPI_Allreduce 的函数

本示例演示了如何在并行池中的所有工作单元上执行聚合操作,例如求和、求积、求最小值或求最大值。在消息传递接口 (MPI) 规范中,这些被称为集体操作,例如 MPI_SUMMPI_PRODMPI_MINMPI_MAX。在 MATLAB ® 中,您可以使用 spmdPlusspmdCatspmdReduce 函数实现类似的功能。

生成关于工作单元的数据

启动一个包含四个工作单元的并行池。

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

在执行数据并行或单程序多数据 (SPMD) 计算时,通常所有工作单元上都会有一个结果变量,此时需要执行一项操作来合并这些值。

例如,在 spmd 模块内,为每个工作单元的 x 赋值。该代码在每个工作单元上创建了一个 1×2 的变体数组,由于 spmdIndex 函数返回的工作单元索引不同,每个工作单元上的数组略有差异。

spmd
    x = (spmdIndex*(1:2)).^2 + 3
end
Worker 1: 
  
  x =
  
       4     7
  
Worker 2: 
  
  x =
  
       7    19
  
Worker 3: 
  
  x =
  
      12    39
  
Worker 4: 
  
  x =
  
      19    67
  

执行简单的求和简化

您可以使用 spmdPlus 函数计算 x 在所有工作单元上的逐元素加法,并将结果复制到每个工作单元上。

spmd
    s = spmdPlus(x)
end
Worker 1: 
  
  s =
  
      42   132
  
Worker 2: 
  
  s =
  
      42   132
  
Worker 3: 
  
  s =
  
      42   132
  
Worker 4: 
  
  s =
  
      42   132
  

spmd 模块内赋值的变量,在客户端上表现为 Composite 对象。您可以通过对 Composite 进行索引来获取特定工作单元的结果,这与元胞数组索引类似。

将工作单元 1 上的 s 的值传回客户端。

sClient = s{1}
sClient = 1×2

    42    132

您还可以通过指定工作单元索引,使用 spmdPlus 将结果返回给单个工作单元。其他工作单元则收到一个空向量。使用 spmdPlus 函数计算所有工作单元中 x 的逐元素加法,并将结果仅存储在工作单元 1 中。

spmd
    s = spmdPlus(x,1);
end

在客户端显示 Composite 对象 s。只有工作单元 1 上的变量包含两个元素。其他工作单元的变量为空。

s
 
s =
 
   Worker 1: class = double, size = [1  2]
   Worker 2: class = double, size = [0  0]
   Worker 3: class = double, size = [0  0]
   Worker 4: class = double, size = [0  0]
 

拼接结果数据

如果结果变量较小,您可以使用 spmdCat 函数将各工作单元上的结果变量进行拼接,并可选择沿哪个维度进行拼接。

spmd
    y1 = spmdCat(x,1); % Concatenate along rows.
    y2 = spmdCat(x,2); % Concatenate along columns.
end
y1{1}
ans = 4×2

     4     7
     7    19
    12    39
    19    67

y2{1}
ans = 1×8

    4    7    7    19    12    39    19    67

使用 spmdReduce 执行缩减运算

spmdPlusspmdCat 函数是 spmdReduce 函数的特例。spmdReduce 函数允许您通过对 spmd 模块中所有工作单元上都存在的变量应用任意二元函数,从而执行任何归约操作。您可以使用 spmdReduce 不仅对各工作单元上的变量进行求和或拼接,还可以查找最小值或最大值,并执行其他有用的操作。

例如,使用 spmdReduce 计算变量 x 在所有工作单元上的逐元素乘积。指定指向 times 函数的函数句柄。该归约操作将 x 视为一个按行分布在各工作单元上的矩阵,因此结果 p 是一个包含 2 个元素的行向量,其中包含 x 的每一列在所有工作单元上的乘积。

spmd
    p = spmdReduce(@times,x);
end
p{1}
ans = 1×2

    6384    347529

您还可以获取所有工作单元中 x 的逐元素最大值和最小值。M{1}m{1} 返回行向量,其中包含所有工作单元中 x 各列的最大值和最小值。

spmd
    M = spmdReduce(@max,x);
    m = spmdReduce(@min,x);
end
M{1}
ans = 1×2

    19    67

m{1}
ans = 1×2

    4    7

当需要执行多次归约操作时(例如计算同一分布式数组的多个统计量),使用自定义归约函数将这些操作合并为单次 spmdReduce 调用会更高效。这减少了工作单元之间交换的消息总数,并提高了性能。为清晰起见,示例中的代码展示了单次缩减,但通常在可能的情况下,您可以将它们合并。

在各个工作单元上执行逻辑运算

MATLAB 为逻辑数组提供了内置的结合律运算,例如逻辑与、逻辑或和异或。使用 andorxor 函数的函数句柄,配合 spmdReduce 函数执行这些操作,并按列对所有工作单元的结果进行合并。

首先,在每个工作单元上创建一个名为 y 的逻辑数组。例如,将 x 的各元素与值 4 进行比较。这将创建一个逻辑数组,其中如果 x 中对应的元素大于 4,则该数组的相应元素为 true

spmd
    y = x > 4;
end

在各个工作单元上对 y 的元素执行逻辑运算。在工作单元 1 上显示结果。这些逻辑运算使您能够使用标准的逻辑规则,将所有工作单元上的逻辑数据进行组合。

spmd
    yand = spmdReduce(@and,y);
    yor  = spmdReduce(@or,y);
    yxor = spmdReduce(@xor,y);
end
yand{1}
ans = 1×2 logical array

    0    1

yor{1}
ans = 1×2 logical array

    1    1

yxor{1}
ans = 1×2 logical array

    1    0

在各个工作单元之间执行位运算

使用 spmdReduce 在所有工作单元上执行位运算,例如位与、位或和异或。这些操作使用 bitandbitorbitxor 函数的函数句柄,对所有工作单元的数据进行按列合并。

例如,在 spmd 模块内,对所有工作单元上定义的变量 x 进行位运算,并显示工作单元 1 的计算结果。这些操作使您能够使用标准的位运算逻辑对不同工作单元之间的数据进行组合。

spmd
    xbitand = spmdReduce(@bitand,x);
    xbitor = spmdReduce(@bitor,x);
    xbitxor = spmdReduce(@bitxor,x);
end
xbitand{1}
ans = 1×2

    0    3

xbitor{1}
ans = 1×2

    31    119

xbitxor{1}
ans = 1×2

    28    112

求全球最小值和最大值的出现位置

在并行计算中,通常需要找出所有工作单元中的全局最小值或最大值,并确定哪个工作单元持有这些值。您可以通过将自定义缩减函数与 spmdReduce 结合使用来实现这一点。

查找最大值

要找出全局最大值及其在各工作单元节点中的位置,请定义一个自定义归约函数。该自定义函数每次比较两个工作进程的局部最大值,并返回较大的那个及其位置,结果以元胞数组的形式返回。

function out = locateMaximum(in1,in2)
% in1 and in2 are cell arrays: {localMax,maxLoc}
% Each contains a scalar max value and its corresponding location
if in1{1} >= in2{1}
    out = in1;
else
    out = in2;
end
end

在所有工作单元上应用 locateMaximum 函数。作为缩减函数的输入,请创建一个元胞数组,其中第一个元素是本地数据的最大值,第二个元素是一个结构体,其中详细说明了工作单元和本地数组的索引。

spmd
    [localMax,idx] = max(x);
    maxLoc = struct("worker",spmdIndex,"arrayIdx",idx);
    result = spmdReduce(@locateMaximum,{localMax,maxLoc});
    maxval = result{1};
    maxloc = result{2};
end

显示存储在工作单元 1 上的全局最大值的值及其位置。

maxval{1}
ans = 
67
maxloc{1}
ans = struct with fields:
      worker: 4
    arrayIdx: 2

查找最小值的位置

同样地,您可以通过定义另一个自定义归约函数来找到全局最小值及其位置,该函数每次比较来自两个工作单元的本地最小值,并返回较小的那个及其位置,结果以元胞数组的形式返回。

function out = locateMinimum(in1,in2)
% in1 and in2 are cell arrays: {localMin,minLoc}
% Each contains a scalar min value and its corresponding location
if in1{1} <= in2{1}
    out = in1;
else
    out = in2;
end
end

将此函数应用于所有工作单元。与之前一样,缩减函数的输入是一个元胞数组,其中第一个元素是本地数据的最小值,第二个元素是一个结构体,其中详细说明了工作单元和本地数组的索引。

spmd
    [localMin,idx] = min(x);
    minLoc = struct("worker",spmdIndex,"arrayIdx",idx);
    result = spmdReduce(@locateMinimum,{localMin,minLoc});
    minval = result{1};
    minloc = result{2};
end

显示全局最小值的值及其位置。

minval{1}
ans = 
4
minloc{1}
ans = struct with fields:
      worker: 1
    arrayIdx: 1

另请参阅

| | |

主题