簡體   English   中英

OpenMP atomic 比對數組的關鍵速度要慢得多

[英]OpenMP atomic substantially slower than critical for array

我看到的 OpenMP 的omp atomic示例通常涉及更新標量,並且通常報告它比omp critical更快。 在我的應用程序中,我希望更新已分配數組的元素,不同線程將更新的元素之間存在一些重疊,我發現 atomic 比關鍵要慢得多。 它是一個數組是否有區別,我是否正確使用它?

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

#define N_EACH 10000000
#define N_OVERLAP 100000

#if !defined(OMP_CRITICAL) && !defined(OMP_ATOMIC)
#error Must define OMP_CRITICAL or OMP_ATOMIC
#endif
#if defined(OMP_CRITICAL) && defined(OMP_ATOMIC)
#error Must define only one of either OMP_CRITICAL or OMP_ATOMIC
#endif

int main(void) {

  int const n = omp_get_max_threads() * N_EACH -
                (omp_get_max_threads() - 1) * N_OVERLAP;
  int *const a = (int *)calloc(n, sizeof(int));

#pragma omp parallel
  {
    int const thread_idx = omp_get_thread_num();
    int i;
#ifdef OMP_CRITICAL
#pragma omp critical
#endif /* OMP_CRITICAL */
    for (i = 0; i < N_EACH; i++) {
#ifdef OMP_ATOMIC
#pragma omp atomic update
#endif /* OMP_ATOMIC */
      a[thread_idx * (N_EACH - N_OVERLAP) + i] += i;
    }
  }

/* Check result is correct */
#ifndef NDEBUG
  {
    int *const b = (int *)calloc(n, sizeof(int));
    int thread_idx;
    int i;
    for (thread_idx = 0; thread_idx < omp_get_max_threads(); thread_idx++) {
      for (i = 0; i < N_EACH; i++) {
        b[thread_idx * (N_EACH - N_OVERLAP) + i] += i;
      }
    }
    for (i = 0; i < n; i++) {
      assert(a[i] == b[i]);
    }
    free(b);
  }
#endif /* NDEBUG */

  free(a);
}

請注意,在這個簡化的示例中,我們可以提前確定哪些元素將重疊,因此在更新這些元素時僅應用atomic / critical元素會更有效,但在我的實際應用程序中這是不可能的。

當我使用以下方法編譯它時:

  • gcc -O2 atomic_vs_critical.c -DOMP_CRITICAL -DNDEBUG -fopenmp -o critical
  • gcc -O2 atomic_vs_critical.c -DOMP_ATOMIC -DNDEBUG -fopenmp -o atomic

並隨時間運行time./critical我得到: real 0m0.110s user 0m0.086s sys 0m0.058s

隨着time./atomic ,我得到: real 0m0.205s user 0m0.742s sys 0m0.032s

所以它在關鍵部分使用了大約一半的掛鍾時間(當我重復它時我得到了同樣的結果)。

還有一篇文章聲稱 critical 比 atomic 慢,但是它使用了一個標量,當我運行提供的代碼時, atomic 結果實際上比 critical 稍快。

您的比較不公平: #pragma omp critical放在for循環之前,因此編譯器可以矢量化您的循環,但#pragma omp atomic update在循環內,這會阻止矢量化。 矢量化的這種差異導致了令人驚訝的運行時間。 為了在循環內進行公平比較:

for (i = 0; i < N_EACH; i++) {
#ifdef OMP_CRITICAL
#pragma omp critical
#endif /* OMP_CRITICAL */
#ifdef OMP_ATOMIC
#pragma omp atomic update
#endif /* OMP_ATOMIC */
   a[thread_idx * (N_EACH - N_OVERLAP) + i] += i;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM