【问题标题】:Parallelize inner loops with streams when writing to outer variables写入外部变量时将内部循环与流并行化
【发布时间】:2018-07-04 07:55:16
【问题描述】:

我坚持并行化以下代码:

double[][] a, b, c;
double d;
double[] e;
for (int i = 1; i < x; i++) {
    double f = 0.0;
    for (int j = 0; j < y; j++) {
        double a1 = a[i-1][j];
        double a2 = a[i][j];
        double a3 = a1 * a2;
        d -= a3;
        c[i][j] = c[i - 1][j] + a3;
        f += c[i][j] * a3;
    }
    e[i] = d + f;
    for (int j = 0; j < y; j++) {
        a[i][j] = e[i] * b[i][j]
    }
}

第二个内部循环依赖于第一个(因为e[i]),因此它们必须按顺序执行,但在每个内部循环中,计算可以在y 上并行化。

问题是它们都在外部变量上读写。写入可以并行化(概念上),因为每个内部循环都将其部分结果聚合到全局变量中。

x 大约为 10000 和 y 250。在此示例中简化了内部循环处理,但实际上计算量更大。

这里的问题是如何并行化读写外部变量的循环?

由于df,以下尝试无法编译:

double[][] a, b, c;
double d;
double[] e;
for (int i = 1; i < x; i++) {
    double f = 0.0;
    IntStream.range(0, y).parallel().forEach(j -> {  
        double a1 = a[i-1][j];
        double a2 = a[i][j];
        double a3 = a1 * a2;
        d -= a3;
        c[i][j] = c[i - 1][j] + a3;
        f += c[i][j] * a3;
    });
    e[i] = d + f;
    IntStream.range(0, y).parallel().forEach(j -> {  
        a[i][j] = e[i] * b[i][j];
    });
}

【问题讨论】:

  • 简答:只是不要为此使用并行。开销远远超过实际收益
  • 那么我可以说。你试过什么?你有 1.4k 的声誉,但似乎仍然不明白在这里问什么。目前,您已经向我们展示了以旧方式工作(正常)的代码。但是你的尝试在哪里?你在哪里挣扎?请澄清并编辑您的问题。因为现在只是。 “请为我做这项工作”
  • 另外,我并没有在您的代码中看到任何流。您想要使用 Streams 简单地为您重写代码吗?因为我很确定你至少可以自己尝试一下
  • @Samuel 您能否提供示例输入和示例输出,a,b,c,d
  • @user1516873 外循环迭代依赖于之前的迭代(读取a[i-1],写入a[i],因此无法并行化。

标签: java parallel-processing java-stream


【解决方案1】:

要并行化子循环,您必须首先了解每个子循环的结果。

第一个循环的结果是:

  • 变量d的增量
  • f的值
  • 新的c[i] 子数组

要计算它们,您可以创建一个自定义类型来保存 3 个值,然后执行可变归约来计算所有 3 个值。由于没有更好的名称,我将自定义类型称为 ResultContainer

同样,第二个循环的结果是数组a[i]。这更简单,因为从Stream 构建数组很容易。

因此,这将给出:

for (int i0 = 1; i0 < x; i0++) {
    final int i = i0; // tmp store as final for use in lambda
    ResultContainer result = IntStream.range(0, y).parallel()
            .collect(() -> new ResultContainer(y), (resultContainer, j) -> {
                double a1 = a[i - 1][j];
                double a2 = a[i][j];
                double a3 = a1 * a2;
                double cij = c[i - 1][j] + a3;
                resultContainer.add(-a3, cij * a3, j, cij);
            }, ResultContainer::add);

    d += result.d;
    e[i] = d + result.f;
    c[i] = result.ci;
    a[i] = IntStream.range(0, y).parallel().mapToDouble(j -> e[i] * b[i][j]).toArray();
}

使用我们的自定义类型:

class ResultContainer {
    double d;
    double f;
    double[] ci;

    public ResultContainer(int y) {
        this.d = 0;
        this.f = 0;
        ci = new double[y];
    }

    public void add(double d, double f, int j, double cij) {
        this.d += d;
        this.f += f;
        ci[j] = cij;
    }

    public void add(ResultContainer resultContainer2) {
        d += resultContainer2.d;
        f += resultContainer2.f;
        for (int j = 0; j < ci.length; j++) {
            // note that one of the two is always 0 here
            ci[j] += resultContainer2.ci[j];
        }
    }
}

【讨论】:

  • 谢谢@Didier L!正在收敛到类似的东西,但你让它在我之前工作。干得好!
猜你喜欢
  • 2014-07-20
  • 2012-12-02
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-25
  • 2012-02-06
相关资源
最近更新 更多