我想使用OpenMP和SIMD对数组进行减少。我读到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 但原子也不支持复杂的运营商。我所做的是用关键替换原子并用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;
}
然而,这个功能并不像我希望的那样好。我正在使用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中是否有更好的方法可以使用更复杂的运算符进行缩减?
答案 0 :(得分:1)
我想你问题的答案是否定的。我不认为在OpenMP中使用更复杂的运算符有更好的方法。
假设阵列是16位对齐的,openmp线程的数量是4,可以预期OpenMP + SIMD的性能增益为12x-16x。实际上,它可能无法产生足够的性能提升,因为