Intel Core 2 Duo
Publié le 22/06/2006 par Franck Delattre et Marc Prieur
Les branchementsLes branchements constituent, après les accès mémoire, le second plus important facteur de ralentissement dans le fonctionnement du processeur en cas de mauvaise prédiction.
Un branchement consiste, dans un flux d’instructions, en un saut vers une nouvelle adresse dans le code. Deux types de branchements existent :
Qu’ils soient directs ou indirects, les branchements constituent un obstacle dans le fonctionnement optimal du pipeline de traitement. Dès lors que l’instruction de saut entre dans le pipeline, celui-ci ne peut, en théorie, plus accueillir de nouvelle instruction tant que l’adresse de destination n’est pas calculée, c’est-à-dire lorsque l’instruction de saut atteint les dernières étapes de traitement. Le pipeline insère alors des bulles, ce qui nuit fortement à son rendement. Le rôle du prédicateur de branchement est donc d’essayer de deviner l’adresse de destination, afin que les instructions suivant le saut soient chargées sans tarder.Les branchements directs pour lesquels l’adresse du saut est explicitement mentionnée dans le code, sous la forme d’une opérande. L’adresse de destination est résolue lors de la compilation. Les branchements directs sont dans la plupart des cas des sauts de boucle. Les branchements indirects sautent vers une adresse qui varie dynamiquement au cours de l’exécution du programme, les destinations possibles sont donc multiples. On les retrouve par exemple dans les tests de type « switch / case », et sont également souvent utilisés dans les langages orientés objets, sous forme de pointeurs de fonctions.
Il n’existe en fait pas un mais plusieurs prédicateurs. Le plus simple et le plus ancien est le prédicateur statique, dont le fonctionnement repose sur l’assertion que la branche sera toujours prise ou à l’inverse jamais prise. Ainsi, dans une boucle, le mécanisme statique prédit correctement tous les sauts, sauf le dernier ! Son taux de succès dépend bien entendu du nombre d’itérations.
Le prédicateur statique montre ses limites dans les branchements de type si … alors … sinon, pour lesquels il a une chance sur deux de se tromper. Le processeur a alors recours à une prédiction dynamique, qui consiste à stocker un historique des résultats des branchements dans une table (la BHT : branch history table). Lorsqu’une branche est rencontrée, la BHT stocke le résultat du saut, et si la branche est prise l’adresse de destination est stockée dans un buffer dédié, le BTB (branch target buffer) (si la branche n’est pas prise aucune adresse n’est évidemment stockée, car la destination est alors l’instruction suivant la branche). Deux types de prédicateurs dynamiques existent au sein d’un processeur, qui se différencient par la portée des branches dont ils stockent l’historique, et ce afin d’améliorer la granularité du mécanisme de prédiction.
L’action combinée des prédicateurs statiques et dynamiques offrent, selon la taille des buffers de stockage, un taux de succès de la prédiction entre 95 et 97% sur les branches directes. En revanche leur efficacité tombe à environ 75% de prédictions correctes sur les branches indirectes, qui, du fait de la multiplicité des destinations possibles, ne sont pas adaptées au stockage de l’information binaire « pris / non pris » des BHT. Mobile a ainsi inauguré un mécanisme de prédiction de branchements indirects. Le prédicateur stocke dans le BTB les différentes adresses auxquelles le branchement a abouti, ainsi que le contexte qui a conduit à cette destination (c’est-à-dire les conditions qui ont accompagné le saut). La décision du prédicateur n’est donc plus limitée à une adresse en cas de branche prise, mais en une série de destinations « préférées » de la branche indirecte. La méthode donne de bons résultats mais est très consommatrice de ressources, le BTB contenant alors plusieurs adresses par branche.
Mobile a également introduit une technique innovante appelée « détecteur de boucle ». Ce détecteur scrute les branches à la recherche du schéma typique de fonctionnement d’une boucle : toutes les branches prises sauf une (ou l’inverse, selon la condition de sortie). Si ce schéma est détecté, une série de compteurs est affectée au branchement concerné, assurant ainsi un taux de succès de 100%.
Core bénéficie bien entendu de tous ces raffinements, en plus de petites améliorations diverses, mais sur lesquelles aucune information n’a filtré.
Les mécanismes de fusionCore comporte un certain nombre de techniques qui visent, pour un nombre d’instructions donné, à réduire le nombre de micro-opérations générées. Effectuer la même tâche avec moins de micro-opérations, cela signifie la faire plus rapidement (augmentation de l’IPC) tout en consommant moins d’énergie (augmentation de la performance par watt consommé).
Initialement introduite sur Mobile, la micro-fusion est l’une de ces techniques. Voyons en quoi elle consiste sur un exemple, l’instruction x86 : add eax,[mem32].
Cette instruction effectue en réalité deux opérations distinctes : une lecture mémoire, et une addition. Elle sera ainsi décodée en deux micro-opérations :
load reg1,[mem32]Cette décomposition suit également la logique de l’organisation du processeur : la lecture et l’addition sont prises en charge par deux unités différentes. Dans un schéma classique, les deux micro-opérations seront traitées dans le pipeline, le moteur OOO s’assurant de gérer les dépendances.
add reg2,reg1
La micro-fusion consiste dans notre cas en l’existence d’une « super » micro-opération se substituant aux deux précédentes, en l’occurrence :
add reg1,[mem32]Ce n’est ainsi plus qu’une unique micro-instruction qui traversera le pipeline de traitement. Lors de l’exécution proprement dite, une logique dédiée à la gestion de cette micro-opération sollicitera de façon parallèle les deux unités concernées. La méthode présente en outre l’intérêt de nécessiter moins de ressources (un seul registre interne est désormais nécessaire dans notre exemple).
Core ajoute à cette technique celle de la macro-fusion. Là où la micro-fusion transforme deux micro-opérations en une seule, la macro-fusion décode deux instructions x86 en une seule micro-opération. Elle intervient donc en amont de la phase de décodage, à la recherche de paires « fusionnables » dans la file d’attente des instructions. A titre d’exemple, la séquence d’instruction :
cmp eax,[mem32]est détectée comme telle, et se décode en la seule micro-opération :
jne target
cmpjne eax,[mem32],targetCette micro-opération bénéficie d’un traitement de faveur car elle est prise en charge par une ALU améliorée, capable de la traiter en un seul cycle (si la donnée [mem32] est dans le cache L1).
Une unité de calcul améliorée de Core est en charge des micro-opérations issues de la macro-fusion.
Il est assez délicat de quantifier le gain de performance apporté par ces mécanismes de fusion. Cela étant, nous avons pu mesurer sur Yonah qu’en moyenne 10% des instructions sont micro-fusées ce qui réduit d’autant le nombre de micro-opérations à traiter. On peut estimer sans trop de risque que l’utilisation simultanée de la macro-fusion étend cette proportion à plus de 15%.
Caches, mémoire et prefetch
Gamme & plates-forme Intel Core
Sommaire
Vos réactions
Contenus relatifs
- [+] 09/05: AMD Ryzen 7 2700, Ryzen 5 2600 et I...
- [+] 04/05: Un Coffee Lake 8 coeurs en préparat...
- [+] 27/04: Le 10nm d'Intel (encore) retardé, l...
- [+] 26/04: Jim Keller rejoint... Intel !
- [+] 23/04: MAJ de notre test des Ryzen 7 2700X...
- [+] 20/04: MAJ de notre comparatif CPU géant
- [+] 19/04: AMD Ryzen 2700X et 2600X : Les même...
- [+] 19/04: 2008-2018 : tests de 62 processeurs...
- [+] 13/04: Les AMD Ryzen Pinnacle Ridge en pré...
- [+] 10/04: LGA4189 pour les Xeon Ice Lake !