【问题标题】:Summing with OpenMP using C使用 C 与 OpenMP 求和
【发布时间】:2011-10-07 12:51:10
【问题描述】:

我已经尝试并行化这段代码大约两天了,但一直出现逻辑错误。该程序是使用非常小的 dx 的总和来找到积分的面积,并计算积分的每个离散值。我正在尝试使用 openmp 来实现这一点,但实际上我没有使用 openmp 的经验。我需要你的帮助。实际目标是并行化线程中的 suma 变量,以便每个线程计算更少的积分值。程序编译成功,但是当我执行程序时它返回错误的结果。

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

int main(int argc, char *argv[]){
    float down = 1, up = 100, dx, suma = 0, j;
    int steps, i, nthreads, tid;
    long starttime, finishtime, runtime; 

    starttime = omp_get_wtime();
    steps = atoi(argv[1]);
    dx = (up - down) / steps;

    nthreads = omp_get_num_threads();
    tid = omp_get_thread_num();
    #pragma omp parallel for private(i, j, tid) reduction(+:suma)
    for(i = 0; i < steps; i++){
        for(j = (steps / nthreads) * tid; j < (steps / nthreads) * (tid + 1); j += dx){
            suma += ((j * j * j) + ((j + dx) * (j + dx) * (j + dx))) / 2 * dx;
        }
    }
    printf("For %d steps the area of the integral  3 * x^2 + 1 from %f to %f is: %f\n", steps, down, up, suma);
    finishtime = omp_get_wtime();
    runtime = finishtime - starttime;
    printf("Runtime: %ld\n", runtime);
    return (0);
}

【问题讨论】:

  • 您收到什么结果?你期望它是什么?
  • 我预计数字在 250 左右,但我得到的结果是 1807761.125000 或更多。我只需要得到一个号码。
  • 250 似乎有点偏离。 3x^2 + 1 从 1 到 100 的积分是 1000098。

标签: c sum openmp


【解决方案1】:

问题在于您的 for 循环。如果您使用 for-pragma,OpenMP 会为您执行循环拆分:

#pragma omp parallel for private(i) reduction(+:suma)
for(i = 0; i < steps; i++) {
    // recover the x-position of the i-th step
    float x = down + i * dx;
    // evaluate the function at x
    float y = (3.0f * x * x + 1)
    // add the sum of the rectangle to the overall integral
    suma += y * dx
}

即使您将转换为必须自己计算索引的并行化方案,这也是有问题的。外部循环应该只执行 nthread 次。

您还应该考虑切换到双精度以提高准确性。

【讨论】:

  • 我使用 i 循环来区分我将上下总计划分为的所有步骤,并使用 j 循环给每个人一个浮点值。我不能给变量 i 一个浮点值,因为 openmp 不允许我这样做。我仍然无法让它正常工作。
  • 您似乎想使用函数的分析积分。如果你已经有了这个,你不需要做一个数值近似。
  • 你觉得private还有用吗?
  • 我用你给我的循环来改变我的代码,但我无法理解你计算 suma 的操作。
  • @ebo 谢谢你的时间。
【解决方案2】:

让我们只考虑threads=1 的情况。这个:

#pragma omp parallel for private(i, j, tid) reduction(+:suma)
for(i = 0; i < steps; i++){
    for(j = (steps / nthreads) * tid; j < (steps / nthreads) * (tid + 1); j += dx){
        suma += ((j * j * j) + ((j + dx) * (j + dx) * (j + dx))) / 2 * dx;
    }
}

变成这样:

for(i = 0; i < steps; i++){
    for(j = 0; j < steps; j += dx){
        suma += ((j * j * j) + ((j + dx) * (j + dx) * (j + dx))) / 2 * dx;
    }
}

然后你就可以开始看到问题了;你基本上是在循环步骤2

此外,您的第二个循环没有任何意义,因为您正在递增 dx.索引 (i, j) 与物理域中的位置 (i*dx) 之间的相同混淆显示在您的增量中。 j+dx 没有任何意义。大概您希望将suma 增加 (f(x) + f(x'))*dx/2 (例如,梯形规则);那应该是

        float x = down + i*dx;
        suma += dx * ((3 * x * x + 1) + (3 * (x + dx) * (x + dx) + 1)) / 2;

正如 ebo 指出的那样,您希望对 被积函数 求和,而不是它的反导数。

现在,如果我们对答案进行检查:

printf("For %d steps the area of the integral  3 * x^2 + 1 from %f to %f is: %f (expected: %f)\n",
            steps, down, up, suma, up*up*up-down*down*down + up - down);

我们连续运行它,我们开始得到正确的答案:

$ ./foo 10
For 10 steps the area of the integral  3 * x^2 + 1 from 1.000000 to 100.000000 is: 1004949.375000 (expected: 1000098.000000)
Runtime: 0
$ ./foo 100
For 100 steps the area of the integral  3 * x^2 + 1 from 1.000000 to 100.000000 is: 1000146.562500 (expected: 1000098.000000)
Runtime: 0
$ ./foo 1000
For 1000 steps the area of the integral  3 * x^2 + 1 from 1.000000 to 100.000000 is: 1000098.437500 (expected: 1000098.000000)
Runtime: 0

在串行案例起作用之前,担心 OpenMP 案例是没有意义的。

正如 ebo 指出的那样,一旦需要 OpenMP,最简单的方法就是让 OpenMP 为您进行循环分解:例如,

#pragma omp parallel for reduction(+:suma)
    for(i = 0; i < steps; i++){
        float x = down + i*dx;
        suma += dx * ((3 * x * x + 1) + (3 * (x + dx) * (x + dx) + 1)) / 2;
    }

运行这个,得到

$ setenv OMP_NUM_THREADS 1
$ ./foo 1000
For 1000 steps the area of the integral  3 * x^2 + 1 from 1.000000 to 100.000000 is: 1000098.437500 (expected: 1000098.000000)
Runtime: 0
$ setenv OMP_NUM_THREADS 2
$ ./foo 1000
For 1000 steps the area of the integral  3 * x^2 + 1 from 1.000000 to 100.000000 is: 1000098.437500 (expected: 1000098.000000)
Runtime: 0
$ setenv OMP_NUM_THREADS 4
$ ./foo 1000
For 1000 steps the area of the integral  3 * x^2 + 1 from 1.000000 to 100.000000 is: 1000098.625000 (expected: 1000098.000000)
Runtime: 0
$ setenv OMP_NUM_THREADS 8
$ ./foo 1000
For 1000 steps the area of the integral  3 * x^2 + 1 from 1.000000 to 100.000000 is: 1000098.500000 (expected: 1000098.000000)

如果您真的愿意,可以在 OpenMP 中显式地进行阻塞,但您应该有这样做的理由。

【讨论】:

  • 很抱歉我没有回复你们的答案,但我急于赶上最后期限。我明白我的问题是什么,我无法将 dx 变量放入整数循环中。谢天谢地,ebo 给了我这个答案,我要感谢乔纳森为我的问题提供了出色的扩展解决方案。我更改了我的代码,我很高兴地说它运行良好;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-02-18
  • 2012-06-04
  • 2021-09-20
  • 2018-03-13
  • 2015-08-06
  • 2017-02-20
  • 1970-01-01
相关资源
最近更新 更多