OpenMP 还原同步错误

OpenMP reduction synchronization error

我正在尝试将一个循环与相互依赖的循环并行化,我已经尝试过缩减并且代码有效,但结果是错误的,我认为缩减适用于总和但不适用于更新正确循环中的数组,有一种方法可以使循环并行化以获得正确的结果吗?

#pragma omp parallel for reduction(+: sum)
for (int i = 0; i < DATA_MAG; i++)
{
    sum += H[i];

    LUT[i] = sum * scale_factor;
}

reduction 子句为团队中的每个线程创建了 sum 的私有副本,就好像私有子句已在 sum 上使用过一样。在 for 循环之后,每个私有副本的结果与 sum 的原始共享值组合。由于共享 sum 仅在 for 循环之后更新,因此您不能在 for 循环内依赖它。

在这种情况下你需要做一个前缀和。不幸的是, 是大 DATA_MAG 的内存带宽限制,而小 DATA_MAG 的 OpenMP 开销占主导地位。但是,在小型和大型之间可能存在一些最佳点,您可以在其中看到使用线程的一些好处。

但在您的情况下,DATA_MAG 只有 256,这非常小,无论如何都不会从 OpenMP 中受益。您可以做的是使用 SIMD。但是,据我所知,OpenMP 的 simd 构造对于前缀和来说还不够强大。但是,您可以像这样手动起诉内在函数来做到这一点

#include <stdio.h>
#include <x86intrin.h>

#define N 256

inline __m128 scan_SSE(__m128 x) {
  x = _mm_add_ps(x, _mm_castsi128_ps(_mm_slli_si128(_mm_castps_si128(x), 4)));
  x = _mm_add_ps(x, _mm_castsi128_ps(_mm_slli_si128(_mm_castps_si128(x), 8)));
  return x;
}

void prefix_sum_SSE(float *a, float *s, int n, float scale_factor) {
  __m128 offset = _mm_setzero_ps();
  __m128 f = _mm_set1_ps(scale_factor);
  for (int i = 0; i < n; i+=4) {
    __m128 x = _mm_loadu_ps(&a[i]);
    __m128 out = scan_SSE(x);
    out = _mm_add_ps(out, offset);
    offset = _mm_shuffle_ps(out, out, _MM_SHUFFLE(3, 3, 3, 3));
    out = _mm_mul_ps(out, f); 
    _mm_storeu_ps(&s[i], out);
  }
}

int main(void) {
  float H[N], LUT[N];
  for(int i=0; i<N; i++) H[i] = i;
  prefix_sum_SSE(H, LUT, N, 3.14159f);
  for(int i=0; i<N; i++) printf("%.1f ", LUT[i]); puts("");
  for(int i=0; i<N; i++) printf("%.1f ", 3.14159f*i*(i+1)/2); puts("");

}

有关使用 SSE 和 AVX 的 SIMD 前缀和的更多详细信息,请参阅 here