Voili voilou
Version imprimable
Voili voilou
par exempleCitation:
Envoyé par Charlemagne
Qu'en penses-tu ?Code:
1
2
3
4
5
6
7
8 double threadvalues = new double[ omp_get_num_threads_max()] #pragma omp for for( i=0 ; i<end-begin ; i++) { threadvalues[omp_get_thread_num()] = xxx; } // maintenant tu fais ta reduction sur les quelques elements = au nombre de processeurs
Astu essayé ICC 10 ? Apparemment, il y a une meilleure adéquation avec la bibliothèque de thread sous-jacente, y compris pour OpenMP - qui est utilisé dans MKL pour les FFT, avec une interface à la FFTW3 -
En ce qui concerne HT, il vaut mieux le désactiver dans ce genre de cas - même Intel l'indique dans sa doc maintenant :) -
Et je trouve aussi que limiter à un int le découpage pour OpenMP est débile, en fait, on ne peut même pas mettre un unsigned long, c'est dire !
Tu as testé/utilisé TBB jusqu'à quel point ?
Les benchs de Laurent sont cohérents avec les tout premiers qu'il a fait avec des gains de x1.3 à x1.8 selon les tailles de matrices, voir même x2 quand le cache est complètement saturé. Vos tests à tous n'ont pas été vains!
Les différences avec les tout premiers benchs sont:
-meilleur détections des processeurs présents
-relativement bon threshold single/multi thread pour éviter des coûts exorbitants de synchronisation.
-meilleur répartition de la charge sur les divers processeurs.
J'ai compilé les derniers benchs FFT avec l'option Ob2 et donc c'est plus lent sur l'Athlon qu'avec l'option Ob1 comme constaté précédemment (la raison est toujours un mystère pour moi). Pourtant l'option Ob2 a l'avantage d'être légèrement plus rapide sur les Pentiums et me donne des exécutables nettement moins volumineux. Ca va pas être un choix facile à prendre au final...
J'ai pas eu l'occasion, c'est encore en version beta non?Citation:
Envoyé par Miles
Et puis j'ai fais arrêter mon abonnement à ICL, car je trouve ICL9 très mauvais. ICL8.1 me donne de bien meilleur résultats.
Je sais pas ce qu'Intel avait modifié, mais y'a pas que du bon.
Quelle doc, t'as un lien?Citation:
Envoyé par Miles
Si tu sais comment désactiver (momentanément) l'hyper-threading ça m'intéresse.
Ca dépend du temps de calcul d'une itération, et puis 1 est la valeur par défaut. En fait c'est moi qui fait le découpage manuellement en morceau de taille 'grain'. J'ai pas trouvé mieux dans mon implémentation pour utiliser des itérateurs, et ça n'a visiblement pas d'incidence sur la rapidité.Citation:
Envoyé par Miles
J'avais la version beta pendant 7-8 mois. J'étais alors encore abonné à ICL et Intel me l'avait proposée.Citation:
Envoyé par Miles
Je l'ai testée un peu à l'époque (en particulier les fonctions PARALLEL_FOR et PARALLEL_REDUCE) et ça marchait bien, mais je trouvais la syntaxe un peu lourde. Je m'en suis inspiré pour mon implémentation, mais j'ai beaucoup simplifier la syntaxe (je trouve). Je pourrais si je voulais éventuellement encore l'utiliser en avançant la date de l'ordi car malheureusement TBB vérifie la date lors de l'exécution. (donc pas question de distribuer un exécutable).
Il est sorti au début du mois, je le teste un peu sous Linux - car gratuit pour une utilisation non commerciale -Citation:
Envoyé par Charlemagne
Je n'ai jamais pu tester ICC8, je suis directement passé au 9 qui était bon. J'ai tout de même des résultats parfois un peu moins bon avec le 10 qu'avec le 9.
Je n'ai pas de lien, c'est dans la doc de MKL.Citation:
Envoyé par Charlemagne
En revanche, à part le désactiver dans le bios, pas de salut. De toute manière, l'utiliser ralentit les perfs d'un code un tant soit peu optimisé :(
Pardon, je parlait du fait qu'on ne pouvait pas faire d'itération sur autre chose qu'un int ;)Citation:
Envoyé par Charlemagne
Lorsque j'ai tenté de parallélisé un code comme ça, j'ai forcé moi-même la taille d'un grain, par exemple pour du calcul patriciel, tu mets un 30, comme ça ça marche à partir de taille 30.
Je n'ia pas vu comment tu avais fait, mais j'ai constaté une sacré différence en modifiant l'ordre des boucles dans la multiplication matricielle, le problème étant de découper correctement les boucles pour maximiser le cache puisqu'il faut 2 boucles pour calculer complètement les valeurs à la place d'une seule dans l'implémentation "naïve".
Ah, c'est une bêta donc limité dans le temps, c'est ça ?Citation:
Envoyé par Charlemagne
Je n'ai pas exactement encore compris l'intérêt de cette bibliothèque, personnellement. Déjà, pour vraiment en tirer partie, il faut au moins un biproc ou un dual-core, on n'a pas toujours ça sous la main :(
Ok, on est d'accord. OpenMP est trop "C", pas assez "générique".Citation:
Envoyé par Miles
C'était bien une beta limitée dans le temps, mais je crois qu'Intel en est au moins à la version 1.1 maintenant.Citation:
Envoyé par Miles
Je suis d'accord aussi mais on pourrait en dire autant pour OpenMP.Citation:
Envoyé par Miles
D'où l'utilité d'avoir ouvert cette discussion :D
Je suis d'accord pour dire qu'avant de penser à l'optimisation multi-threads, y'a pleins de pistes à suivre. Avec la FFT l'optimisation du cache n'est malheureusement pas aussi facile que pour la multiplication matricielle, puique la FFT fait des accès randoms à la mémoire et pas sur des morceaux contigüs.Citation:
Lorsque j'ai tenté de parallélisé un code comme ça, j'ai forcé moi-même la taille d'un grain, par exemple pour du calcul patriciel, tu mets un 30, comme ça ça marche à partir de taille 30.
Je n'ia pas vu comment tu avais fait, mais j'ai constaté une sacré différence en modifiant l'ordre des boucles dans la multiplication matricielle, le problème étant de découper correctement les boucles pour maximiser le cache puisqu'il faut 2 boucles pour calculer complètement les valeurs à la place d'une seule dans l'implémentation "naïve".
Tôt ou tard je vais quand même multi-threader mon implémentation de multiplication matricielle.
Tu pourras comparer avec l'impélemntation d'Intel ;)Citation:
Envoyé par Charlemagne
je pense qu'on peut eviter ta section critique.
Aussi je ne vois pas pourquoi OpenMP aurait une latence, c'est bizarre.
J'aurais bien aimé comparer vraiment en essayant d'autres facons de mon coté ...
Je crois pas. D'une manière ou d'une autre il faut protéger la réduction qui écrit dans une variable partagée (la variable 'f').Citation:
je pense qu'on peut eviter ta section critique.
La réduction d'OpenMP contourne le problème en imposant d'utiliser une instruction atomique (instruction atomique=instruction spéciale de processeurs qui permet la lecture ou l'écriture concurrente sans avoir à recourir à un mutex.
Je trouve ça pas si bizarre. Et puis, que mon implémentation batte OpenMP sur les petites tailles n'est pas pour me déplaire! J'ai l'impression qu'OpenMP est légèrement plus rapide que moi pour les grandes dimensions mais vraiment pas de beaucoup.Citation:
Aussi je ne vois pas pourquoi OpenMP aurait une latence, c'est bizarre.
J'aurais bien aimé comparer vraiment en essayant d'autres facons de mon coté .
Il faudrait comparer avec ce que donne l'implémentation de VC2005 ou tout autre implémentation. Peut-être en utilisant le petit programme qui teste 'accumulate' plus haut en réduisant fortement la taille de vecteur (de l'ordre de l'unité ou de la dizaine), mais en augmentant le nombre de boucles pour garder un temps de mesure de l'ordre de la seconde.
je suis d'accord que la reduction avec openmp est limité.Citation:
Envoyé par Charlemagne
je pense aussi que ta section critique peut etre evitée en gardant les valeurs de chaque thread puis de faire la reduction manuellement (pas multithread) car ce serait sur une tableau de taille toute petite (nombre de processeurs)
;-) j'aime vraiment OpenMP c'est pour ca que je m'interesse beaucoup si il y a des eventuelles faiblesses, et de chercher la raison... Ton benchmark me passionne, la concurrence est un sujet qui me tient particulierement a coeur...Citation:
Envoyé par Charlemagne
je ne suis pas encore convaincu par les resultats alors je cherche avec toi
et en tous les cas j'aime bien le code que tu as fait et que j'ai pu voir.
merci pour ton travail. (je le teste sur un server de prod :mouarf:)
Une seule condition et j'y tiens: licence GPLCitation:
Envoyé par epsilon68
Je suis tres respectueux des licences et j'apprecie ton travail.Citation:
Envoyé par Charlemagne
Merci, et merci pour le compliment.
Y'a rien à faire: OpenMP (en tout cas l'implémentation d'ICL) est lente pour les petites boucles.
Le petit programme suivant teste la clause 'if' d'OpenMP. (Y'a eu quelques changements mineurs dans mon implémentation que tu trouveras ci-joint).
Code:
1
2
3
4
5 C : 0.828s OpenMP: 1.953s STL : 0.812s Ma lib: 0.828s
PS: J'ai changé le code ci-dessus, y'avait un problème dans les includes.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
81
82
83
84
85
86
87 #include <vector> #include <numeric> #include <iostream> #include <ctime> #include <omp.h> using namespace std; #define inline __forceinline #define MAX_THREADS 2 //#define OPENMP #include "threads.h" inline double get_time() { return double(clock())/CLOCKS_PER_SEC; } template<class V> struct fill_function { V val; fill_function(const V &v) : val(v) {} template<class RanIt> void operator()(RanIt begin, RanIt end) const { fill(begin,end,val); } }; template<class V> struct accumulate_function { typedef V result_type; mutable result_type val; accumulate_function() : val(0) {} void join(const accumulate_function &x) const { val+=x.val; } template<class It> void operator()(It begin, It end) const { val=accumulate(begin,end,val); } }; template<class RanIt,class T> T parallel_accumulate(RanIt begin, RanIt end, const T &val) { accumulate_function<T> func; gmt::parallel_reduce(begin,end,func,10000); return val+func.val; } int main() { typedef int value_type; vector<value_type> X(1000,1); value_type *pX=&X[0]; int imax=1000000; //fill(X.begin(),X.end(),1); //gmt::parallel_for(X.begin(),X.end(), fill_function<value_type>(1), 10000); double t0=0; value_type *psum = new value_type; // alloué dynamiquement pour tromper l'optimisation d'ICL t0=get_time(); for (int i=0; i<imax; ++i) { value_type sum=0; int jmax=X.size(); for (int j=0; j<jmax; ++j) sum+=pX[j]; *psum=sum; } cout << "C =" << *psum << ": " << get_time()-t0 << "s" << endl; t0=get_time(); for (int i=0; i<imax; ++i) { value_type sum=0; int jmax=X.size(); #pragma omp parallel for if (jmax>10000) schedule(dynamic,10000) reduction(+:sum) for (int j=0; j<jmax; ++j) sum+=pX[j]; *psum=sum; } cout << "OpenMP=" << *psum << ": " << get_time()-t0 << "s" << endl; t0=get_time(); for (int i=0; i<imax; ++i) *psum=accumulate(X.begin(),X.end(),0); cout << "STL =" << *psum << ": " << get_time()-t0 << "s" << endl; t0=get_time(); for (int i=0; i<imax; ++i) *psum=parallel_accumulate(X.begin(),X.end(),0); cout << "Ma lib=" << *psum << ": " << get_time()-t0 << "s" << endl; }
je n'obtiens pas du tout des resultats coherents...
si on pouvait maintenant changer d'une addition et appliquer une formule telle que cosinus ...
Tu peux essayer mais je crois pas que ça fasse une différence parce que le but ici c'est de mesurer le coût d'une condition de clause 'if' non remplie par rapport à une implémentation single-thread.
mais que ton implementation soit si rapide m'etonne vraiment ...
tu ne peux pas etre aussi rapide que le c quand meme .... tu as au minimum une legere penalité d'initialisation des threads ..
Tu fais appel a des std:accumulate dans ton implementation,
tu pourrais donner la fonction pour appliquer un cos par exemple ?
Faut se rendre à l'évidence:DCitation:
mais que ton implementation soit si rapide m'etonne vraiment ...
tu ne peux pas etre aussi rapide que le c quand meme .... tu as au minimum une legere penalité d'initialisation des threads ..
C'est négligeable pour mon implémentation vis-à-vis de la taille du vecteur de 1000. Pour une raison X ce n'est pas négligeable pour l'OpenMP de ICL. Je peux pas faire mieux: c'est exactement le même code dans le petit programme entre les tests "C" et "OpenMP".
Tu n'as pas de résultats cohérents sous Visual?
number of thread max: 4
C =1000: 0.89s
OpenMP=1000: 10.735s
et j'ai le code suivant (apres beaucoup de tests/modifs)
Je ne comprends pas le if openmp,
autant faire un if (en dessous d'une certaine taille) prendre l'implementation single thread
[EDIT] J'ai rien dit, je comprends
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 double t0=0; double *psum = new double; // alloué dynamiquement pour tromper l'optimisation d'ICL t0=get_time(); for (int i=0; i<imax; ++i) { double sum=0; for (int j=0; j<LEN; ++j) //sum+=cos(pX[j]); sum+=pX[j]; *psum=sum; } cout << "C =" << *psum << ": " << get_time()-t0 << "s" << endl; t0=get_time(); for (int i=0; i<imax; ++i) { double sum=0; #pragma omp parallel for reduction(+:sum) for (int j=0; j<LEN; ++j) //sum+=cos(pX[j]); sum+=pX[j]; *psum=sum; } cout << "OpenMP=" << *psum << ": " << get_time()-t0 << "s" << endl;
Ca sert à rien de le faire, ça n'aurait pour effet que de rallonger les temps de calcul et donc de rendre moins flagrant les temps de latence...Citation:
Tu fais appel a des std:accumulate dans ton implementation,
tu pourrais donner la fonction pour appliquer un cos par exemple ?
A priori ça revient au même mais c'est pas dans "l'esprit" d'OpenMP.Citation:
autant faire un if (en dessous d'une certaine taille) prendre l'implementation single thread
Faut pas croire que les standards sont toujours bien.
Pas terrible du tout pour l'implémentation OpenMP de Visual pour un code identique...Citation:
number of thread max: 4
C =1000: 0.89s
OpenMP=1000: 10.735s
attends j'avais enlevé le if
donc en mettant
j'obtiens:Code:#pragma omp parallel for if(LEN>10000) reduction(+:sum)
number of thread max: 4
C =1000: 0.875s
OpenMP=1000: 1.297s
t'as remarqué le temps d'exec en plus si on fait X[j] au lieu de pX[j] ?
(dans le test C et aussi openmp)
on explique ca comment ? que la STL est lente ?
ou finalement que le compilo fait plein d'arrangement et que si c'est le cas les tests de latence ne veulent plus rien dire ?
C'est plus correct comme temps d'exécution!
J'avais déjà remarqué, c'est bien pour ça que j'ai mis des pointeurs.Citation:
t'as remarqué le temps d'exec en plus si on fait X[j] au lieu de pX[j] ?
(dans le test C et aussi openmp)
on explique ca comment ? que la STL est lente ?
ou finalement que le compilo fait plein d'arrangement et que si c'est le cas les tests de latence ne veulent plus rien dire ?
Je pense que c'est un problème de fonctions non inlinées mais j'ai pas vérifié dans le code assembleur.
Y'a pas que ce petit programme qui confirme les temps de latence d'OpenMP, c'est ma FFT qui m'avait mise sur la voie et qui m'a fait faire ce test...
Pas la peine de chercher midi à 14 h, c'est bien ça l'explication.
Je me méfie énormement .... je pense que le compilateur fait beaucoup de choses derriere et la latence n'est plus mise en evidence ...Citation:
Envoyé par Charlemagne
Y-aurait-il un expert en assembleur qui pourrait nous renseigner ?
Je suis là plus pragmatique: peu importe comment le compilo se débrouille, OpenMP a une pénalité non négligeable pour les petites boucles.
Ma librairie aussi d'ailleurs mais nettement moins. J'essaye de faire pour le mieux.
Y'aura prochainement d'autres benchs (pour les matrices de réels), à priori lundi prochain.
... attendons les prochains benchs !
si un expert assembleur nous lis alors j'aimerais bien avoir son avis sur le code généré 8-)
Qu'appelles-tu petite boucle ?
Il faut avoir conscience du fait que les lignes de cache ont une taille de 64k, sur le P4. Du coup, sur une petite boucle (ne tenant pas par tranches dans la cache) alors on peut avoir un effet inverse avec openMP, car il va séparer en deux petites boucles pour chaque processeur (s'il y en a deux).
Casser le cache, c'est casser le pipeline, et donc perte de perf.
Faut que tu lances le petit programme de test plus haut et que tu fasses varier les tailles de vecteurs pour juger...
Je pense qu'à l'avenir je ne vais enclencher le multi-threading que sur des processeurs multi-cores. Les autres cas (hyper-threading et processeurs multiples) ne donnant pas des gains en vitesses appréciables, ils se justifient uniquement pour des programmes indépendants.
Voici le benchmark pour des matrices de nombres réels.
http://www.ient.rwth-aachen.de/team/...ads/Benchs.zip
C'est probablement le dernier Bench pour la FFT. Merci d'avance.
Tôt ou tard, j'aurais aussi des benchs à vous faire faire sur des multiplications de matrices.
Voilà pour moi. Quand aux résultats d'avant, je les avais fait, mais comme après tu as fourni un nouveau je ne les ai pas gardés.
Merci beaucoup.
Bon ça marche.
OpenMP est toujours plus lent pour les petites dimensions que mon implémentation. Il est néanmoins nettement plus rapide au démarrage du multi-threading pour la taille de matrice 128x128. Le gain est bon par la suite. C'est clair que ton processeur est celui avec le plus grand cache de tous ceux testés.
Seul gros bémol: ton ordi paraît vraiment lent par rapport aux autres et je ne comprends pas du tout pourquoi, vu que c'est un Core Duo (c'est bien la même chose qu'un "core 2 duo" ? ). C'est visiblement pas dû à mon programme puisque FFTW (qui me sert pour la comparaison) y est lent également.
voila:
Sur ton 2x Xeon HT, le multi-threading est moins bon que le single-thread!
Ca confirme ce que je disais: je ne vais autoriser le multi-threading que sur les processeurs multi-core.
L'inconvénient des systèmes avec plusieurs processeurs, c'est que rien ne dit que le 2ème thread sera sur le 2ème core du même processeur...
Bah ... il doit bien y avoir une fonction pour fixer l'affinité d'un thread, de la même manière que cela existe pour les processus ?Citation:
Envoyé par Charlemagne
[EDIT] Je viens de trouver la fonction Win32 "SetThreadAffinityMask()" ...
Sur XP, le scheduler commence par remplir de threads les différents coeurs physiques. Lorsque l'un des coeurs a deux threads logiques (un normal et un HT), le scheduler les remplit donc mal et il y a perte de vitesse. C'est pour ça que l'HT est si nul. Sous Linux, ça se passe mieux, le scheduler ne fait pas cette erreur.
Intéressant. Seulement la doc n'est pas vraiment claire, et parle d'un "vecteur de bits". Je ne sais pas quel bit représente quel processeur.Citation:
Envoyé par mchk0123
Sais-tu s'il y a une fonction équivalente pour la librairie pthreads? Je doute qu'elle existe, mais ça coûte rien de demander.
J'avais eu l'impression que c'était aléatoire: 2 benchmarks exécutés par Epsilon68 sur son "2x Xeon HT" avait donné des performances différentes. La première fois un certain gain, la 2ème fois une légère perte. J'avais interprété ça par le fait que le 2ème processus avait été attribué au 2ème processeur dans le premier bench, alors qu'il avit été attribué au processeur logique HT dans le second bench.Citation:
Envoyé par miles
Mais si vraiment XP réagit comme tu le dis, alors limiter le nombre de processus au nombre de cores n'est pas si mal, étant donné le faible gain de processeurs multiples.
Pas encore put regardé la documentation, mais je dirais bit 0 => core / proc 0 et bit 1 => core / proc 1 ... etcCitation:
Envoyé par Charlemagne
On m'avait demandé les sources. Voilà. J'ai mis en ligne la nouvelle version de ma bibliothèque avec la FFT 2D multi-threadée: http://www.ient.rwth-aachen.de/team/...al/genial.html
Si y'en a qui veulent tester la FFT multi-threadée (ou toute autre chose...), je conseille d'utiliser l'interface C du projet correspondant pour VC2005 compilé avec ICL (voir exemples dans projet sample2).
Je vais commencer à multi-threader la multiplication de matrices, j'ouvrirai une nouvelle discussion le moment venu pour de nouveaux benchmarks. Peut-être que certains parmi vous voudront même comparer avec leur propre implémentation.
Ce message pour vous dire que je viens d'ouvrir un topic pour faire des benchmarks de produit matriciel.
http://www.developpez.net/forums/sho...82#post2275482
Merci
hop je ré-ouvre un peu ce débat, je suis sur mon mac avec gcc 4.2
j'ai essayé de regarder un peu si Openmp etait aussi plus lent sur de petites boucles avec le code suivant qui est le meme que celui posté par Charlemagne:
et j'obtiens les resultats suivants: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 #include <vector> #include <numeric> #include <iostream> #include <ctime> #include <omp.h> using namespace std; inline double get_time() { return double(clock())/CLOCKS_PER_SEC; } int main() { typedef int value_type; vector<value_type> X(10000000,1); value_type *pX=&X[0]; double t0=0; value_type *psum = new value_type; // alloué dynamiquement pour tromper l'optimisation d'ICL t0=get_time(); for (int i=0; i<50; ++i) { value_type sum=0; int imax=X.size(); for (int i=0; i<imax; ++i) sum+=pX[i]; *psum=sum; } cout << "C =" << *psum << ": " << get_time()-t0 << "s" << endl; t0=get_time(); for (int j=0; j<50; ++j) { value_type sum=0; int imax=X.size(); //#pragma omp parallel for schedule(static) reduction(+:sum) #pragma omp parallel for schedule(dynamic,100000) reduction(+:sum) for (int i=0; i<imax; ++i) sum+=pX[i]; *psum=sum; } cout << "OpenMP=" << *psum << ": " << get_time()-t0 << "s" << endl; t0=get_time(); for (int i=0; i<50; ++i) *psum=accumulate(X.begin(),X.end(),0); cout << "STL =" << *psum << ": " << get_time()-t0 << "s" << endl; }
C =10000000: 2.29s
OpenMP=10000000: 2.52s
STL =10000000: 21.39s
je reve autant que vous ... la STL est-elle buggée ?
[EDIT]
en fait non, les resultats ci-dessus sont en mode debug .... :mouarf:
voici les resultats en mode release:
C =10000000: 0.89s
OpenMP=10000000: 0.78s
STL =10000000: 0.71s
Qu'en pensez-vous ? pas mal gcc 4.2 non ? sur macosx :aie:
config du mac:
2 GHz Intel Core Duo
[/EDIT]
Minute Papillon. Le code que je vois fait des grandes boucles. (sur des vecteurs de 10000000 éléments)
Ca me paraît à première vue pas terrible pour OpenMP. Il utilise 2 threads et il est a peine plus rapide que du C pure single-thread, et il est même toujours plus lent que la STL single-thread.
J'ai l'impression que les programmeurs de GCC doivent revoir leur copie...