2013-03-15 4 views
4

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?

+0

Quelle est la taille de N que vous utilisez pour faire le test? – veda

+0

Je fais les tests sur les tailles de cache: N (L1) = 32k, N (L2) = 256K, N (L3/4) = 2M, et N >> N (L3). –

Répondre

1

Je pense que la réponse à votre question est: Non, je ne pense pas qu'il y ait une meilleure façon de faire réduire avec des opérateurs plus complexes OpenMP.

En supposant que le réseau est aligné 16 bits, le nombre de threads OpenMP est de 4, on peut attendre du gain de performances à 12x - 16x + OpenMP par SIMD. En réalité, il peut ne pas produire suffisamment de gain de performances car

  1. Il existe un surdébit dans la création des threads openmp.
  2. Le code effectue 1 opération d'ajout pour 1 opération de chargement. Par conséquent, le processeur ne fait pas assez de calculs. Ainsi, il semble presque que le processeur passe la plupart du temps à charger les données, le type de bande passante de la mémoire liée.
+0

Il existe une dépendance de boucle transportée dans le code SSE. Il peut être déroulé 2 fois ou plus pour accélérer les choses. En fait, je le fais déjà mais je n'ajoute pas le code car il prolonge le texte. –

+0

Les tableaux sont alignés sur 32 bits parce que je vérifie également AVX. –