• Des cpu multicore, du parallélisme

Des cœurs, mais pourquoi faire ?

Les optimisations vues précédemment avaient pour vue d'augmenter l'IPC (Instruction Par Cycle). Or l'hypothèse de Moore (prononcée en 1965 par Gordon E. Moore, un des fondateurs d'Intel) énonce que la densité des transistors intégrable dans un circuit électronique double tous les deux ans. Alors qu'elle que ne repose sur aucune démonstration, cette prédiction s'est révèlée étrangement véridique, malgré un ralentissement des performances vers les années 2010. Pourquoi ? Comme nous l'avons vu précédemment, il est difficile d'augmenter la fréquence pour gagner en performance, et les processeurs deviennent de plus en plus complexes, si bien qu'il n'existe plus (enfin, qu'on ne trouve plus) de nouvelles optimisations permettant de rendre nos chers bouzins plus rapides, malgré une surface en silicium toujours plus grande.

 

intel 4004 die

Schéma logique du premier CPU Intel, le 4004. La simplicité même : un unique cœur, pas de pipeline, pas de calcul flottant.

 

Intel 4004 : ses entrailles [cliquer pour agrandir]

Et pourtant, ses entrailles sont loin d'être simples ! (Schémas en provenance d'Intel)

 

Les fondeurs ont alors commencé à utiliser le gain de transistors induit par la conjoncture de Moore pour coller deux processeurs ensembles sur le même package. Il en résulte un CPU capable d'effectuer deux tâches simultanément : le premier dual-core était né !

Par la suite, on définit un cœur comme une partie d'un CPU capable de recevoir une liste d'instructions et de les exécuter les unes à la suite des autres.

 

Si, de nos jours, un CPU monocœur est tout à fait capable d'exécuter plusieurs applications en même temps, il ne s'agit que d'une illusion crée par l'OS. Sur Linux, une application dispose d'environ 50 μs pour effectuer ses opérations, puis est mise en pause par une partie du noyau nommée ordonnanceur, qui décide par la suite de la prochaine application à exécuter. Ainsi, on a l'impression que les applications tournent en même temps.

 

Hyper-threading, SMT ?

Sur les CPU actuels, on différencie les cœurs physiques des cœurs logiques. Cela est dû au fait qu'un CPU monocœur contient de nombreuses parties, vue précédemment, (unités de calcul entier, flottant, vectoriel, etc), mais que ces dernières ne sont jamais utilisées toutes en même temps. Il est alors possible de mutualiser les ressources : on parle alors de Simultaneous Multi-Threading (SMT), plus connu sous l'appellation commerciale d'Intel HyperThreading (HT). Son implémentation est simple : dans un cœur physique, tout est partagé sauf le pipeline qui est dupliqué ; en cas de conflit, une des deux instructions attend que l'unité utilisée se libère avant de l'utiliser à son tour.

 

Un i9, c'est couillu ! [cliquer pour agrandir]

Les i9 skylake (nommée i9-7XXX) proposent jusqu'à 18 cœurs hyperthreadés, soit 36 cœurs logiques, bien visibles sur son die. Le multicœur a de beaux jours devant lui !

 

A l'inverse, AMD avait tenté une architecture à base de modules avant Ryzen (qui lui utilise le SMT). Cela consistait en une fusion de deux cœurs, dont certaines unitées étaient partagées mais d'autre non. Si, théoriquement, un module possédait 90% de la puissance de deux cœurs, le processeur devenait également plus complexe, et la performance monocœur était en deça de la concurrence. Les applications nécessitaient d'être optimisées pour tirer correctement parti des modules AMD, là où les CPU utilisant l'hyperthreading étaient moins impactés par ces optimisations, mais proposaient des performances monocœurs bien meilleures. Et comme les jeux, même de nos jours, sont particulièrement sensibles aux performances monocœur, vous connaissez la sombre destinée des FX.

 

Synchronisation

Pour comprendre en quoi le multicœur nécessite des optimisations là où le pipeline ou le prédicteur de branchement n'en ont pas besoin, il faut revenir aux bases de la programmation. Et la programmation, c'est simplement une liste de courses effectuée par le CPU : les étapes sont effectuées les unes après les autres. Avec deux cœurs (et pire encore, avec 4 ou 8), il faut coordonner les calculs, s'échanger des valeurs entre cœurs, s'informer de l'état actuel des opérations... Le travail que vous pouvez faire en deux mois à vous seul ne se fait pas toujours à deux en un mois ! Partir d'un programme séquentiel (i.e. monocœur) pour le multi-threader n'est pas une chose aisée : il faut répartir la charge de travail efficacement entre plusieurs processus, qui travailleront plus ou moins en même temps à l'exécution : il convient donc de définir des points de synchronisation entre ces processus, notamment lors d'échange de données (ne serait-ce que pour attendre la fin du calcul avant de consulter la case mémoire correspondant au résultat !). Heureusement, le hardware offre des instructions dites atomiques qui garantissent qu'aucune autre opération n'interfèrent pendant leur exécution, ce qui permet les synchronisations de s'effectuer sans problème. Cependant, il faut faire extrêmement attention aux phénomènes de famine, c'est-à-dire lorsque plusieurs processus sont bloqués dans l'attente d'une même ressource.

 

Pour accélérer ces échanges, les extentions Intel TSX apportent le supporte des mémoires transactionnelles. Cette technologie permet, dans certains cas, de se passer de blocages inutiles en effectuant spéculativement les opérations sur la mémoire sans tenir compte des changements apportés par les autres cœurs. Les conflits (par exemple si deux variables sont modifiées par deux cœurs différents dans des zones non désirées), sont détectés automatiquement mais mènent à un surcoût en temps d'exécution : on ne risque pas de voir le TSX utilisé autre part que dans les bases de données avant un bon moment.

 



Les 24 Ragots
   
Les ragots sont actuellement
ouverts à tous, c'est open bar !