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:
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:
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:
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:
#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:
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:
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:
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.