在 Chapel 中使用 forall 填充数组

Filling an array using forall in Chapel

这在我的笔记本电脑上运行良好,但我想知道这是否会导致大规模问题。假设我想填充一个非常大的数组,但每个条目都需要对一个大的、稀疏的分布式矩阵进行密集的矩阵运算。我应该期望以下设计能够坚持下去吗?

var x: [1..N] real;

forall i in [1..N] {
  x[i] = reallyHeavyMatrixComputation(i);
}

是否有保持清醒的技巧?我应该为 x 的域使用 dmapped 还是什么?

Chapel 的 forall 循环可以扩展到多个区域,但它们是否会这样做取决于它们迭代的内容以及循环的主体。

更详细地说,"How many tasks should be used?" 和 "Where should those tasks run?" 等关键并行循环策略由循环的 iterand 控制。例如,在下面的循环中:

var x: [1..N] real;                        // declare a local array

forall i in 1..N do                        // iterate over its indices in parallel
  x[i] = reallyHeavyMatrixComputation(i);

循环的 iterand 是范围 1..N。默认情况下,范围的迭代器将创建多个本地任务,这些任务等于当前语言环境中处理器的数量 units/cores。因此,上面的循环不会随着更多语言环境的使用而变得更快,除非 reallyHeavyMatrixComputation() 本身包含以智能方式在语言环境中分布计算的子句。如果它根本不包含任何 on-clauses,计算将永远不会离开 locale #0 并且只会是共享内存。

相比之下,如果使用 forall 循环遍历分布式域或数组,默认策略通常是 运行 每个目标区域设置上的任务数等于该区域设置上的处理器内核数"owner-computes" 时尚的语言环境。也就是说,每个语言环境将执行它拥有的由分布确定的迭代子集。例如,给定循环:

use CyclicDist;                            // make use of the cyclic distribution module

var D = {1..N} dmapped Cyclic(startIdx=1); // declare a cyclically distributed domain
var x: [D] real;                           // declare an array over that domain

forall i in D do                           // iterate over the domain in parallel
  x[i] = reallyHeavyMatrixComputation();

D 的索引将在区域设置中循环分布,因此并行循环将在每个区域设置上创建任务,这些任务将执行区域设置的索引。因此,即使 reallyHeavyMatrixComputation() 是完全本地计算,此循环也应扩展到多个区域。

在 Chapel 中编写可伸缩并行循环的另一种方法是调用一个显式并行迭代器,该迭代器将跨语言环境本身分配工作。例如,Chapel 版本 1.16 添加了一个 distributed iterators 包模块,该模块提供将为您分配工作的迭代器。回到第一个例子,如果改写为:

use DistributedIters;                  // make use of the distributed iterator module                                                                          

var x: [1..N] real;                    // declare a local array

forall i in distributedDynamic(1..N) do    // distribute iterations across locales
  x[i] = reallyHeavyMatrixComputation(i);

那么 distributedDynamic 迭代器的调用将成为循环的 iterand 而不是范围 1..N 和控制任务创建。此迭代器使用指定的块大小(默认为 1)动态地将迭代处理到语言环境,因此可用于以可扩展的方式使用多个语言环境。有关详细信息,请参阅 its documentation