嵌套缩减——在 Chapel 中最惯用的写法是什么?

Nested reductions -- what is the most idiomatic way to write these in Chapel?

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

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

writeln(x);

returns 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 意图?或者,是否有更惯用的(Chapel-rrific)方式来做到这一点?

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

我认为更重要的问题是进行这些嵌套归约的正确方法是什么...

在我看来,这是对 Chapel 的 reduce 意图设计的疏忽。具体来说,虽然我认为每个 task 在将其减少变量的个人副本初始化为标识时忽略原始变量的值是合适的(正如您目前所做的那样),但我相信任务的贡献应该在并行循环结束时组合回原始变量的值,而不是简单地在它们相互组合时覆盖原始值。这将使您最初的尝试如您预期的那样工作,并且也将遵循 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);