2016-04-18 1 views
2

J'essaye de paralléliser une boucle avec des cycles interdépendants, j'ai essayé avec une réduction et le code fonctionne, mais le résultat est faux, je pense que la réduction fonctionne pour la somme mais pas pour le mise à jour de la matrice dans le bon cycle, il existe un moyen d'obtenir le bon résultat paralléliser la boucle?Erreur de synchronisation de réduction OpenMP

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

    LUT[i] = sum * scale_factor; 
} 
+1

Vous ne pouvez pas utiliser la valeur que vous réduisez pendant que vous la réduisez, c'est-à-dire que 'LUT [i] = sum * facteur_d'échelle;' ne fonctionnera pas. –

+0

Vous devez utiliser une méthode de somme prédéfinie. Malheureusement, la somme de préfixe parallèle est liée à la bande passante de la mémoire, donc je ne pense pas que cela vous aidera beaucoup. –

+0

Ok ... Je m'en doutais, alors il n'y a aucun moyen? – CIVI89

Répondre

2

La clause de réduction crée des copies privées de sum pour chaque fil dans l'équipe comme si la clause avait été privée utilisée sur sum. Après la boucle for, le résultat de chaque copie privée est combiné avec la valeur partagée d'origine de sum. Puisque le sum partagé n'est mis à jour qu'après la boucle for, vous ne pouvez pas vous en tenir à l'intérieur de la boucle for.

Dans ce cas, vous devez faire une somme de préfixes. Malheureusement, le parallel prefix-sum using threads est la bande passante de la mémoire liée à DATA_MAG grande et il est dominé par le surdébit OpenMP pour petit DATA_MAG. Cependant, il peut y avoir une petite tache entre petit et grand où vous avez vu un certain avantage en utilisant des fils.

Mais dans votre cas DATA_MAG est seulement 256 qui est très petit et ne bénéficierait pas de OpenMP de toute façon. Ce que vous pouvez faire est d'utiliser SIMD. Cependant, la construction simd d'OpenMP n'est pas assez puissante pour la somme des préfixes autant que je sache. Cependant, vous pouvez le faire manuellement comme celui-ci poursuit intrinsics

#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(""); 

} 

Voir here pour plus de détails sur la somme pré-fix SIMD avec SSE et AVX.