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++

  1. #21
    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
    Alors bonjour, je vous ai fait une fusion de mes en-êtes et d’un bout de mes sources un peu plus propre, normalement, et puis j’ai mis mon main avec le boost::timer de 3DArchi. J’ai simplifié le traitement de Pythagor qui rend pour le cas qui m’intéresse une grandeur facile à analyser à travers l’échantillonnage de grandeurs statistiques (la moyenne et d’un écart type. D’une variable aléatoire qui est tirée entre 0 et 1)
    Ca retrouve bien la moyenne (0.5), ça galère encore un peu pour la variance (1/12=0.083 normalement mais je vais regarder), ça galère surtout côté temps d’exécution:

    Grandeurs comparables en « mono thread » et « 1 thread » (0.5s)
    ça fait peur (?) dès qu’on met n>=2 threads: temps d’exécution fois 4!
    Mais la nouveauté ici c’est que pour 2, 5 ou 10 threads, le temps de calcul ne bouge pas Je vois pas ce qui m’échappe en même temps je trouve pas ça très normal. J’ai aussi fait un essai mutex mais ça ébranle pas le bestiau. Voilà le tou:

    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
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    typedef boost::mt19937 ref_generator_type;
    class Rand_Gen
    {
    	private:
    	ref_generator_type generator;	
    	boost::uniform_real<> uni_dist;
    	boost::variate_generator<ref_generator_type&, boost::uniform_real<> > uni;
    	public:
    	Rand_Gen()
    	:generator(42u),uni_dist(0,1),uni(generator,uni_dist)
    	{	}
    	double Next()
    	{
    		return uni();
    	}
    	vector<double> Skip(size_t  _start_idx,size_t _length)
    	{
    		vector<double> v;
    		size_t end_idx=_start_idx+_length;
    		for (size_t i=_start_idx;i<end_idx;++i)
    			v.push_back((*this).tempVec[i]);
    		return v;
    	}
    	void sampleVect(vector<double> &_v, size_t _n)
    	{
    		for(size_t i=0;i<_n;++i)
    			_v.push_back(Next());
    	}
    	vector<double> tempVec, vec;
    	void sampleVectAlea(size_t _n)
    	{
    		for (size_t i=0;i<_n;++i)
    			(*this).tempVec.push_back(Next());
    	}
    };
     
    struct Result
    {
    	Result()
    	:mean(0.0),var(0.0)
    	{}
    	double mean;
    	double var;
    };
     
    class Threaded_Sim_Class
    {
    	private: 
    	Rand_Gen gen;
    	size_t nbSimulations;
    	bool isMT;
    	struct MT_Data
    	{
    		Result mt_resStruct;
    		size_t nbSimulations;
    		Rand_Gen gen;
    	};
    	vector<boost::shared_ptr<MT_Data>> threadData;
    	size_t nbThreads;
    	public:
    	Threaded_Sim_Class(size_t _nbSimulations)
    	:isMT(false),nbSimulations(_nbSimulations),gen()
    	{}
    	Threaded_Sim_Class(size_t _nbSimulations,size_t _nbThreads)
    	:nbThreads(_nbThreads),threadData(_nbThreads),nbSimulations(_nbSimulations),gen()
    	{
    				isMT=true;
    	}
    	~Threaded_Sim_Class()
    	{}
    	double Pythagor(double _u)
    	{
    		return _u;
    	}
     
    	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
    		{
    			gen.sampleVectAlea(nbSimulations);//alea global
    			for(size_t i=0; i<nbThreads; i++)
    			{
    				threadData[i]=boost::shared_ptr<MT_Data>(new MT_Data);
    				threadData[i]->nbSimulations=nbSimulations/nbThreads;
    				gen.vec=gen.Skip(i*nbSimulations/nbThreads,nbSimulations/nbThreads);								
    				threadData[i]->gen.vec=(*this).gen.vec;
    			}
    			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]->mt_resStruct.mean;
    				res.var+=threadData[i]->mt_resStruct.var;
    			}
    			res.mean/=nbThreads;
    			res.var/=nbThreads;
    		}
    		return res;
    	}
    	boost::mutex m;
    	void getResult(size_t _j)
    	{
    		//m.lock();
    		boost::shared_ptr<MT_Data> td=threadData[_j]; 
    		size_t strace=td->nbSimulations;
    		double dtrace=0;
    		for(size_t i=0;i<td->nbSimulations;++i)
    		{
    			double tmp=Pythagor(td->gen.vec[i]);
    			td->mt_resStruct.mean+=tmp;
    			td->mt_resStruct.var+=tmp*tmp;
    		}
    		dtrace=td->mt_resStruct.var;
    		td->mt_resStruct.mean/=td->nbSimulations;
    		td->mt_resStruct.var=td->mt_resStruct.var/nbSimulations-(td->mt_resStruct.mean*td->mt_resStruct.mean); 
    		dtrace=td->mt_resStruct.var;
    	//m.unlock();
    	}
    };
     
    long main()
    {
    	size_t N=1000000, n=2;
    	{
    		Threaded_Sim_Class tscObj(N);
    		boost::timer t;
    		Result result=tscObj.getResult();
    		cout << "runtime monothread: " <<  t.elapsed() << " s" << "\n";
    		cout << result.mean << '\t' << result.var << '\n';
    	}
    	{
    		Threaded_Sim_Class tscObj(N,n);
    		boost::timer t;
    		Result result=tscObj.getResult();
    		cout << "runtime " << n << " thread(s): "<<  t.elapsed() << " s" << "\n";
    		cout << result.mean << '\t' << result.var << '\n';
    	}
    	cout << "\n";
    	return 1;
    }

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

    Informations forums :
    Inscription : Septembre 2006
    Messages : 2 965
    Par défaut
    le travail effectué par chaque thread est sans doute trop léger par rapport à l'overhead des pthreads…

  3. #23
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Ah ben voila, avec un peu de code c'est plus clair.
    Il y a deux raisons qui expliquent tes performances décevantes simong.

    1) Quelques maladresses dans l'implémentation de la version multi-thread plombent un peu tes perfs.

    2) Ton opération Pythagor+moyenne est intrinsèquement beaucoup trop légère pour être accéléré par du multi-threading. Deux addition et une multiplication, c'est pas assez pour espérer gagner quoi que ce soit.

    Par contre, si ton opération Pythagor est plus complexe, ça peut valoir le coup. Par exemple avec ta version on commence à voir des gains coté MT avec la fonction pythagore suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    double Pythagor(double _u)
    {
       return cos(cos(cos(cos(cos(cos(cos(_u)))))));
    }
    Et en Bonus
    Les threads bas niveau c'est has-been.
    Pour être awesome et épater vos amis, voici la version Intel TBB + C++0x!

    RandGen.h
    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
     
    #include <boost/random.hpp>
    #include <vector>
    typedef boost::mt19937 ref_generator_type;
     
    class RandGen
    {
    private:
       ref_generator_type generator;
       boost::uniform_real<> uni_dist;
       boost::variate_generator<ref_generator_type&, boost::uniform_real<> > uni;
    public:
       RandGen():
       generator(42u),uni_dist(0,1),uni(generator,uni_dist)
       { }
     
       double Next()
       {
    	return uni();
       }
     
       std::vector<double> sampleVectAlea(size_t _n)
       {
          std::vector<double> alea;
          alea.reserve(_n);
     
          for (size_t i = 0 ; i <_n ; ++i)
             alea.push_back(Next());
     
          return alea;
       }
    };
    main.cpp
    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
    82
    83
    84
    85
    86
    87
    88
     
    #include <tbb/tbb.h>
     
    #include "RandGen.h"
    #include <vector>
    #include <boost/timer.hpp>
     
    struct Result
    {
       Result():mean(0.0),var(0.0)	{}
       Result(double mean, double var):mean(mean), var(var){}
       double mean;
       double var;
    };
     
    Result operator+(const Result& r1, const Result& r2)
    {
       return Result(r1.mean + r2.mean, r1.var + r2.var);
    }
     
    double Pythagor(double _u)
    {
       return _u;
    }
     
    Result SingleThread_Sim(size_t nbSimulations)
    {
       RandGen gen;
       Result res;
     
       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);
       return res;
    }
     
    Result MultiThread_Sim(size_t nbSimulations)
    {
       RandGen gen;
       std::vector<double> alea = gen.sampleVectAlea(nbSimulations);
     
       Result res = tbb::parallel_reduce(  
          tbb::blocked_range<double*>(alea.data(), alea.data() + alea.size() ),
          Result(),
          [](const tbb::blocked_range<double*>& range, Result r) -> Result
          { 
             for( double* a = range.begin(); a != range.end(); ++a )  
    	 {
    	    double p = Pythagor(*a); 
    	    r.mean += p;
    	    r.var += p * p;
    	 }
             return r;
          },
          std::plus<Result>()
       );
     
       res.mean /= nbSimulations;
       res.var = res.var / nbSimulations - (res.mean * res.mean);
       return res;
    }
     
    int main()
    {
       size_t N = 100000000;
     
       {
          boost::timer t;
          Result result = SingleThread_Sim(N);
          std::cout << "runtime monothread: " << t.elapsed() << " s" << "\n";
          std::cout << result.mean << '\t' << result.var << '\n';
       }
     
       {
          boost::timer t;
          Result result = MultiThread_Sim(N);
          std::cout << "runtime multithread: "<< t.elapsed() << " s" << "\n";
          std::cout << result.mean << '\t' << result.var << '\n';
       }
     
       std::cout << "\n";
    }
    Avec cette version on obtient à peu près les mêmes timing entre la version mono et multi thread avec une fonction Pythagor vide. Des que Pythagor se complique un peu, le gain du MT apparait nettement.

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

    Informations forums :
    Inscription : Septembre 2006
    Messages : 2 965
    Par défaut
    Citation Envoyé par Arzar Voir le message
    Ah ben voila, avec un peu de code c'est plus clair.
    Il y a deux raisons qui expliquent tes performances décevantes simong.

    1) Quelques maladresses dans l'implémentation de la version multi-thread plombent un peu tes perfs.

    2) Ton opération Pythagor+moyenne est intrinsèquement beaucoup trop légère pour être accéléré par du multi-threading. Deux addition et une multiplication, c'est pas assez pour espérer gagner quoi que ce soit.



    Avec cette version on obtient à peu près les mêmes timing entre la version mono et multi thread avec une fonction Pythagor vide. Des que Pythagor se complique un peu, le gain du MT apparait nettement.
    on est donc 2 du même avis…
    pour compléter l'analyse voilà un benchmark en utilisant GCD (une autre librairie "light " de threading…) sur un 8 cœurs :

    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
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
     
    0.499926 0.0833593
    10000000 benchSimulator 1 threads: 23087635 ns 0.0231 sec
    0.499926 0.0833593
    10000000 benchSimulator 2 threads: 11480357 ns 0.0115 sec
    0.499926 0.0833593
    10000000 benchSimulator 4 threads: 8781267 ns 0.0088 sec
    0.499926 0.0833592
    10000000 benchSimulator 8 threads: 11653867 ns 0.0117 sec
    0.499926 0.0833592
    10000000 benchSimulator 16 threads: 5853897 ns 0.0059 sec
     
    0.499975 0.0833595
    20000000 benchSimulator 1 threads: 46323583 ns 0.0463 sec
    0.499975 0.0833595
    20000000 benchSimulator 2 threads: 32905020 ns 0.0329 sec
    0.499975 0.0833595
    20000000 benchSimulator 4 threads: 17016605 ns 0.0170 sec
    0.499975 0.0833595
    20000000 benchSimulator 8 threads: 14129530 ns 0.0141 sec
    0.499975 0.0833594
    20000000 benchSimulator 16 threads: 13027681 ns 0.0130 sec
     
    0.499963 0.0833506
    30000000 benchSimulator 1 threads: 68635326 ns 0.0686 sec
    0.499963 0.0833506
    30000000 benchSimulator 2 threads: 49441516 ns 0.0494 sec
    0.499963 0.0833506
    30000000 benchSimulator 4 threads: 25205901 ns 0.0252 sec
    0.499963 0.0833506
    30000000 benchSimulator 8 threads: 21313353 ns 0.0213 sec
    0.499963 0.0833505
    30000000 benchSimulator 16 threads: 22071225 ns 0.0221 sec
     
    0.49999 0.0833574
    40000000 benchSimulator 1 threads: 89597187 ns 0.0896 sec
    0.49999 0.0833574
    40000000 benchSimulator 2 threads: 59447328 ns 0.0594 sec
    0.49999 0.0833574
    40000000 benchSimulator 4 threads: 36500150 ns 0.0365 sec
    0.49999 0.0833574
    40000000 benchSimulator 8 threads: 22690614 ns 0.0227 sec
    0.49999 0.0833574
    40000000 benchSimulator 16 threads: 25736769 ns 0.0257 sec
     
    0.499984 0.0833481
    50000000 benchSimulator 1 threads: 111737589 ns 0.1117 sec
    0.499984 0.0833481
    50000000 benchSimulator 2 threads: 74131346 ns 0.0741 sec
    0.499984 0.0833481
    50000000 benchSimulator 4 threads: 45278567 ns 0.0453 sec
    0.499984 0.0833481
    50000000 benchSimulator 8 threads: 28661595 ns 0.0287 sec
    0.499984 0.0833481
    50000000 benchSimulator 16 threads: 30730253 ns 0.0307 sec
     
    0.499974 0.0833469
    60000000 benchSimulator 1 threads: 139367362 ns 0.1394 sec
    0.499974 0.0833469
    60000000 benchSimulator 2 threads: 98164232 ns 0.0982 sec
    0.499974 0.0833469
    60000000 benchSimulator 4 threads: 49928390 ns 0.0499 sec
    0.499974 0.0833469
    60000000 benchSimulator 8 threads: 35138936 ns 0.0351 sec
    0.499974 0.0833469
    60000000 benchSimulator 16 threads: 35932576 ns 0.0359 sec
     
    0.499984 0.0833443
    70000000 benchSimulator 1 threads: 164312297 ns 0.1643 sec
    0.499984 0.0833442
    70000000 benchSimulator 2 threads: 113705721 ns 0.1137 sec
    0.499984 0.0833442
    70000000 benchSimulator 4 threads: 59007812 ns 0.0590 sec
    0.499984 0.0833442
    70000000 benchSimulator 8 threads: 44368048 ns 0.0444 sec
    0.499984 0.0833442
    70000000 benchSimulator 16 threads: 36701681 ns 0.0367 sec
     
    0.5 0.0833414
    80000000 benchSimulator 1 threads: 184234482 ns 0.1842 sec
    0.5 0.0833414
    80000000 benchSimulator 2 threads: 128443534 ns 0.1284 sec
    0.5 0.0833414
    80000000 benchSimulator 4 threads: 67449588 ns 0.0674 sec
    0.5 0.0833414
    80000000 benchSimulator 8 threads: 44285955 ns 0.0443 sec
    0.5 0.0833414
    80000000 benchSimulator 16 threads: 42433204 ns 0.0424 sec
     
    0.499994 0.0833428
    90000000 benchSimulator 1 threads: 239967578 ns 0.2400 sec
    0.499994 0.0833428
    90000000 benchSimulator 2 threads: 144906967 ns 0.1449 sec
    0.499994 0.0833428
    90000000 benchSimulator 4 threads: 79507369 ns 0.0795 sec
    0.499994 0.0833427
    90000000 benchSimulator 8 threads: 55318992 ns 0.0553 sec
    0.499994 0.0833427
    90000000 benchSimulator 16 threads: 49335229 ns 0.0493 sec

  5. #25
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Citation Envoyé par JeitEmgie Voir le message
    on est donc 2 du même avis…
    pour compléter l'analyse voilà un benchmark en utilisant GCD (une autre librairie "light " de threading…) sur un 8 cœurs :
    Oui, mais tu benches quoi exactement là JeitEmgie ?
    Tes résultats laissent entendre un gros gain à chaque thread rajouté, mais, chez moi, avec le code original de simong - c'est à dire avec une fonction pythagor qui ne fait rien - le temps passé à calculer mean et var est presque négligeable par rapport au temps nécessaire à générer les nombres aléatoires. Donc le nombre de thread ne devrait pas jouer ?

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

    Informations forums :
    Inscription : Septembre 2006
    Messages : 2 965
    Par défaut
    Citation Envoyé par Arzar Voir le message
    Oui, mais tu benches quoi exactement là JeitEmgie ?
    Tes résultats laissent entendre un gros gain à chaque thread rajouté, mais, chez moi, avec le code original de simong - c'est à dire avec une fonction pythagor qui ne fait rien - le temps passé à calculer mean et var est presque négligeable par rapport au temps nécessaire à générer les nombres aléatoires. Donc le nombre de thread ne devrait pas jouer ?
    bonne remarque…

    le benchmark est sur la totalité du calcul… mais par paire de (nSimulations, nThreads)…
    et la génération des nombres aléatoires est faite une seule fois par valeur de nSimulations…

    si l'on inclut celle-ci au bench pour chaque paire (nSimulations, nThreads), alors l'amélioration de 1 thread à 8 threads est de l'ordre de 15 à 25% et négligeable de 8 à 16 threads…

    et évidemment cette génération représente presque 95% du temps de calcul…

    (et pour la petite histoire : je n'utilise pas le code original… j'ai utilisé ce sujet comme prétexte pour m'amuser avec GCD, par simple curiosité…)

    tout çà ne change rien aux conclusions :

    a. si les threads sont utilisés il faut que le calcul effectué par chaque thread en vaille la peine (un "gros" calcul CPU-bounded ou qu'il contienne un minimum d'IO…)
    b. utiliser des librairies à faible overhead par rapport aux pthreads repousse la limite de "légèreté" acceptable du calcul mais ne change rien au raisonnement de base
    c. on peut, sans crainte de se tromper beaucoup, ajouter que plus le calcul est léger plus le risque que ce soient les accès mémoire en écriture qui deviennent le bottleneck qui ralentit les threads est élevé (dans un cas où les résultats des calculs seraient stockés individuellement par exemple, mais ce n'est pas ce que nous avons ici… on lit beaucoup d'adresses mémoires différentes - les nombres aléatoires - mais on n'écrit que dans un nombre bien défini - les résultats locaux aux threads…)

    et dans cet exemple-ci :
    disposer d'un générateur de nombre aléatoire parallélisable apporterait un "énorme" gain…
    donc avoir une API à laquelle on demanderait de remplir le tableau et qui se chargerait de le faire en utilisant les threads (légers) en fonction de la taille de celui-ci…

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

    Informations forums :
    Inscription : Septembre 2006
    Messages : 2 965
    Par défaut
    J'en encore eu 5 minutes pour tester une librairie parallélisable de générateur de nombres aléatoires : http://math.asu.edu/~eubank/webpage/rngStreamPaper.pdf
    (j'ai remplacé OpenMP par GCD…)

    et le benchmark complet "génération et calcul" donne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    0.499926 0.0833593
    10000000 benchRngStream 1 threads: 229833484 ns 0.2298 sec
    0.499926 0.0833593
    10000000 benchRngStream 2 threads: 171874116 ns 0.1719 sec
    0.499926 0.0833593
    10000000 benchRngStream 4 threads: 169751145 ns 0.1698 sec
    0.499926 0.0833592
    10000000 benchRngStream 8 threads: 166911711 ns 0.1669 sec
    0.499926 0.0833591
    10000000 benchRngStream 16 threads: 159357028 ns 0.1594 sec

  8. #28
    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, un merci à tous. Arzar, je fixe maintenant les « points chauds » de mon source de départ, c’est clair. J'avais pas trop analysé la dimension « charge-CPU » et recherche du gain MT sous cet angle.
    Je vais peut-être pouvoir faire maintenant un essai avec TBB. Au passage tu entendais quoi exactement avec «threads bas niveaux »? JeitEmgie, je ne te cache pas que j'ai pas toujours percuté sur tous tes arguments (mon noviciat peut-être) mais je ne doute pas de la pertinence de ton argumentaire. Je ne manque pas de vous tenir au jus, Bien cool d’avoir pu avancer grâce à vous.

  9. #29
    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,
    Alors j'ai un peu regardé, pour TBB. Et voilà ce que j'ai :
    en gardant tout pareil par ailleurs (RanGen et Result) j'ai déporté dans une classe (My_Sim_Class) mes variables

    private :
    (double*) pAleaVect
    et
    (Result*) pRes;
    (Donc ce sont des pointeurs.)

    Comme méthodes de la classe, j'ai moi la surcharge d'opérateur:
    Public:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void operator() (const tbb::blocked_range<size_t> &r) ;
    //Qui fait :
    {
    Double tmp=0;
    for (size_t i=r.begin(); i!=r.end(); ++i) tmp+=pAleaVect[i];
    		pRes->mean=tmp;
    }
    Et ja'i :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    Result getResult_tbb(size_t _nbSimulations) qui fait :
    {
    Size_t blocked_range_size_=1000;
    tbb::parallel_for(tbb::blocked_range<size_t>(0, _nbSimulations,blocked_range_size_),
    		My_Sim_Class(pAleaVect,pRes)); 
    res_.mean=(*pRes).mean/(_nbSimulations/blocked_range_size_);
    		return res_;
    }
    Dans un main je fais:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    {
    size_t nbSimulations=100000;
    My_Sim simObj(aleaVectlea,res);
    res=simObj.getResult_tbb(nbSimulations);
    }
    Donc initialisation de mon vecteur d'alea, une instanciation et un appel du calcul carrément standards.

    J'ai deux questions:
    Comment le nombre de threads est ici passé? C'est TBB qui fait en fonction du rapport nbSimulations /blocked_range_size_?

    Ca compile et ça tourne, mais, manque de pot pour moi je trouve des résultats délirants pour mon calcul de "mean" alors que ça devrait être tout simple.

    Est-ce que j'aurais justement pas fauté avec ces histoires de priorité/attente de threads et les mutex du coup?
    Est-ce que quelqu'un a une piste ?
    Merci

  10. #30
    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
    Bonjour, je ne suis pas arrivé à faire tourner le source posté par Arzar (je comprends pas tout de ce qu'il y a dans les paramètres), et mon essai à moi de faire tourner TBB n'a pas trop donné (mais en ce qui me concerne je pense que c'est quelque chose de complè_complètement basique qui m'échappe, bref....), est-ce que vous auriez la possilbité de donner des indications sur ce qui vous paraîtrait pas OK? Ca serait juste pas mal de pouvoir tester ce qui peut être fait avec l'approche autre que Boost. A+ et merci.

  11. #31
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Tu n'as pas eu mon MP, simong ?
    Je le recopie au cas ou

    Citation Envoyé par Arzar
    Salut simong.
    Bon, j'ai quelques remords d'avoir parler de TBB sur le thread. Je n''ai malheureusement pas été assez clair mais TBB n'est *pas* l'approche que je conseillerais dans ton cas. C'était plus une petite gourmandise de ma part, pour s'amuser un peu avec TBB et le C++0x (la syntaxe en [] c'est pour les lambda de la prochaine norme)

    Non, pour ton cas, il est beaucoup plus indiqué d'utiliser openMP. C'est la solution standard pour le multi-threading et le calcul scientifique en C/C++ et fortran. Il est dispo sur presque tous les compilateurs de la planète (par exemple il suffit d'un include et d'un switch pour l'activer sur Visual C++) et surtout il est beaucoup, beaucoup plus simple à appréhender que TBB. OpenMP ne s'intéresse principalement qu'à la parallélisation de boucle for et ainsi qu'aux types basiques (int, float, double). Au final, il n'y a donc qu'une poignée de concept à comprendre et souvent peu de code à modifier. Découvrir et maitriser openMP, c'est l'affaire d'une demi-journée.

    Avec openMP, on obtient :

    Randgen.h
    Code C++ : 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
     
    #include <boost/random.hpp>
    typedef boost::mt19937 ref_generator_type;
     
    class RandGen
    {
    private:
       ref_generator_type generator;
       boost::uniform_real<> uni_dist;
       boost::variate_generator<ref_generator_type&, boost::uniform_real<> > uni;
    public:
    // nouveau constructeur qui prend la graine en paramètre	
       RandGen(boost::uint32_t seed): 
       generator(seed),uni_dist(0,1),uni(generator,uni_dist)
       { }
     
       RandGen():generator(42u),uni_dist(0,1),uni(generator,uni_dist)
       { }
     
       double Next()
       {
          return uni();
       }
    };

    main.cpp
    Code C++ : 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
     
    #include "RandGen.h"
    #include <omp.h>
    #include <ctime>
    #include <boost/timer.hpp>
     
    struct Result
    {
       Result():mean(0.0),var(0.0)	{}
       Result(double mean, double var):mean(mean), var(var){}
       double mean;
       double var;
    };
     
    double Pythagor(double _u)
    {
       return _u;
    }
     
    Result SingleThread_Sim(size_t nbSimulations)
    {
       RandGen gen;
       Result res;
     
       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);
       return res;
    }
     
    Result MultiThread_Sim(size_t nbSimulations)
    {
       double mean = 0.0;
       double var = 0.0;
     
      #pragma omp parallel 
      {
          RandGen gen(int(time(NULL)) ^ omp_get_thread_num());
     
          #pragma omp for reduction(+:mean), reduction(+:var)
          for(int i=0; i <nbSimulations; i++)
          {
             double tmp = Pythagor(gen.Next());
     
    	 mean += tmp;
    	 var += tmp*tmp;
          }
       }
     
       mean /= nbSimulations;
       var = var / nbSimulations - (mean * mean);
       return Result(mean, var);
    }
     
    int main()
    {
       size_t N = 100000000;
     
       {
          boost::timer t;
          Result result = SingleThread_Sim(N);
          std::cout << "runtime monothread: " << t.elapsed() << " s" << "\n";
          std::cout << result.mean << '\t' << result.var << '\n';
       }
     
       {
          boost::timer t;
          Result result = MultiThread_Sim(N);
          std::cout << "runtime multithread: "<< t.elapsed() << " s" << "\n";
          std::cout << result.mean << '\t' << result.var << '\n';
       }
     
       std::cout << "\n";
    }

    Détail du code openMP :

    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    double mean = 0.0;
    double var = 0.0;
    la directive reduction utilisée un peu plus loin n'est applicable que sur des types basiques, donc il faut temporairement laisser tomber la structure Resut et travailler directement sur les double mean et var.

    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    #pragma omp parallel
    Ce pragma indique le début d'une section parallèle, c'est à dire une section qui sera exécutée en parallèle sur plusieurs thread

    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     {
    RandGen gen(int(time(NULL)) ^ omp_get_thread_num());
    A l'intérieur du pragma parallel, une variable déclarée est locale à chaque thread. Donc, par exemple ici, sur un quad-core, on construirait quatre Randgen avec quatre graines légèrement différentes à chaque fois (en utilisant le numéro du thread pour faire varier la graine)

    La directive indiquant que la boucle qui suit doit être paralléllisée.

    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    reduction(+:mean), reduction(+:var)
    C'est la seule difficulté. La directive réduction (op : var) indique que dans la boucle for la variable "var" est locale à chaque thread, *mais*, tout à la fin, on appliquera automatiquement l'opération de réduction op sur la variable partagée de même nom. Par exemple sur un quad-core reduction (+:mean) signifie que quatre variables mean vont être créée, une pour chaque thread, et qu'à la fin de la boucle for, la variable mean partagée, cad déclarée avant la section #pragma parrallel va être mis à jour quatre fois, en faisant mean = mean + mean_thread1; mean = mean + mean_thread2 etc.

    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    for(int i=0; i <nbSimulations; i++)
    {
       double tmp = Pythagor(gen.Next());
     
       mean += tmp;
        var += tmp*tmp;
    }
    le reste est identique à la version monothread.

    Si tu veux vraiment rester avec la version TBB (qui est moins performante que le code OpenMP donné plus haut, car la version TBB précalcule un énorme vecteur de nombre aléatoire alors que le code OpenMP utilise N générateur aléatoire en parallèle, N étant le nombre de thread), j'avais jeté un coup d'oeil à ton code et il me semble qu'il était quasiment bon.
    Si je me souviens bien, la seule modif était de remplacer :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    res_.mean=(*pRes).mean/(_nbSimulations/blocked_range_size_);
    par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    res_.mean=(*pRes).mean/(_nbSimulations);
    Je ne comprends d'ailleurs pas du tout ce que viendrait faire le blocked_range_size dans le calcul final... blocked_range_size est utilisé par le parrallel_for pour découper l'intervalle [0; nbSimulation] en sous-intervalle de taille approximative blocked_range_size (car blocked_range_size n'est qu'une suggestion pour TBB, qui au final choisi la taille qui l'arrange). L'idée c'est que des threads peuvent ensuite parcourir les sous-intervalles en parallèle sans se marcher dessus. Mais une fois sortit du parrallel_for, blocked_range_size n'est plus d'aucune utilité, non ?

  12. #32
    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
    Citation Envoyé par Arzar Voir le message
    Je le recopie au cas ou
    Ca aurait été dommage qu'il soit le seul à en profiter
    Ceci dit, il me semble qu'OpenMP n'est pas disponible avec les versions Express de Visual, non ?

  13. #33
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    Ceci dit, il me semble qu'OpenMP n'est pas disponible avec les versions Express de Visual, non ?
    Euuu oui bon point.
    OpenMP n'est effectivement malheureusement pas disponible pour les versions express.

  14. #34
    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, merci beaucoup pour les détails. Sur ton MP je ne te cache pas que j'avais pas tout saisi et que partir sur open MP, j'avais regardé mais outch, ça me paraissait bien compliqué. Là avec tes explications c'est déjà bien plus accessible. Oui en effet pour mon code, j'avais pas nettoyé une ligne qui m'avait servi à tracer un résultat raison pour laquelle tu t'étonnes à juste titre de la présence de "blocked_range_size" en fin de mon traitement. Merci pour ta remarque en tout cas et en effet, pour l'erreur "basique" que je cherchais c'est juste que je ne lisais pas correctement ma structure de résultats, maintenant c'est tout bon. J'essaie de regarder pour le OpenMP. A+

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, 14h49
  2. Multithreads attendre la fin d'exécution
    Par Aure7780 dans le forum Langage
    Réponses: 4
    Dernier message: 27/04/2011, 13h09
  3. Réponses: 5
    Dernier message: 31/10/2008, 12h35
  4. [ArchiveBuilder][JavaMail] exécution impossible...
    Par Gorthal dans le forum JBuilder
    Réponses: 7
    Dernier message: 10/01/2003, 10h12
  5. Réponses: 2
    Dernier message: 06/07/2002, 13h36

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