c - 与 omp atomic 并行化

标签 c openmp

我想知道这两个循环在性能上是否等效,a 是一个全局 int 而 M 是一个 int[]:

#pragma omp for
for (int i = 0; i < N; ++i) {
    #pragma omp atomic
    a += M[i];
}

for (int i = 0; i < N; ++i) {
    a += M[i];
}

换句话说,知道唯一的指令必须是原子的,是否值得将其并行化? 我个人认为加速第二个循环是不可能的,因为这种行为永远不会同时执行超过一次。

最佳答案

没有同步的循环的问题是结果可能不正确。

为了加快计算速度,您可以使用reduction 子句,这样:

#pragma omp parallel for reduction(+:a)

你可以很容易地对计算产生一些反作用:

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

int main(void)
{
    long long int setup = 0, sum_atomic = 0, sum_simple = 0, sum_reduc = 0, sum_noomp = 0;
    const int N = 100000;
    int *M = malloc(sizeof *M * N);
    double tick[5];

    for (int i = 0; i < N; ++i) {
        M[i] = i;
    }

    /* setup zone to prevent measuring first parallel zone drawback */
    #pragma omp parallel for 
    for (int i = 0; i < N; ++i) {
        setup += M[i];
    }

    /* using single thread execution */
    tick[0] = omp_get_wtime();
    for (int i = 0; i < N; ++i) {
        sum_noomp += M[i];
    }        

    /* using reduction */
    tick[1] = omp_get_wtime();
    #pragma omp parallel for reduction(+:sum_reduc)
    for (int i = 0; i < N; ++i) {
        sum_reduc += M[i];
    }

    /* using openmp, the wrong way */
    tick[2] = omp_get_wtime();
    #pragma omp parallel for
    for (int i = 0; i < N; ++i) {
        sum_simple += M[i];
    }

    /* using atomic keyword */
    tick[3] = omp_get_wtime();
    #pragma omp parallel for
    for (int i = 0; i < N; ++i) {
        #pragma omp atomic
        sum_atomic += M[i];
    }

    tick[4] = omp_get_wtime();

    printf("noomp:  %lld, in %.0f us\n", sum_noomp,  1000000.*(tick[1]-tick[0]));
    printf("reduc:  %lld, in %.0f us\n", sum_reduc,  1000000.*(tick[2]-tick[1]));
    printf("simple: %lld, in %.0f us\n", sum_simple, 1000000.*(tick[3]-tick[2]));   
    printf("atomic: %lld, in %.0f us\n", sum_atomic, 1000000.*(tick[4]-tick[3]));

    free(M);

    return 0;
}

在双核 CPU 上,结果是:

noomp:  4999950000, in 28 us  -- single thread quite fast 
reduc:  4999950000, in 17 us  -- reduction: twice fast
simple: 2024135316, in 12 us  -- incorrect sum 
atomic: 4999950000, in 3686 us -- atomic kw: slow compared to single thread version

所以:

  • 更快的方法是使用 openmp reduction
  • openmp 在使用 atomic 时比顺序版本慢
  • 在没有 reductionatomic 的情况下使用 openmp 结果是错误的

详细信息:

使用 gcc 8.2.1 在 tio machine 上使用 -Wall -O3 -mtune=native -march=native -fopenmp 选项编译,使用 Xeon CPU E5-2650 v4 的两个内核。

关于c - 与 omp atomic 并行化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53823793/

相关文章:

c - 在 Linux 中使用 fgets 命名管道在 C 程序中删除数据

c - 使用仅一个函数参数的递归的对称树检查算法

c - 在 2 个 .c 文件之间共享函数

c - 为什么 ELF 头和文本段一起加载到内存中?

c++ - 使用 OpenMP 和减少优化外循环

c++ - OpenMP:有人知道堆损坏的原因吗?

c - 总线错误协助

c - 在不平衡树上拆分 OpenMP 线程

c++ - OpenMP:同时写入 std::map

c++ - 是否可以在 C++ 中使用 openmp 进行乘法初始化?