礼拜堂简化目前忽略变量的初始值。这意味着这段代码

var x: int;
for i in 1..3 {
  forall j in 1..10 with (+ reduce x) {
    x += 1;
  }
}

writeln(x);

会返回10,而不是30(就像该用户天真的想法一样)。尽管这种行为很好(并且在减少子句的注释中有记录-我只是对此并不认真考虑),但事实证明,如果我想达到30(通过在两个循环中累加),我需要实际上是手工做的总和。我认为for循环也具有reduce意图是非常优雅和对称的...即我想写
var x: int;
for i in 1..3 with (+ reduce x) {
  forall j in 1..10 with (+ reduce x) {
    x += 1;
  }
}

writeln(x);

请注意,即使在对数字求和的情况下,我也需要引入一个临时变量。对于像max/min这样的操作,需要格外小心。

有没有理由不支持for循环中的reduce意图?或者,是否有更惯用的方法(教堂礼拜式的)?

更新:我考虑得越多,在外部forforall替换的情况下,我建议的代码是否可以工作并不明显。我认为问题在于变量是任务本地的,而不是迭代本地的,因此减少只会发生在任务上。因此,仍然需要一个单独的内部减少步骤。这将消除对临时变量的需求。

我认为更重要的问题是,进行此类嵌套式约简的正确方法是什么...

最佳答案

在我看来,这是Chapel的reduce intent设计中的一个疏忽。具体来说,虽然我认为在将其归约变量的个人副本初始化为身份时,每个任务都应忽略原始变量的值是适当的(如您所知,目前已完成),但我认为任务的贡献应合并回原始变量中变量在并行循环结束时的值,而不是简单地覆盖原始值,因为它们会相互组合。这将使您的原始尝试按预期工作,并遵循OpenMP所做的工作,如以下C示例所建议的那样,其结果为35:

#include <stdio.h>
#include <omp.h>

int main(int argc, char* argv[]) {
  int tot = 5;
  for (int i=0; i<3; i++) {
  #pragma omp parallel for reduction(+:tot)
    for (int j=0; j<10; j++) {
      tot += 1;
    }
  }
  printf("tot is: %d\n", tot);
}

我建议在Chapel GitHub issues page上提出一个提倡这种行为的错误/功能请求。

从Chapel 1.15.0开始,解决此问题的一种方法是在串行循环中手动执行减少操作,如下所示:
config var tot: int = 5;

for i in 1..3 {
  var subtot: int;
  forall j in 1..10 with (+ reduce subtot) do
    subtot += 1;
  tot += subtot;
}

writeln("tot is: ", tot);

10-07 21:36