2011-01-25 3 views
1

Dans le code C suivant j'utilise OpenMP dans une boucle imbriquée. Depuis condition de course se produit, je veux effectuer des opérations atomiques à la fin:Pourquoi le compilateur ignore-t-il les pragmas OpenMP?

double mysumallatomic() { 

    double S2 = 0.; 
    #pragma omp parallel for shared(S2) 
    for(int a=0; a<128; a++){ 
    for(int b=0; b<128;b++){ 
     double myterm = (double)a*b; 
     #pragma omp atomic 
     S2 += myterm; 
    } 
    } 
    return S2; 
} 

La chose est que #pragma omp atomic n'a pas d'effet sur le comportement du programme, même si je l'enlève, rien ne se passe. Même si je le change en #pragma oh_my_god, je ne reçois aucune erreur!

Je me demande ce qui ne va pas ici, si je peux dire au compilateur d'être plus strictes lors de la vérification pragma OMP ou pourquoi je ne reçois pas une erreur quand je fais le dernier changement

PS: Pour la compilation j'utiliser :

gcc-4.2 -fopenmp main.c functions.c -o main_elec_gcc.exe 

PS2: Nouveau code qui me donne le même problème et basé sur l'idée de gillespie:

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <omp.h> 
#include <math.h> 

#define NRACK 64 
#define NSTARS 1024 

double mysumallatomic_serial(float rocks[NRACK][3], float moon[NSTARS][3], 
          float qr[NRACK],float ql[NSTARS]) { 
    int j,i; 
    float temp_div=0.,temp_sqrt=0.; 
    float difx,dify,difz; 
    float mod2x, mod2y, mod2z; 
    double S2 = 0.; 

    for(j=0; j<NRACK; j++){ 
    for(i=0; i<NSTARS;i++){  
     difx=rocks[j][0]-moon[i][0]; 
     dify=rocks[j][1]-moon[i][1]; 
     difz=rocks[j][2]-moon[i][2]; 
     mod2x=difx*difx; 
     mod2y=dify*dify; 
     mod2z=difz*difz; 
     temp_sqrt=sqrt(mod2x+mod2y+mod2z); 
     temp_div=1/temp_sqrt; 
     S2 += ql[i]*temp_div*qr[j];  
    } 
    } 
    return S2; 
} 

double mysumallatomic(float rocks[NRACK][3], float moon[NSTARS][3], 
         float qr[NRACK],float ql[NSTARS]) { 
    float temp_div=0.,temp_sqrt=0.; 
    float difx,dify,difz; 
    float mod2x, mod2y, mod2z; 
    double S2 = 0.; 

    #pragma omp parallel for shared(S2) 
    for(int j=0; j<NRACK; j++){ 
    for(int i=0; i<NSTARS;i++){ 
     difx=rocks[j][0]-moon[i][0]; 
     dify=rocks[j][1]-moon[i][1]; 
     difz=rocks[j][2]-moon[i][2]; 
     mod2x=difx*difx; 
     mod2y=dify*dify; 
     mod2z=difz*difz; 
     temp_sqrt=sqrt(mod2x+mod2y+mod2z); 
     temp_div=1/temp_sqrt; 
     float myterm=ql[i]*temp_div*qr[j];  
     #pragma omp atomic 
     S2 += myterm; 
    } 
    } 
    return S2; 
} 
int main(int argc, char *argv[]) { 
    float rocks[NRACK][3], moon[NSTARS][3]; 
    float qr[NRACK], ql[NSTARS]; 
    int i,j; 

    for(j=0;j<NRACK;j++){ 
    rocks[j][0]=j; 
    rocks[j][1]=j+1; 
    rocks[j][2]=j+2; 
    qr[j] = j*1e-4+1e-3; 
    //qr[j] = 1; 
    } 

    for(i=0;i<NSTARS;i++){ 
    moon[i][0]=12000+i; 
    moon[i][1]=12000+i+1; 
    moon[i][2]=12000+i+2; 
    ql[i] = i*1e-3 +1e-2 ; 
    //ql[i] = 1 ; 
    } 
    printf(" serial: %f\n", mysumallatomic_serial(rocks,moon,qr,ql)); 
    printf(" openmp: %f\n", mysumallatomic(rocks,moon,qr,ql)); 
    return(0); 
} 

Répondre

