Je veux faire une réduction sur un tableau en utilisant OpenMP et SIMD. Je lis qu'une réduction de OpenMP est équivalent à:réduction avec OpenMP avec SSE/AVX
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;
}
J'ai eu cette idée du lien de suivi: http://bisqwit.iki.fi/story/howto/openmp/#ReductionClause Mais atomique ne supporte pas les opérateurs complexes. Ce que je faisais était de remplacer atomique avec critique et mis en œuvre la réduction avec OpenMP et SSE comme ceci:
#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;
}
Cependant, cette fonction ne fonctionne pas aussi bien que je l'espère. J'utilise Visual Studio 2012 Express. Je sais que je peux améliorer la performance un peu en déroulant la charge SSE/ajouter quelques fois mais c'est quand même moins que ce à quoi je m'attendais.
Je reçois beaucoup de meilleures performances en cours d'exécution sur les tranches des tableaux égal au nombre de fils:
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;
}
Est-ce que quelqu'un sait s'il y a une meilleure façon de faire des réductions de plus compliquées opérateurs dans OpenMP?
Quelle est la taille de N que vous utilisez pour faire le test? – veda
Je fais les tests sur les tailles de cache: N (L1) = 32k, N (L2) = 256K, N (L3/4) = 2M, et N >> N (L3). –