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 :

Exécution ralentie en multithread


Sujet :

Threads & Processus C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    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 Exécution ralentie en multithread
    Bonjour à tous,
    je compile et j'exécute un proto de code C++ multithreadé (je m'appuie sur boost::thread). Mon problème est qu'alors que je m'attendais au global à une réduction du temps de d'exécution de mon programme, celui-ci autmente avec le nombre de threads que je crée ! (4 threads sur un quadricore). Quelqu'un a t-il un avis ? Merci

  2. #2
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Salut,
    Tu accèdes à une ressource système ou partagée qui nécessite synchronisation (allocation dynamique ?) ?

  3. #3
    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,
    Non pas l'impression et pas de ressource partagée en tout cas :
    l'idée est à la base, simplement de balancer en parallèle un traitement (boucle) qui s'exécute en local, sur les processeurs. Mon algo est un random simulator, je souhaite réaliser N simuls, faire à terme en sorte que mon traitement soit réparti sur les n proc. De la machine. Il y aurait ruse particulière? Merci à toi.

  4. #4
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    C'est possible de voir le code parallélisé ?
    Tu peux aussi avoir des impacts de perfs selon la façon dont tu accèdes aux données (pb de cache)

  5. #5
    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
    t'utilise quoi comme pRNG ? rand() ? si oui rand contient des données statiques en interne qui emepche son usage propre en MT. y a rand_r sinon.

  6. #6
    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,
    On peut effectivement constater des impacts « à la marge) en fonction de comment on adresse les données, j'ai vu ça.
    Je n’utilise pas le Rand native de C++, mais boost
    La classe que je teste en gros:

    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
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    class Threaded_Sim_Class
    {
    	private : 
    	Rand_Gen	gen;
    	size_t nbSimulations;
    	bool isMT;
    	struct MT_Data
    	{
    		Rand_Gen gen;
    		size_t nbSimulations;
    		Result result;
    					};
    	vector<boost::shared_ptr<MT_Data>> threadData;
    	size_t nbThreads;
    public :
    	Threaded_Sim_Class(size_t _nbSimulations) //monothreading
    		:isMT(false),nbSimulations(_nbSimulations),gen()
    	{	}
    	Threaded_Sim_Class(size_t _nbSimulations,size_t _nbThreads) //multithread si _nbThreads>1
    	:nbThreads(_nbThreads),threadData(_nbThreads),nbSimulations(_nbSimulations),gen()
    	{
    		isMT=(nbThreads>1) ? true : false;
    	}
    	~Threaded_Sim_Class()
    	{}
     
    	double Pythagor(double _u)
    	{
    		return...;
    	}
     
    	Result getResult()
    	{
    		Result res;
    		if(!isMT)
    		{
    			for(size_t i=0; i<nbSimulations; ++i)
    			{
    				double tmp=Pythagor(gen.Next());
    				res.mean+=tmp;
    				res.var+=tmp*tmp;
    			}
    			res.mean/=nbSimulations;	res.var=res.var/nbSimulations-(res.mean*res.mean); 
    		}
    		else
    		{
    			for(size_t i=0; i<nbThreads; ++i)
    			{
    				threadData[i]=boost::shared_ptr<MT_Data>(new MT_Data);
    				threadData[i]->nbSimulations=nbSimulations/nbThreads;
    				threadData[i]->gen.skip(2*i* (nbSimulations/nbThreads));
    			}
    			boost::thread_group tg;
    			for(size_t i=0;i<nbThreads;++i)
    			{
    	tg.create_thread(boost::bind(&Threaded_Sim_Class::getResult,this,i));
    			}
    			tg.join_all();
    			for(size_t i=0;i<nbThreads;++i)
    			{
    				res.mean += threadData[i]->result.mean;
    				//res.var +=threadData[i]->result.var;
    			}
    			res.mean/=nbThreads;
    		}
    		return res;
    	}
     
    	void getResult(size_t _iThread)
    	{
    		boost::shared_ptr<MT_Data> td=threadData[_iThread]; //tempo
    		for(size_t i=0; i<td->nbSimulations; ++i)
    		{
    			double tmp=Pythagor(td->gen.Next(),td->gen.Next());
    			td->result.mean += tmp;
    			td->result.var +=tmp*tmp;
    		}
    		td->result.mean/=td->nbSimulations;
    		td->result.var=td->result.var/nbSimulations-(td->result.mean*td->result.mean); 
    	}
    	};
    J'exécute getResult() dans mon main. Je peux comparer des calculs en monothread et sur plusieurs threads. Mon temps d'exé croît avec le nombre de threads! Je comrpends pas!!!

  7. #7
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    On va avancer à tâtons....
    A quoi ressemble Pythagore ?
    Quel est le volume de donnée (combien de boucles ) ? Et quel est le temps en mono thread vs multi thread.
    Créés 1 thread de plus que le nb de core (5) et regarde si ça impacte.

  8. #8
    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, sans problème:
    Pythagor : mesure un écart quadratique dans l'idée: return (u*u-M*M), où u tirée aléatoirement avant, est passé à la fonction. Rien comme traitement supplémentaire à l’intérieur.
    Volume de données: 10000000 de tirages
    Mon impact 9.75s en mono (et 9.5 quand je construis un objet paramétré de façon explicite avec n=1 thread, pour comparer), puis 10.1s sur les 4, 14.7.7 sur 5, 18.7s sur 6, 22.9 sur 7, 26.6s sur 8…
    Alors, à la base pas une différence folle entre mono et 4threads mais c’est déjà clairement pas le résultat auquel je me serais attendu. Et puis après, c’est carrément la misère (sans compter que ça a pas l'air très linéaire en impact, tout ça)!

  9. #9
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Il n'y aurait pas un mutex sur ton générateur aléatoire ?
    Une telle perte de perf me fait quand même penser que les threads se synchronisent ou s'excluent mutuellement régulièrement. Bref, il y a de l'attente quelque part.
    Identifie tous tes appels systèmes (y compris allocation, vecteur, liste, flux, trace, etc.) et tous tes appels à des fonctions de bibliothèques tierces.

  10. #10
    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,
    Non aucun mutex nulle part pour l'instant. D'ailleurs il faudra p't^t que je regarde où en mettre c'est vrai... Ma classe de génération aléatoire implémente boost::random, et dans mon main je fais que construire un objet de type Threaded_Sim_Class sur lequel j'appelle la méthode getResult! Je fais aucune autre allocation ou ne traite aucun autre vecteur que ceux que tu vois...
    => "Identifie tous tes appels systèmes (y compris allocation, vecteur, liste, flux, trace, etc.) et tous tes appels à des fonctions de bibliothèques tierces.": Ce serait quoi les précautions à prendre avec ces appels système ?

    => quand tu parles des threads "qui se synchronisent ou s'excluent mutuellement régulièrement", ce serait à rechercher où où dans mon programme???? A+ et merci

  11. #11
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Salut,
    Prenons le problème autrement : instrumente chacun de tes threads de façon de plus en plus fine pour voir où le temps est consommé (boost.timer peut t'aider je pense pour mesurer le temps écoulé). Une fois que tu as identifié ce qui prend du temps, tu auras probablement la réponse à ton problème

  12. #12
    Membre Expert
    Homme Profil pro
    Inscrit en
    Septembre 2006
    Messages
    2 963
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Septembre 2006
    Messages : 2 963
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    Salut,
    Prenons le problème autrement : instrumente chacun de tes threads de façon de plus en plus fine pour voir où le temps est consommé (boost.timer peut t'aider je pense pour mesurer le temps écoulé). Une fois que tu as identifié ce qui prend du temps, tu auras probablement la réponse à ton problème
    la lecture du code source permet quand même de déduire quelques petits choses :

    a. si c'est un problème de lock caché ce ne peut être que dans
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    tg.create_thread(boost::bind(&Threaded_Sim_Class::getResult,this,i));
    boost::bind ne devant pas jouer directement avec des mutex, create_thread est le suspect, mais ce serait quand même surprenant… à moins qu'il ne crée un mutex sur l'objet passé en paramètre et que l'expansion de boost::bind provoque une situation où l'objet serait toujours le même… mais on rentre dans des hypothèses du genre "bizarre"… éventuellement un effet de bord d'une optimisation du compilateur…
    …mais çà reste peu probable… de plus le comportement de l'augmentation du temps de calcul serait plus linéaire…
    (encore que pour être certain de cela… il faudrait savoir exactement ce que vous avez mesuré et comment …)

    b. si ce n'est pas un problème de mutex lié au threading, alors le suspect principal est :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    threadData[i]->gen.skip(2*i* (nbSimulations/nbThreads));
    que fait skip() et comment le fait-il ?


    PS
    petit détail : si le nombre de simulations n'est pas un multiple du nombre de threads le code n'exécutera pas le nombre de simulations espérés… (vous perdez nSimulations modulo nThreads)


    NB
    si skip() fait "while (toBeSkipped--) next();" alors c'est bien lui le coupable :

    avec skip "enabled"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    benchSimulator  1 threads:  168595199 ns
    benchSimulator  2 threads:  529154506 ns
    benchSimulator  4 threads:  547437236 ns
    benchSimulator 10 threads: 1308146160 ns
    benchSimulator 20 threads: 2578868055 ns
    avec skip "disabled"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    benchSimulator  1 threads: 171873820 ns
    benchSimulator  2 threads: 466571113 ns
    benchSimulator  4 threads: 242895564 ns
    benchSimulator 10 threads: 151853600 ns
    benchSimulator 20 threads: 146165826 ns

  13. #13
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Salut,
    @JeitEmgie : le cout de création de thread existe, mais pas au point de passer de 10s à 14s avec 5 threads (ou alors boost.thread est à jeter, ce que je ne crois pas).
    Pour gen.skip, il n'est pas présenté. J'ai pensé qu'il s'agissait simplement d'une segmentation de son espace de valeurs selon le nombre de threads. Mais, je n'avais pas pensé que cette fonction pouvait être 'lourde'. C'est vrai que le sur-coût n'est peut être pas dans les fonctions exécutées dans le thread mais dans le travail de préparation avant. En tout cas, je pense qu'instrumenter un peu pour voir où se consomme le temps est peut être une façon rapide d'identifier le ou les lignes problématiques.

  14. #14
    Membre Expert
    Homme Profil pro
    Inscrit en
    Septembre 2006
    Messages
    2 963
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Septembre 2006
    Messages : 2 963
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    Salut,
    @JeitEmgie : le cout de création de thread existe, mais pas au point de passer de 10s à 14s avec 5 threads (ou alors boost.thread est à jeter, ce que je ne crois pas).
    Pour gen.skip, il n'est pas présenté. J'ai pensé qu'il s'agissait simplement d'une segmentation de son espace de valeurs selon le nombre de threads. Mais, je n'avais pas pensé que cette fonction pouvait être 'lourde'. C'est vrai que le sur-coût n'est peut être pas dans les fonctions exécutées dans le thread mais dans le travail de préparation avant. En tout cas, je pense qu'instrumenter un peu pour voir où se consomme le temps est peut être une façon rapide d'identifier le ou les lignes problématiques.
    c'est bien pour cette raison que je parie plus sur le skip()...
    les générateurs de nombre aléatoires ne sont pas des fonctions auxquelles ils suffit de passer un index N pour avoir le Nième élément...
    la génération du Nième dépend en général des N-1 précédents...
    donc çà n'aurait rien d'étonnant à ce qu'il fasse une boucle appelant next()... et dans ce cas inutile d'instrumenter... il est évident que le problème vient de là... d'autant plus que pour chaque "gen" il recalcule certainement depuis le début...

    si c'est çà, la solution est de générer les nombre aléatoires avant l'étape du splitsing du travail en threads et de les stocker dans un tableau, chaque accédant en lecture au segment qui le concerne (donc pas de mutex nécessaire...)

    ou de changer de générateur de nombre aléatoire (les digits de PI par exemple...) …

    en utilisant un tableau contenant les nombres pré-calculés :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    benchSimulator  1 threads:  70493335 ns
    benchSimulator  2 threads: 110037032 ns
    benchSimulator  4 threads:  76292946 ns
    benchSimulator 10 threads:  62239087 ns
    benchSimulator 20 threads:  51807857 ns
    (PS ns = nanosecondes... autrement dit l'ensemble des benchmarks va plus vite que ce qu'il a mesuré comme son temps le plus rapide... :
    real 0m0.730s
    user 0m1.927s
    sys 0m0.106s

    d'où l'interrogation de ce qui a été mesuré et comment... et l'intérêt d'en savoir un peu plus sur le code qui n'a pas été montré...)

Discussions similaires

  1. [IDE] Exécution ralentie quand RAD Studio n'est pas lancé
    Par kurul1 dans le forum C++Builder
    Réponses: 13
    Dernier message: 27/11/2013, 13h49
  2. Multithreads attendre la fin d'exécution
    Par Aure7780 dans le forum Langage
    Réponses: 4
    Dernier message: 27/04/2011, 12h09
  3. Réponses: 5
    Dernier message: 31/10/2008, 11h35
  4. [ArchiveBuilder][JavaMail] exécution impossible...
    Par Gorthal dans le forum JBuilder
    Réponses: 7
    Dernier message: 10/01/2003, 09h12
  5. Réponses: 2
    Dernier message: 06/07/2002, 12h36

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