c - 当使用 openMP 并行化代码时,哪些变量应该是私有(private)的和/或firstprivate,什么时候合适?

标签 c parallel-processing openmp

我的任务是并行化这个函数并使其比顺序运行时间更快,但是我尝试过的#pragma omp parallel for语句似乎没有产生实质性的效果。

此代码的顺序版本本质上是相同的,除了#pragma 语句之外。我意识到代码写得非常糟糕,它是作业的一部分,其目标是实现 8 倍的加速。代码运行的 Linux 机器是具有超线程的 8 核系统。

测试运行时间的方法是通过以下代码行的输出:

    clock_gettime(CLOCK_MONOTONIC, &start);
    work_it_par(original, new);
    clock_gettime(CLOCK_MONOTONIC, &finish);

类似的代码调用同一函数的顺序版本,然后通过顺序时间/并行时间计算加速比。然而,我的结果似乎非常不一致,并且我似乎无法并行化超过 1.5。

void work_it_par(long *old, long *new) {
    int i, j, k;
    int u, v, w;
    long compute_it;
    long aggregate=1.0;
    long weNeedTheFunc = we_need_the_func();
    long gimmieTheFunc = gimmie_the_func();
    int marker = DIM-1;
    #pragma omp parallel for private(i, j, k, compute_it)
    for (i=1; i<marker; i++) {
        for (j=1; j<marker; j++) {
            for (k=1; k<marker; k++) {
                compute_it = old[i*DIM*DIM+j*DIM+k] * weNeedTheFunc;
                aggregate+= compute_it / gimmieTheFunc;
            }
        }
    }
    printf("AGGR:%ld\n",aggregate);
//#pragma omp parallel for private(i, j, u, v)
    for (i=1; i<marker; i++) {
#pragma omp parallel for private(k)
    for (j=1; j<marker; j++) {
        for (k=1; k<marker; k++){
            new[i*DIM*DIM+j*DIM+k]=0;
            for (u=-1; u<=1; u++) {
                for (v=-1; v<=1; v++) {
                    for (w=-1; w<=1; w++) {
                        new[i*DIM*DIM+j*DIM+k]+=old[(i+u)*DIM*DIM+(j+v)*DIM+(k+w)];
                    }
                }
            }
        new[i*DIM*DIM+j*DIM+k]/=27;
      }
    }
  }
#pragma omp parallel for private(i, j)
    for (i=1; i<marker; i++) {
//#pragma omp parallel for private(k)
        for (j=1; j<marker; j++) {
            for (k=1; k<marker; k++) {
                u=(new[i*DIM*DIM+j*DIM+k]/100);
                if (u<=0) u=0;
                if (u>=9) u=9;
                histogrammy[u]++;
             }
         }
    }
}

最佳答案

首先,您的代码在许多地方都是错误的。我乍一看有 7 个竞争条件。

我建议使用以下一般规则:

  1. 声明变量尽可能本地化。这比试图找出哪个变量需要私有(private)更容易。它还可以帮助将变量声明为 const 以确保它们可以安全地共享。

  2. 如果在并行循环中对变量求和,请使用归约子句。

将这些原则应用于第一个循环如下所示:

#pragma omp parallel for reduction(+:aggregate)
for (int i=1; i<marker; i++) {
    for (int j=1; j<marker; j++) {
        for (int k=1; k<marker; k++) {
            long compute_it = old[i*DIM*DIM+j*DIM+k] * weNeedTheFunc;
            aggregate+= compute_it / gimmieTheFunc;
        }
    }
}
  • 对于直方图,您还可以从 OpenMP 4.5 开始使用 reduction(+:histogrammy[:10]),或者在增量操作。哪一个更好取决于大小 - 数组缩减会产生每线程内存成本,原子更新会产生争用惩罚。

  • 通常,在安全的情况下并行化最外层循环。对于嵌套循环,应用 collapse 子句可能会很有帮助,其中包括工作共享中的多个循环。这是否有帮助,取决于线程数量、循环大小和平衡。通常它不会造成伤害。

  • 例如

    #pragma omp parallel for collapse(3)
    for (int i=1; i < marker; i++) {
        for (int j=1; j < marker; j++) {
            for (int k=1; k < marker; k++) {
    

    如果您已确保代码正确并且想要查看性能,请考虑以下事项:使用了解 OpenMP/线程的性能分析工具。如果您想讨论 StackOverflow 上的实际性能,您必须

    1. 包括 reproducible example - 包括如何构建它
    2. 描述您的具体绩效衡量方法
    3. 包含您的具体绩效衡量结果
    4. 描述您的系统(CPU、编译器版本)

    关于c - 当使用 openMP 并行化代码时,哪些变量应该是私有(private)的和/或firstprivate,什么时候合适?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56472915/

    相关文章:

    c - 将具有外部依赖项的共享库集成到 MATLAB |即 Armadillo 、LAPACK、BLAS

    c - 使用 openmp 并行化

    c - OpenMP Monte_Carlo 模拟实现目标与 PI 的接近度

    c++ - OpenMP:共享同一算法的单线程和多线程实现

    CUDA + 使用 C 计算 int 元素出现次数

    C语言——将输入转化为代码

    performance - Haskell 并行性能

    python - 如何并行运行生成器代码?

    matrix - Julia:将 pmap 与数组与 SharedArrays 一起使用

    c - 使用 scanf 解析 c 中的方程串