3
  1. L'utilisation du drapeau -Wall met en évidence les erreurs pragma. Par exemple, lorsque j'ai mal orthographié atomic, j'obtiens l'avertissement suivant.

    main.c:15: warning: ignoring #pragma omp atomic1

  2. Je suis sûr que vous le savez, mais juste au cas où, votre exemple doit être manipulé avec un reduction

  3. Lorsque vous utilisez parallèle OMP, la valeur par défaut est pour toutes les variables à partager . Ce n'est pas ce que vous voulez dans votre cas. Par exemple, chaque thread aura une valeur différente difx. Au lieu de cela, votre boucle doit être:

    #pragma omp parallel for default(none),\ 
    private(difx, dify, difz, mod2x, mod2y, mod2z, temp_sqrt, temp_div, i, j),\ 
    shared(rocks, moon, ql, qr), reduction(+:S2) 
    for(j=0; j<NRACK; j++){ 
        for(i=0; i<NSTARS;i++){ 
        difx=rocks[j][0]-moon[i][0]; 
        dify=rocks[j][1]-moon[i][1]; 
        difz=rocks[j][2]-moon[i][2]; 
        mod2x=difx*difx; 
        mod2y=dify*dify; 
        mod2z=difz*difz; 
        temp_sqrt=sqrt(mod2x+mod2y+mod2z); 
        temp_div=1/temp_sqrt; 
        S2 += ql[i]*temp_div*qr[j]; 
        } 
    } 
    
+0

salut, 2) oui, j'ai utilisé avant réduction, mais même problème! 3) donc, il semble que ni réduction ni aide atomique ici, que se passe-t-il? – flow

+0

@Werner: Quand j'utilise atomique, je reçois toujours la bonne réponse. Je vais ajouter mon code à ma réponse – csgillespie

+0

votre programme fonctionne efficacement. J'ai réalisé que mon erreur précédente était due au fait que "a" et "b" devraient être définis dans le parallèle pour, et pour cela nous devons utiliser le compilateur g ++. Avant je n'utilisais que gcc. Eh bien maintenant, j'ai modifié votre code et ajouté de nouvelles choses, comme vous pouvez le voir sur l'édition de la question, PS2, mais maintenant je reçois des résultats différents complétant g ++. Est-ce dû au fait que je fais trop d'opérations? – flow

0

En premier lieu, en fonction de la mise en œuvre, la réduction pourrait être mieux que d'utiliser atomique. Je voudrais essayer les deux et les chronométrer pour voir à coup sûr. Deuxièmement, si vous laissez l'atome, vous pouvez ou ne pouvez pas voir le problème (mauvais résultat) associé à la course. Tout est une question de timing, qui peut être très différent d'une course à l'autre. J'ai vu des cas où le résultat était erroné seulement une fois dans 150 000 courses et d'autres où il a eu tort tout le temps. Troisièmement, l'idée derrière pragmas était que l'utilisateur n'a pas besoin de savoir à leur sujet si elles n'ont pas d'effet. En plus de cela, la philosophie d'Unix (et de ses dérivés) est que c'est calme sauf s'il y a un problème. Dire cela, de nombreuses implémentations ont une sorte de drapeau afin que l'utilisateur puisse obtenir plus d'informations, car ils ne savaient pas ce qui se passait. Vous pouvez essayer -Wall avec gcc, et au moins il devrait marquer le pragma oh_my_god comme étant ignoré.

+0

oui, avec -Wall (désolé, j'ai complètement oublié), je peux voir maintenant quand le pragma oh_my_god donne une erreur. Avec un pragma atomique, il n'y a pas d'erreur, mais je me demande quelle est la garantie que le pragma atomique fonctionne vraiment, car il me semble qu'il est inutile – flow

0

Vous avez

#pragma omp parallel for shared(S2) 
    for(int a=0; a<128; a++){ 
    .... 

La seule parallélisation sera de la boucle.

Si vous voulez avoir le atomique ou la réduction vous devez faire

#pragma omp parallel 
{ 
#pragma omp for shared(S2) 
    for(int a=0; a<128; a++){ 
    for(int b=0; b<128;b++){ 
     double myterm = (double)a*b; 
     #pragma omp atomic 
     S2 += myterm; 
    } // end of second for 
    } // end of 1st for 
} // end of parallel code 
return S2; 
} // end of function 

Sinon, tout ce qui suit # sera commentaire

0

Je sais que c'est un ancien poste, mais je pense que le problème est l'ordre des paramètres de gcc, -fopenmp devrait être à la fin de la ligne de compilation.