Dans cet article nous testons la performance de la parallélisation et du placement des processus sur la routine SGEMM de la MKL dans sa version multithreadée.
ATTENTION - Cet article date de 2014, les tests ont été effectués sur Eos (le supercalculateur précédent Olympe).
Nous traitons ici différents cas, puisque nous faisons varier aussi bien la taille de la matrice carrée (5000, 10000, 25000 et 50000 lignes/colonnes), que le nombre de cores sur lequel est lancé ce calcul : 1 core (=sequentiel), 5 cores, 10 cores, 20 cores, 40 cores (=hyperthreading).
Dans l’environnement par défaut
Squelette du script testé dans l’environnement par défaut :
On s’intéresse au speed up de ce calcul, à savoir le ratio du temps de calcul en séquentiel (=sur 1 core) sur le temps de calcul en parallèle sur n cores :
Sp=Tséquentiel/Tparallèle |
Plus ce ratio se rapproche du nombre de cores utilisés pour le calcul parallèle, plus l’efficacité parallèle de ce code est bonne. Par exemple : si avec 10 cores on va dix fois plus vite qu’en séquentiel, alors le speed up vaut 10 et il est donc idéal.
Pour l’exemple traité ici, le speed up a été calculé sur la base des temps de restitution ci-dessous :
TEMPS DE | Nombre de lignes/colonnes de la matrice | SPEED UP | Nombre de lignes/colonnes de la matrice | ||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
RESTITUTION | 5000 | 10000 | 25000 | 50000 | ASSOCIE | 5000 | 10000 | 25000 | 50000 | ||
Nombre de cores | 1 | 6,54 | 47,53 | 738,05 | 5886 | Nombre de cores | 1 | / | / | / | / |
5 | 2,11 | 10,50 | 149,85 | 1190 | 5 | 3,11 | 4,53 | 4,93 | 4,95 | ||
10 | 0,85 | 5,47 | 75,36 | 598 | 10 | 7,74 | 8,69 | 9,79 | 9,84 | ||
20 | 0,96 | 5,12 | 61,81 | 444 | 20 | 6,79 | 9,29 | 11,94 | 13,25 | ||
40 | 1,39 | 4,32 | 52,17 | 410 | 40 | 4,69 | 11,01 | 14,15 | 14,34 |
Courbe de scalabilité (ou courbe d’extensibilité) : courbe affichant le speed up en fonction du nombre de cores sur lequel est lancé le calcul Ci-contre la courbe de scalabilité associée au code calculant le résultat d’un multiplication de deux matrices carrées non vides de différentes tailles (5000, 10000, 25000 et 50000 lignes/colonnes). On peut voir que pour 5 cores (et 10 cores respectivement), quelque soit la taille du problème, le speed up est très bon puisqu’il est très proche de 5 (et 10 respectivement). Par contre pour le problème de taille 5000, augmenter encore le nombre de cores sur lequel est lancé le calcul dégrade le speed up. La scalabilité dépend donc de la taille du problème étudié. |
|
En plaçant la variable : OMP_PROC_BIND=true
Cette variable assure que lorsqu’une tâche est lancée sur un cpu, elle reste bien sur celui-ci. De plus, elle répartie les tâches uniquement sur les cœurs physiques, et dans l’ordre. Ainsi pour un nombre de tâches égal à 10, les threads occuperont les cœurs de 0 à 9.
Les temps de restitution que nous obtenons alors sont les suivants :
TEMPS DE | Nombre de lignes/colonnes de la matrice | GAIN | Nombre de lignes/colonnes de la matrice | ||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
RESTITUTION | 5000 | 10000 | 25000 | 50000 | RELATIF | 5000 | 10000 | 25000 | 50000 | ||
Nombre de cores | 1 | 6,39 | 47,98 | 737,37 | 5910 | Nombre de cores | 1 | 2% | -1% | 0% | 0% |
5 | 1,92 | 9,99 | 149,43 | 1191 | 5 | 9% | 5% | 0% | 0% | ||
10 | 1,40 | 5,52 | 75,51 | 595 | 10 | -65% | -1% | 0% | 1% | ||
20 | 0,85 | 3,56 | 38,60 | 302 | 20 | 12% | 30% | 38% | 32% | ||
40 | 0,76 | 4,21 | 63,47 | 533 | 40 | 45% | 2% | -22% | -30% |
Pour le problème de petite taille (n=m=5000), les temps de restitutions sont très faibles (<2s) et varient d’un job à un autre, ce qui empêche de tirer une conclusion claire.
Pour les problèmes de plus grande taille (n=m=25000 et 50000), on voit que lorsqu’on utilise moins de la moitié du nœud (nombre de cœurs < ou = à 10), sans spécifier de placement particulier, les tâches se répartissent "naturellement" sur des cores différents. Lorsqu’on les place avec l’option OMP_PROC_BIND
, cette répartition est plus "propre", mais ne change pas les temps de calcul.
Par contre lorsqu’on utilise la totalité du nœud sans spécifier de placement, certaines tâches vont se placer sur des cores physiques et leur core logique associé, créant de l’hyperthreading. Avec l’option OMP_PROC_BIND toutes les tâches sont placées sur les cores physiques, on gagne systématiquement 30% de temps de calcul (pour cet exemple) et on améliore grandement le speed up. Pour les problèmes de grande taille, il est presque idéal : lorsqu’on fait tourner un job sur x cores, il va x fois plus vite qu’en séquentiel (sauf en hyperthreading).
SPEED UP | Nombre de lignes/colonnes de la matrice | ||||
---|---|---|---|---|---|
ASSOCIE | 5000 | 10000 | 25000 | 50000 | |
Nombre de cores | 1 | / | / | / | / |
5 | 3,33 | 4,80 | 4,93 | 4,96 | |
10 | 4,57 | 8,69 | 9,76 | 9,94 | |
20 | 7,51 | 13,49 | 19,10 | 19,56 | |
40 | 8,36 | 11,40 | 11,62 | 11,09 |
- 1/ En pointillés : speed up précédent
2/ En traits pleins : speed up avec la variable OMP_PROC_BIND=true
3/ 40 cores = 20 cores physiques + 20 cores logiques (hyperthreadés)