主要内容

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

遍历分布式范围(for-drange)

注意

在分布式范围 (for) 上使用 drange 循环旨在对共存分布式数组的分布式维度进行显式索引(例如在 spmd 语句或通信作业内部)。对于大多数涉及并行 for 循环的应用程序,您应该首先尝试使用 parfor 循环。请参阅并行 for 循环 (parfor)

并行化 for 循环

在某些情况下,您已经有一个粗粒度的应用程序需要执行,即,该应用程序的运行时间明显大于启动和停止程序所需的通信时间。如果您不想为定义作业和任务的开销而烦恼,您可以利用 spmd 提供的易用性。现有程序可能需要数小时或数天来处理所有独立数据集,但您可以通过将这些独立计算分布在集群上来缩短该时间。

例如,假设您有以下序列代码:

results = zeros(1, numDataSets); 
for i = 1:numDataSets
    load(['\\central\myData\dataSet' int2str(i) '.mat'])
    results(i) = processDataSet(i);
 end 
plot(1:numDataSets, results);
save \\central\myResults\today.mat results

下列更改使此代码可以并行运行,既可以在 spmd 中交互运行,也可以在通信作业中运行:

results = zeros(1, numDataSets, codistributor()); 
for i = drange(1:numDataSets)
    load(['\\central\myData\dataSet' int2str(i) '.mat'])
    results(i) = processDataSet(i); 
end 
res = gather(results, 1); 
if spmdIndex == 1
    plot(1:numDataSets, res);
    print -dtiff -r300 fig.tiff;
    save \\central\myResults\today.mat res
end

请注意,for 迭代的长度和共存分布式数组 results 的长度需要匹配,以便在 for drange 循环内索引 results。这样,工作单元之间就不需要进行通信了。如果 results 只是一个复制数组,就像在并行运行原始代码时一样,那么每个工作单元都会将其部分分配给 results,而 results 的剩余部分则为 0。最后,results 会是一个变体,并且如果不明确调用 spmdSendspmdReceivespmdCat,就无法将总结果返回给一个(或所有)工作单元。

当使用 load 函数时,需要注意的是,必要时所有工作单元都可以访问数据文件。最佳做法是使用共享文件系统上的文件的明确路径。

相应地,当使用 save 函数时,您应该注意一次只能让一个工作单元保存到特定文件(在共享文件系统上)。因此,建议将代码包装在 if spmdIndex == 1 中。

因为 results 分布在各个工作单元上,所以本示例使用 gather 将数据收集到工作单元 1 上。

工作单元无法绘制可见的图形,因此 print 函数创建了可查看的绘图文件。

For-drange 循环中的共存分布式数组

当在通信作业中执行分布式范围内的 for 循环时,每个工作单元都会执行其循环部分,以便所有工作单元同时工作。因此,在执行 for-drange 循环时不允许工作单元之间进行通信。具体来说,一个工作单元只能访问其共存分布式数组的分区。在这样的循环中,任何需要一个工作单元访问另一个工作单元的共存分布式数组的部分的计算都将产生错误。

为了说明这一特性,您可以尝试下面的示例,其中一个 for 循环有效,但另一个无效。

使用 spmd,创建两个共存分布式数组,一个为单位矩阵,另一个设置为零,分布在四个工作单元上。

D = eye(8, 8, codistributor())
E = zeros(8, 8, codistributor())

默认情况下,这些数组按列分布;也就是说,四个工作单元中的每一位都包含每个数组的两列。如果在 for-drange 循环中使用这些数组,则任何计算都必须在每个工作单元内自包含。换句话说,您只能执行限制在每个工作单元内的工作单元所包含的数组的两列的计算。

例如,假设您想要将数组 E 的每一列设置为数组 D 的相应列的某个倍数:

for j = drange(1:size(D,2)); E(:,j) = j*D(:,j); end

此语句将 j 的第 E 列设置为 j 乘以 j 的第 D 列。实际上,虽然 D 是一个单位矩阵,其主对角线为 1,但 E 的主对角线序列为 123 等。

这是有效的,因为每个工作单元都可以访问执行计算所需的整个 D 列和整个 E 列,因为每个工作单元独立且同时地在八列中的两列上工作。

但是,假设您尝试根据 E 的不同列来设置 D 的列的值:

for j = drange(1:size(D,2)); E(:,j) = j*D(:,j+1); end

此方法失败,因为当 j 为 2 时,您尝试使用 E 的第三列来设置 D 的第二列。这些列存储在不同的工作单元中,因此会出现错误,表明不允许工作单元之间进行通信。

限制

要在共存分布式数组上使用 for-drange,必须满足以下条件:

  • 共存分布式数组使用一维分布方案(不是 2dbc)。

  • 分布符合默认分区方案。

  • for-drange 循环索引的变量为分布维度提供了数组下标。

  • 所有其他下标都可以自由选择(并且可以从每个维度的整个范围内的 for 循环中获取)。

要循环遍历数组中的所有元素,可以在分布维度上使用 for-drange,在所有其他维度上使用常规 for 循环。以下示例在由 4 个工作单元组成的并行池中执行 spmd 语句:

spmd
  PP = zeros(6,8,12,"codistributed");
  RR = rand(6,8,12,codistributor())
  % Default distribution: 
  %   by third dimension, evenly across 4 workers.

  for ii = 1:6
    for jj = 1:8
      for kk = drange(1:12)
        PP(ii,jj,kk) = RR(ii,jj,kk) + spmdIndex;
      end
    end
  end
end

要查看数组的内容,请输入:

PP