IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Threads & Processus C++ Discussion :

Pb OpenMP avec fonction + boucles


Sujet :

Threads & Processus C++

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Novembre 2007
    Messages : 15
    Par défaut Pb OpenMP avec fonction + boucles
    Bonjour,

    Je viens vers vous, afin d'avoir des conseilles sur OpenMP.
    Dans mon code C, je parallélise une partie (voir partie très simplifiée ci-dessous):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    int k=0;
    #pragma omp parallel for
     
    for (int i = 0 ; i < 10  ; ++i)
        for (int l = 0 ; l < 10  ; ++l) {
     
            if (_p[k]==0)         funct_1(i,k);
            else if (_p[k]==1)   funct_2(i,k);
     
            ++k;
        }
    Il se trouve qu'en rajoutant la ligne (#pragma omp parallel for), mes résultats sont complètement faux. Je ne maîtrise pas encore toutes les les fonctionnalités d'OpenMP.
    Dois je rajouter d'autres mots-clés ?
    A quoi cela sert de privatiser ou de les partager des variables entre les threads ?
    J'ai lu la documentation plusieurs fois, mais je ne comprends pas l'intérêt. Pouvez-vous me l'expliquer ?

    Je vous remercie pour vos éventuelles réponses
    Syens

  2. #2
    Membre Expert
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Par défaut
    que font tes fonctions ?

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Novembre 2007
    Messages : 15
    Par défaut
    Bonsoir,

    Mes fonctions réalisent des opérations très basiques, en voici un exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    static inline void funct_1 (int i, int k) {
     
        dn = x(i) - position_mur_x - rayon(i);
     
        F(k) = Kn1 * dn;
     
        f_part(i) += F(k);
        MurY0.f -= F(k);
    }
    Merci
    Syens

  4. #4
    Membre confirmé
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Avril 2010
    Messages
    26
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Vaucluse (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : Avril 2010
    Messages : 26
    Par défaut
    Hey,

    Tu as des accés concurrents sur la variable f_part(i) ce qui peut-etre la source de tes résultats faux. Il faut spécifier que cette variable est shared.
    F(k) peut probablement être accédé par plusieurs threads en meme temps.

    J'écrirais plutot ca:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
     
     
    #pragma omp parallel for private(i,k) shared(f_part,F)
     
    for (int i = 0 ; i < 10 ; ++i)
    for (int k= 0 ; k< 10 ; ++k) {
     
    if (_p[k]==0)
    {
    dn = x(i) - position_mur_x - rayon(i);
     
    F(k) = Kn1 * dn;
     
    f_part(i) += F(k);
    MurY0.f -= F(k);
    }
    else if (_p[k]==1){
    dn = x(i) - position_mur_x - rayon(i);
     
    F(k) = Kn1 * dn;
     
    f_part(i) += F(k);
    MurY0.f -= F(k);
    }
     
     
    }

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Novembre 2007
    Messages : 15
    Par défaut
    Merci beaucoup de ta réponse, elle m'a donnée pas mal de pistes.
    Il faut savoir que les variables globales (f_part,F) ne peuvent pas être facilement partagées. Donc j'ai choisi l'option default(shared).

    Ensuite mon code tourne parfaitement qd je parallélise uniquement la 2ième boucle et que je fais un omp critical pour mes fonctions:


    !! Attention !! j'ai posté mes vraies boucles pour plus de compréhension du code. J'ai ajouté les variables: nbreVoisin et voisin
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    int k=0;
     
    for (int i = 0 ; i < 10  ; ++i)
    #pragma omp parallel for default(shared)
        for (int l = 0 ; l < nbreVoisin[i]  ; ++l) {
    #pragma omp critical
    {    
            if (_p[k]==0)         funct_1(i,voisin[k]);
            else if (_p[k]==1)   funct_2(i,voisin[k]);
     
            ++k;
    }
        }
    J'aimerais bien savoir pourquoi je ne peux pas appliquer la parallélisation sur la première boucle. Si quelqu'un a une suggestion à faire, je suis preneur.
    Merci
    Syens

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Novembre 2007
    Messages : 15
    Par défaut
    Bonjour,

    En utilisant le #pragma omp critical, la parallélisation du code n'est pas optimale. Comment puis-je partager des variables globales qui sont au sein des fonctions (e.g. la variable f_part) ?

    Merci
    JF

  7. #7
    Membre confirmé
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Avril 2010
    Messages
    26
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Vaucluse (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : Avril 2010
    Messages : 26
    Par défaut
    Tu as trop de variables partagées. Il faut repenser ton code
    Ca explique pourquoi 1 tu ne peux pas paralléliser la première boucle et obtenir des résultats correctes.

    Pour les variables globales partagées normalement ca doit marcher, mais je ne suis pas sur (j'utilise jamais de variable globale).

    Citation Envoyé par Syens Voir le message
    J'aimerais bien savoir pourquoi je ne peux pas appliquer la parallélisation sur la première boucle. Si quelqu'un a une suggestion à faire, je suis preneur.


    Merci
    Syens

    Après réflexion, peux tu expliquer que fais ton code ? Ca permettra d'y voir plus clair et de proposer une solution adaptée.

    Pour les perfs avec ton code actuel, voilà une voie plus saine pour les boucles internes:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
     
     
    int k=0;
    int tmp=0;
     
    #pragma omp parallel 
    {
     
       for (int i = 0 ; i < 10  ; ++i)
       {
    #pragma omp for private(i,l,k) reduction(+:tmp)
          for (int l = 0 ; l < nbreVoisin[i]  ; ++l)
          {   
            if (_p[k+l]==0)        tmp = tmp + funct_1(i,voisin[k+l]);
            else if (_p[k+l]==1)   tmp = tmp + funct_2(i,voisin[k+l]);
     
           }
            f_part(i)+=tmp;
            k+=nbreVoisin[i]-1;
         }
       }
    }
     
     
     
    int funct_1 (int i, int k) {
     
    dn = x(i) - position_mur_x - rayon(i);
     
    F(k) = Kn1 * dn;
     
    MurY0.f -= F(k);
    return F(k);
    }
    Déclarer une section parallel permet d'éviter de recréer les threads.
    Regarde si ce code fonctionne, ca devrait améliorer certaines choses.

    La variable k n'est plus partagée et est devenu virtuellement privée pour chaque thread. Tu fais un count global donc autant ajouter ca à la fin du parallélisme. J'ai modifié également ta fonction de manière à ce que la variable f_part ne soit plus partagée. J'utilise la variable tmp sur laquelle est fait une sommation parallel (reduction) et le résultat est ensuite mit dans f_part(i). De toute f_part(i) n'est pas accèder en parallèle. Je crois ne pas avoir foiré. f_part(i) n'était pas utilisé autre part dans ta fonction.

    ok, donnes nous ton feedback la dessus.

  8. #8
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Novembre 2007
    Messages : 15
    Par défaut
    Bonjour,

    Je te remercie de m'avoir apporté de l'aide sur mon bout de code et tu as raison le code n'est pas adapté.
    Donc je l'ai modifié un peu et voici une version un peu plus détaillée comme tu me l'as demandé.


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    #pragma omp parallel for private(i)
    for (i = 0; i < nbreInteractions ; ++i) {
    #pragma omp critical
        {
            if (_p[i]==0)       funct_1(part[i],Voisin[i],i);  // Interaction entre deux particules
            else if (_p[i]==1)  funct_2(part[i],Voisin[i],i);  // Interaction entre un mur et une particule
        }
     
    }
     
    //%%%%%%%%%  DEFINITION DES 2 FONCTIONS %%%%%%%%%
     
    static inline void funct_1 (int i, int j, int k) {
     
        dn = distance_part(i,j)- (rayon[i]+rayon[j]);  // calcul valeur d'interpenetration
        Fn[k] = Kn2 * dn;                               // calcul de la force normale
     
        fn_par[i] += Fn[k];
        fn_par[j] -= Fn[k];
    //============================================
        dt = distance_tang(i,j);
        Ft[k] = Kt * dt;                                 // calcul de la force tangentielle
     
        ft_par[i] += Ft[k];
        ft_par[j] -= Ft[k];
    }
     
     
    static inline void funct_2 (int i, int j, int k) {
     
        dn = x[i] - position_mur[j] - rayon[i];  // calcul valeur d'interpenetration
        Fn[k] = Kn1 * dn;                         // calcul de la force normale
     
        fn_par[i] += Fn[k];
        fn_mur[i] -= Fn[k];
    }
    // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    Mon problème est de pouvoir partager les variables globales (fn_par, ft_par, fn_mur).
    Le compilo refuse de les partager, car il ne les voit pas comme étant déclarées .

    De plus j'ai besoin, comme tu le vois de retourner plus d'une valeur à la fin de mes fonctions.
    Je pense ne pas être le premier à vouloir faire ce genre de chose !!??

    Ps: est-ce que les fonctions static inine posent problème à OpenMP ?


    Merci de votre aide, je continue mes recherches
    Jf

  9. #9
    Membre confirmé
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Avril 2010
    Messages
    26
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Vaucluse (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : Avril 2010
    Messages : 26
    Par défaut
    En faisant une lecture rapide de ton update.
    Ca change complétement tout ! Tes accès sont indéterminés à cause de tes tableaux contenant des valeurs non prédictibles.

    Je regarde ca plus tard et te réponds au plus vite.

    Y a til une logique dans tes tableaux voisin et part ?

    Concernant le inline et static, je ne sais pas si openmp n'aime pas. N'ayant jamais testé , j'ai préféré les exclure pour inviter d'inclure des éléments dont on ne maitrise pas l'effet.

    Si ma proposition de code est correcte, peux tu la tester pour voir si cela améliore quelque chose ?

    Merci

  10. #10
    Membre Expert
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Par défaut
    Citation Envoyé par plumedesiles Voir le message
    Déclarer une section parallel permet d'éviter de recréer les threads.
    openMP ne recrée jamais de threads. Le compilo examine le code pour déterminer le nombre de threads max, les crée au demarrage de l'appli et les reveille/endort en fonction des besoins.

  11. #11
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Novembre 2007
    Messages : 15
    Par défaut
    Salut,

    J'ai donc remplacé mes fonctions static inline void par void. Du coup mes résultats de sortis sont enfin des chiffres et non plus des "nan".

    Donc OpenMP n'aime pas les fonctions static inline !!!!

    J'ai toujours le problème sur mes variables globales (fn_par[N], ft_par[N], fn_mur[N]) car elles ne sont pas partagées par les différents threads.

    Donc la solution est de créer des variables locales fn_par'[N1], ft_par'[N1], fn_mur'[N1], avec N1 la taille du tableau qui est égale à:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     N1=N*omp_get_num_threads().
    Ensuite je ferai :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    #pragma omp for private(i,j)
    for(i=0; i<N;++i)
     for(j=0; j<omp_get_num_threads();++j)
    {
       fn_par[N] += fn_par'[i+N*j];
       ft_par[N] += ft_par'[i+N*j];
       fn_mur[N] += fn_mur'[i+N*j];
    }
    Pas simple à comprendre, mais c'est la seule solution (enfin je pense !!!) pour que les threads n'écrivent pas en même temps dans les variables globales et pour que je puisse avoir la bonne valeur à la fin.

    Pas sûre que je gagne tant de temps que cela, à voir !!! En tout cas, mille mercis à toi pour t'être plongé sur mon problème.

    Jf

  12. #12
    Membre confirmé
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Avril 2010
    Messages
    26
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Vaucluse (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : Avril 2010
    Messages : 26
    Par défaut
    Citation Envoyé par Joel F Voir le message
    openMP ne recrée jamais de threads. Le compilo examine le code pour déterminer le nombre de threads max, les crée au demarrage de l'appli et les reveille/endort en fonction des besoins.
    Dans toutes les documentations que j'ai lu, il n'a jamais eu de mention à un pool de threads réutilisables. OpenMP fonctionne sur un modèle fork join.
    Je profite également de l'occasion pour corriger le code que j'ai proposé en supprimant sections. (voir post précédent)

    Slide 22

    Slide 6

    Tutorial de l'Université de Berkeley, voir slide 12

  13. #13
    Membre Expert
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Par défaut
    Citation Envoyé par plumedesiles Voir le message
    Dans toutes les documentations que j'ai lu, il n'a jamais eu de mention à un pool de threads réutilisables. OpenMP fonctionne sur un modèle fork join.
    Je connais le cours de Mark Snir, je le donne à la fac.
    Modéle d'exécution != modéle d'implantation.

    Suffit de regarder le code généré

  14. #14
    Membre confirmé
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Avril 2010
    Messages
    26
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Vaucluse (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : Avril 2010
    Messages : 26
    Par défaut
    Citation Envoyé par Joel F Voir le message
    Je connais le cours de Mark Snir, je le donne à la fac.
    Modéle d'exécution != modéle d'implantation.

    Suffit de regarder le code généré
    Oui je suis d'accord avec toi, mais je n'ai jamais eu la certitude que ce pool soit créé au début de l'application, excepté pour icc. Si tu as un pointeur concernant GCC, ca serait inéressant.

  15. #15
    Membre averti
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    31
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 31
    Par défaut
    Salut, en lisant vos différentes contributions au sujet de la parallélisation sur le forum, et en procédant moi-même à des tests d'implémentation, je me dis finalement que j'ai encore bien de la difficulté à reconnaître d’un coup d’œil les variables qui vont être "accédées" par plusieurs threads. Pour l'instant, je ne vois que les variables globales. Quels-sont vos conseils pour faire une analyse des variables partagées du code? Et dans OMP, à quoi cela va servir au compilo de savoir qu’une variable est « shared » ? Quelle va être la différence d’avec la directive « reduction » que l’on place dans certaines boucles for ? Merci de vos réponses.

Discussions similaires

  1. Fonction boucle avec condition
    Par gaetanos dans le forum Macros et VBA Excel
    Réponses: 3
    Dernier message: 21/05/2015, 15h12
  2. [Toutes versions] Modifier la formule d'une colonne en fonction d'une autre avec une boucle
    Par captaincss dans le forum Macros et VBA Excel
    Réponses: 3
    Dernier message: 01/07/2014, 18h20
  3. probleme de boucle avec fonction et appel
    Par Invité dans le forum Langage
    Réponses: 2
    Dernier message: 26/05/2011, 15h37
  4. Boucle avec fonction
    Par croset dans le forum R
    Réponses: 4
    Dernier message: 14/06/2010, 20h59
  5. fonction avec la boucle while
    Par hanou88 dans le forum C
    Réponses: 4
    Dernier message: 04/12/2009, 03h42

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo