Je suis nouveau sur openmp et j'essaie actuellement de mettre en parallèle la multiplication de matrice dans xcode sur mac.
Le résultat que j'obtiens est bizarre car il augmente le temps de mon programme au lieu de le réduire. je suppose que cela se produit car il n'utilise qu'un seul noyau et n'utilise pas d'autres cœurs voici mon code:
omp_set_num_threads(4); #pragma omp parallel for private(i,j,k) for (i=0; i<n; ++i) { for (j=0; j<n; ++j) { for (k=0; k<n; ++k) { c[i][j] += a[i][k] * b[k][j]; } } }
sur deux matrices 400 * 400 avec 1 thread, j'obtiens un 551 ms, avec 2 threads 421 et avec 3 threads 678 et cela augmente à mesure que j'augmente mes threads.
des idées: qu'est-ce que je fais mal ou que dois-je faire?!
3 Réponses :
des idées ce que je fais mal ou que dois-je faire?!
Il ne semble pas que vous fassiez quelque chose de mal en ce qui concerne votre code. Cependant, le multithreading entraîne une surcharge, à la fois au niveau logiciel et au niveau matériel. Par conséquent, appliquer plus de threads à un problème n'accélère pas toujours le calcul global et peut le ralentir. La façon dont cela affecte une tâche particulière dépend des détails de cette tâche et de l'architecture et de l'environnement de l'hôte.
Néanmoins, considérez ce programme de test complet construit autour de votre exemple de code:
#include <stdlib.h> int main() { double a[400][400], b[400][400], c[400][400] = { { 0.0 } }; int i, j, k, n = 400; srand(time(NULL)); for (i = 0; i < n; ++i) { for (j = 0; j < n; ++j) { a[i][j] = rand() / (double) RAND_MAX; b[i][j] = rand() / (double) RAND_MAX; } } #pragma omp parallel for private(i,j,k) num_threads(4) for (i=0; i<n; ++i) { for (j=0; j<n; ++j) { for (k=0; k<n; ++k) { c[i][j] += a[i][k] * b[k][j]; } } } return EXIT_SUCCESS; }
Vous utilisez une méthode mauvaise pour multiplier vos matrices. L'algorithme ijk génère de nombreux échecs de cache. Regardez votre boucle intérieure. Chaque fois que votre index k change, vous accédez à une nouvelle ligne de la matrice b
au lieu d'utiliser un parcours convivial de cache le long d'une ligne. Et ce grand nombre de cache manque réduit vos performances et est plus mauvais pour le code parallèle en raison des algorithmes de cohérence du cache. L'algorithme ikj (voir code ci-dessous) est bien meilleur. Les matrices sont toutes traversées en ligne majeure et ne génèrent pas d'erreurs de cache.
J'ai essayé d'expérimenter votre code.
Pour avoir un timing régulier, je chronomètre 10 boucles de multiplication matricielle, et je le fais 10 fois et je garde le temps le plus bas.
En fonction des définitions, on peut choisir soit ijk ou ikj et contrôlez le parallélisme.
Un autre définit la version parallèle ou séquentielle.
am@Mandel$ cc -fopenmp -O3 -march=native -DTHREADS=0 -Uijk omp2.c; ./a.out 0.114659 am@Mandel$ cc -fopenmp -O3 -march=native -DTHREADS=4 -Uijk omp2.c; ./a.out 0.06113
Maintenant les expériences:
D'abord avec ijk
am@Mandel$ cc -fopenmp -O3 -march=native -DTHREADS=0 -Dijk omp2.c; ./a.out 0.196313 am@Mandel$ cc -fopenmp -O3 -march=native -DTHREADS=4 -Dijk omp2.c; ./a.out 0.293023
Et nous voyons que le parallèle La version est environ 50% plus lente.
Nous passons maintenant à ikj
#include <stdio.h> #include <omp.h> #include <stdlib.h> int main() { double a[400][400], b[400][400], c[400][400] = { { 0.0 } }; int i, j, k, n = 400; double t1, t2,t; srand(100); // better be deterministic when benchmarking for (i = 0; i < n; ++i) { for (j = 0; j < n; ++j) { a[i][j] = rand() / (double) RAND_MAX; b[i][j] = rand() / (double) RAND_MAX; } } t=1E100; for(int ll=0;ll<10;ll++){ t1 = omp_get_wtime(); for(int mm=0;mm<10;mm++){ #if THREADS>1 #pragma omp parallel for private(i,j,k) num_threads(THREADS) #endif #ifdef ijk for (i=0; i<n; ++i) { for (j=0; j<n; ++j) { for (k=0; k<n; ++k) { c[i][j] += a[i][k] * b[k][j]; } } } #else // ikj matrix multiplication for (i=0; i<n; ++i) { for (k=0; k<n; ++k) { double r=a[i][k]; for (j=0; j<n; ++j) { c[i][j] += r * b[k][j]; } } } #endif } t2 = omp_get_wtime(); if (t>t2-t1) t=t2-t1; } printf("%g\n",t); // to fool these smart optimizers, do something with c FILE* devnull=fopen("/dev/null","w"); fprintf(devnull,"%g\n",c[0][0]); return EXIT_SUCCESS; }
Le code séquentiel est ~ deux fois plus rapide et la version parallèle est ~ deux fois plus rapide que le séquentiel.
Probablement avec des matrices plus grandes, vous pouvez améliorer l'efficacité du code parallèle.
sonne comme ça a résolu mon problème merci. J'ai aussi utilisé pour calculer le temps en utilisant la fonction clock_t tStart = clock () mais il semble que omp_get_wtime () donne une réponse meilleure et plus précise des idées pourquoi?
Excellente réponse. (Observation mineure: si vous vouliez des produits matriciels réels, par exemple pour vérifier les calculs, vous voudriez évidemment réinitialiser c chaque itération, mais ce n'est pas vraiment important pour la question sur les performances.)
Votre grande erreur est d'essayer de paralléliser la multiplication matricielle. Non pas parce que c'est impossible, mais parce que cela a déjà été fait (avec d'autres optimisations importantes telles que le blocage du cache et la vectorisation, auxquelles vous ne pourrez probablement pas vous déplacer).
Souvenez-vous de la phrase clé: "Le meilleur code est le code que je n'ai pas à écrire" :-)
Donc, à moins que votre objectif ne soit de vous éduquer, arrêtez de faire cela. Au lieu de cela, trouvez une bonne bibliothèque BLAS et passez votre temps à apprendre à l'utiliser.
( Intel MKL est bon et gratuit pour tout le monde, c'est donc un choix raisonnable, mais il existe de nombreuses autres options que Google peut trouver pour vous).
Divulgation complète: je travaille pour Intel, mais pas sur MKL.
oui, comme tu l'as dit, j'essaie de m'éduquer et c'est en fait un devoir pour les algorithmes parallèles que j'ai mais merci pour le conseil :)
Ah, OK, donc ce n'est pas de ta faute. La prochaine fois, il vaudrait la peine de mentionner cela d'avance ...
Synchronisation probablement
Il y a des rendements décroissants et finalement négatifs pour une parallélisation excessive. Trouvez le sweet spot.
Possibilité de duplication de C ++ OpenMP fonctionnant très lentement sur un produit matrice-vecteur < / a>
Vous ne semblez pas faire une distinction claire entre les threads et les cœurs. Combien de cœurs (physiques) votre machine fournit-elle réellement? Il est peu probable qu'il soit utile d'utiliser plus de threads que de cœurs sur lesquels les exécuter.
mon ordinateur portable a 4 cœurs mais plus de deux threads commencent à augmenter le temps plutôt qu'à l'abaisser @JohnBollinger
a essayé de réchauffer les caches comme il est dit dans le sujet que vous avez mentionné mais il semble que ce ne soit pas le cas pour moi @HighPerformanceMark
Comment chronométrez-vous cela?
Et avez-vous expérimenté des problèmes de taille beaucoup plus grande? De plus, ne vous attendez pas à ce que les hyperthreads partageant un seul cœur donnent comme par magie le même type de performances que plusieurs cœurs. Généralement, pour de nombreuses charges de travail HPC telles que la multiplication matricielle, les hyperthreads n'apportent aucun avantage, souvent un ralentissement comme vous semblez l'avoir observé.