使用SSE / AVX进行OpenMP减少

我想使用OpenMP和SIMD对arrays进行减少。 我读到OpenMP的减少相当于:

inline float sum_scalar_openmp2(const float a[], const size_t N) { float sum = 0.0f; #pragma omp parallel { float sum_private = 0.0f; #pragma omp parallel for nowait for(int i=0; i<N; i++) { sum_private += a[i]; } #pragma omp atomic sum += sum_private; } return sum; } 

我从以下链接得到了这个想法: http : //bisqwit.iki.fi/story/howto/openmp/#ReductionClause但是atomic也不支持复杂的运算符。 我所做的是用关键替换primefaces并用OpenMP和SSE实现这种减少:

 #define ROUND_DOWN(x, s) ((x) & ~((s)-1)) inline float sum_vector4_openmp(const float a[], const size_t N) { __m128 sum4 = _mm_set1_ps(0.0f); #pragma omp parallel { __m128 sum4_private = _mm_set1_ps(0.0f); #pragma omp for nowait for(int i=0; i < ROUND_DOWN(N, 4); i+=4) { __m128 a4 = _mm_load_ps(a + i); sum4_private = _mm_add_ps(a4, sum4_private); } #pragma omp critical sum4 = _mm_add_ps(sum4_private, sum4); } __m128 t1 = _mm_hadd_ps(sum4,sum4); __m128 t2 = _mm_hadd_ps(t1,t1); float sum = _mm_cvtss_f32(t2); for(int i = ROUND_DOWN(N, 4); i < N; i++) { sum += a[i]; } return sum; } 

但是,这个function并不像我希望的那样好。 我正在使用Visual Studio 2012 Express。 我知道我可以通过展开SSE加载/添加几次来提高性能,但仍然低于我的预期。

通过运行等于线程数的数组切片,我获得了更好的性能:

 inline float sum_slice(const float a[], const size_t N) { int nthreads = 4; const int offset = ROUND_DOWN(N/nthreads, nthreads); float suma[8] = {0}; #pragma omp parallel for num_threads(nthreads) for(int i=0; i<nthreads; i++) { suma[i] = sum_vector4(&a[i*offset], offset); } float sum = 0.0f; for(int i=0; i<nthreads; i++) { sum += suma[i]; } for(int i=nthreads*offset; i < N; i++) { sum += a[i]; } return sum; } inline float sum_vector4(const float a[], const size_t N) { __m128 sum4 = _mm_set1_ps(0.0f); int i = 0; for(; i < ROUND_DOWN(N, 4); i+=4) { __m128 a4 = _mm_load_ps(a + i); sum4 = _mm_add_ps(sum4, a4); } __m128 t1 = _mm_hadd_ps(sum4,sum4); __m128 t2 = _mm_hadd_ps(t1,t1); float sum = _mm_cvtss_f32(t2); for(; i < N; i++) { sum += a[i]; } return sum; 

}

有人知道在OpenMP中是否有更好的方法可以使用更复杂的运算符进行减少?

我想你问题的答案是否定的。我不认为在OpenMP中使用更复杂的运算符有更好的方法。

假设arrays是16位对齐的,openmp线程的数量是4,可以预期OpenMP + SIMD的性能增益为12x-16x。 实际上,它可能无法产生足够的性能提升,因为

  1. 创建openmp线程有一个开销。
  2. 代码正在为1次加载操作执行1次加法操作。 因此,CPU没有做足够的计算。 因此,它几乎看起来CPU花费大部分时间来加载数据,这种内存带宽受限。