Gestion des branchements L’un des goulots d’étranglements des processeurs moderne est ce qui se passe en présence de branchements Que peut-on faire pour accélérer l’exécution du code en présence de branchements conditionnels?
Pourquoi la gestion des branchements est de première importance dans les architectures superscalaires? Nous savons déjà que dans une simple machine pipeline comme le DLX, chaque branchement causera possiblement un délai de 2 coups d’horloge. En conséquence, il y aura dans ce cas deux instructions qui devront être éliminées, et le CPI augmentera en conséquence Dans une machine superscalaire, le problème est encore plus aigu: à chaque coup d’horloge, on lit maintenant plusieurs instructions (de 2 à 8 instructions simultanées) Un délai de 2 coups d’horloge peut donc maintenant représenter plus d’une dizaine d’instructions L’impact sur le CPI est donc encore plus important pour une machine superscalaire que pour un simple pipeline
Prédiction des branchements Nous avons vu déjà qu’il est possible d’utiliser des prédictions statiques (décidées au niveau du compilateur) pour améliorer la performance des branchements Pourquoi ne pas tenter de faire des prévisions dynamiques (i.e. basée sur l’éxécution actuelle du code)?
Accélérer les branchements: les choix possibles Déterminer plus tôt si le branchement sera pris ou non (branch resolution) Utiliser des prédictions de branchement et l’exécution spéculative Pouvoir revenir en arrière si la prédiction s’avère fausse Déterminer plus tôt la destination du branchement Garder dans un tampon l’adresse de destination du branchement, pour utilisation future Permettre de gérer plusieurs branchements imbriqués Il peut arriver qu’un branchement ne soit toujours pas résolu lorsqu’un 2e branchement doit être traité
Prédiction dynamique des branchements: Prédiction 1-bit 1ère idée (simple): on utilise un registre à 1 bit pour capturer la décision du dernier branchement. Lors du traitement de toute instruction de branchement, on met le registre à 1 si le branchement a été effectué, et à 0 si le branchement n’a pas été effectué. Chaque fois qu’on rencontre une instruction de branchement, on utilise le registre de prédiction pour tenter de deviner la décision de branchement. Évidemment, la prédiction doit être validée plus tard par la vraie décision de branchement! (Et si la prédiction était mauvaise, on doit pouvoir éliminer les instructions inappropriées et commencer à lire et exécuter les bonnes instructions)
Prédiction dynamique des branchements: Prédiction 1-bit (2) Le système simple qu’on vient de présenter va bien fonctionner tant qu’on a affaires au même branchement, mais la prévision n’aura pas de sens dès qu’on passera à une autre instruction de branchement… Que peut-on faire? On peut utiliser une petite mémoire de 2^k bits, et pour chaque instruction de branchement, on utilise les k bits d’adresse les plus petits de cette instruction pour accéder à la mémoire prédictive. (Idée connexe: hash de l’adresse sur k bits) On utilise maintenant 2^k bits au lieu d’un seul bit, mais la prévision est valable pour un ensemble plus grand de branchements (on appelle tout de même cette technique « prédiction à 1-bit »)
Prédiction dynamique des branchements: problèmes avec la prédiction 1-bit Supposons une boucle qui s’exécute 10 fois, et qui est elle-même ré-exécutée répétitivement. Quel sera le pourcentage de succès de notre prédicteur à 1 bit en régime permanent? Évidemment, le dernier branchement de la boucle (lorsqu’on sort après 10 itérations) est toujours mal prédit. De plus, le branchement de sortie de la boucle va modifier la valeur de notre prédicteur à 1-bit (il va maintenant prédire qu’on sort de la boucle), et donc la prochaine fois qu’on exécutera cette boucle, le premier branchement sera aussi mal prédit. Donc, on se trompe 2 fois sur dix (20% d’erreur)! Comment faire mieux?
Prédiction dynamique des branchements: Prédiction 2-bits Pour diminuer le pourcentage d’erreur de notre prédiction, on peut utiliser 2 bits au lieu de 1. Avec 2 bits, on peut représenter 4 états, ce qui nous permet de définir une petite machine à états finis. Par exemple, on peut définir un compteur avec saturation: (11) branche (10) (01) ne branche pas (00) Note: en blanc, prédiction en orange, valeur calculée (réelle)
Prédiction dynamique des branchements: Prédiction 2-bits La machine à états finis précédente va doucement évoluer vers l’un des deux pôles, et ensuite va tendre à rester dans cet état pendant longtemps. Chez SUN, pour le Ultra-SPARC-I, on a utilisé une version légèrement modifiée de cette machine à états finis: Note: en blanc, prédiction en orange, valeur calculée (réelle) (11) branche (10) (01) ne branche pas (00)
Prédiction dynamique des branchements: Prédiction dynamique des branchements: Efficacité de la prédiction 2-bits
Prédiction dynamique des branchements: Prédiction n-bits? On vient de voir qu’en passant d’un prédicteur à 1-bit à un prédicteur à 2-bits, on diminue le pourcentage d’erreur. On peut continuer dans le même sens, et passer à un prédicteur a k-bit… Est-ce une bonne idée?
Prédiction dynamique des branchements: Prédiction n-bits? Réponse: non, les résultats sont essentiellement les mêmes qu’avec un prédicteur à 2-bits
Prédiction dynamique des branchements: Prédiction dynamique des branchements: problèmes avec la prédiction 2-bits La prédiction à 2-bits est plus efficace que la prédiction à 1-bit, mais elle n’est tout de même pas si bonne. Avec les benchmarks entiers, ce type de prédiction se trompe plus de 11% du temps. Quoi faire? Augmenter la taille de la mémoire? Pas très efficace Tenir compte de la corrélation entre les branchements? Oui! (Idée de Pan, So et Rameh, 1992)
Prédiction dynamique des branchements: problèmes de corrélation Soit le code suivant: if (d == 0) d = 1; if (d == 1) … Ceci devient: BNEZ R1, L1 branche b1 (d != 0) ADDI R1, R0, 1 L1: SUBI R3, R1, 1 BNEZ R3, L2 branche b2 (d != 1) L2: … Supposons que le code est exécuté avec les valeurs d = 2,0,2,0. Qu’est-ce qui arrive à nos prédictions?
Prédiction dynamique des branchements: utilisation de la corrélation Puisque les branchements sont souvent dépendants les uns des autres, comment peut-on faire pour en tenir compte lors de notre prédiction? Nous utiliserons un registre à décalage de « m » bits pour conserver les valeurs des « m » derniers branchements. Ces « m » bits seront ensuite utilisés en partie pour adresser notre tampon de prédiction (le lire, et lorsque la décision définitive de branchement est prise, l’écrire) Le reste de l’adresse utilisée pour lire le tampon proviendra des k bits les plus faibles de l’adresse de l’instruction de branchement que nous sommes à prédire
Prédiction dynamique des branchements: utilisation de la corrélation (2) Si on utilise les « m » derniers branchements (leurs valeurs, 1 ou 0, étant conservées dans un registre à décalage de « m » bits) Si on utilise un prédicteur à n-bits dans notre tampon de prédiction On dit que le prédicteur basé sur la corrélation est un prédicteur (m,n) Notre prédicteur 2-bits vu auparavant est donc un (0,2), puisqu’il n’utilisait aucune historique Les prédicteurs (2,2) donnent habituellement de bons résultats
Prédiction dynamique des branchements: utilisation de la corrélation (3) Branch history register (BHR) Pattern History Table (PHT)
Prédiction dynamique des branchements: utilisation de la corrélation (4) Le prédicteur (2,2) donne des résultats supérieurs ou égaux à ceux de prédicteurs 2-bits utilisant beaucoup plus de mémoire
Prédiction dynamique des branchements: corrélation différente (Yeh et Patt, en 1992, 1993): Lorsqu’on parle de corrélation (m,n) (m choix de branchements, n bits de prédiction), doit-on considérer les « m » choix de branchements globalement ou localement? De même, la mémoires des « n » bits de prédiction doit-elle être globale ou locale? En d’autres mots, aurait-on intérêt à produire localement ou globalement l’information dans le BHR (Branch History Register)? Même question pour le PHT (Pattern History Table)?
Prédiction dynamique des branchements: corrélation différente (2) On appellera prédicteur XAy des systèmes de prédiction de branchement Adaptifs (le A du milieu) où le type de BHR (Branch History Register) est « X », et le type de PHT (Pattern History Table) est « y ».
Prédiction dynamique des branchements: corrélation différente (3) On définit les types de prédicteurs XAy suivants: G: BHR global (unique) g: PHT global (unique) P: BHR local, lu/écrit par adresse complète p: PHT local, lu/écrit par adresse complète S: BHR utilisant un sous-ensemble des bits d’adresse s: PHT utilisant un sous-ensemble des bits d’adresse On peut donc avoir des prédicteurs de 9 types différents: GAg, GAp, GAs, PAg, PAp, PAs, SAg, SAp et SAs
Prédiction dynamique des branchements: corrélation différente (4) Exemple: GAg Un seul BHR (Branch History Register), un seul PHT (Pattern History Table) On appelle GAg(4) un tel système qui utilise 4 bits pour le BHR (Branch History Register)
Prédiction dynamique des branchements: Système de Yeh et Patt PHT Exemple: GAg(4) 1 1111 1110 1101 1100 1011 1010 1001 1000 0111 0110 0101 0100 0011 0010 0001 0000 1 Prédiction: on branche! 1 BHR shift
Prédiction dynamique des branchements: Système de Yeh et Patt (2) Adresse de l’instruction de branchement Exemple: GAp(4) GAs(4): comme GAp(4), mais avec un sous-ensemble de l’adresse de l’instruction de branchement 1 1 1 1111 1110 1101 1100 1011 1010 1001 1000 0111 0110 0101 0100 0011 0010 0001 0000 1 Prédiction: on branche! … 1 BHR shift PHT
Prédiction dynamique des branchements: Système de Yeh et Patt (3) PHT Exemple: PAg(4) Semblable: SAg(4) 1 1111 1110 1101 1100 1011 1010 1001 1000 0111 0110 0101 0100 0011 0010 0001 0000 1 Prédiction: on ne branche pas! Adresse de l’instruction de branchement 1 1 BHR shift
Prédiction dynamique des branchements: Système de Yeh et Patt (4) Adresse de l’instruction de branchement Exemple: PAp(4) Semblable: SAs(4) 1 1 1 1111 1110 1101 1100 1011 1010 1001 1000 0111 0110 0101 0100 0011 0010 0001 0000 1 1 Prédiction: on branche! … Adresse de l’instruction de branchement 1 1 BHR shift PHT
Prédiction dynamique des branchements: Système de Yeh et Patt (5) Coût en matériel des différents systèmes de prédiction adaptive: Meilleur système avec un budget de 128k bits: GAs(13,32), qui obtient 97,2% de succès avec les SPEC89 Type de prédicteur Longueur du BHR Nombre de PHT Coût (nombre de bits à emmagasiner) GAg(k) k 1 k + 2^k * 2 GAs(k,p) p k + p * 2^k * 2 SAg(k,s) s * k + 2^k * 2 SAs(k,s,p) s * k + p * 2^k * 2
Prédiction dynamique des branchements: Système de Yeh et Patt (6) Fait intéressant: Les systèmes utilisant de l’information globale semblent donner de meilleurs résultats pour des programmes qui utilisent des opérations sur des entiers (ces applications utilisent habituellement beaucoup de conditions) Les systèmes utilisant de l’information locale semblent donner de meilleurs résultats pour des programmes qui utilisent des opérations sur des nombres à point flottant (ces applications utilisent souvent des boucles) GAs(13,32) est probablement un bon compromis entre ces deux types d’applications
Gestion des branchements: manque-t-il quelque chose? Nous avons maintenant une technique intéressante pour prédire si un branchement sera accepté ou non. Est-ce qu’il nous manque encore quelque chose pour que le tout fonctionne vraiment? Oui! C’est bien de savoir si le branchement sera pris ou non, mais il faut encore savoir où va nous mener ce branchement pour aller chercher l’instruction suivante en cas de branchement…
Gestion des branchements: Branch target Buffer Pour savoir où aller dès que le prédicteur a produit son estimé, on utilise une mémoire tampon qui contient l’adresse de destination du branchement en question. On appelle ce dispositif un Branch Target Buffer (BTB) Ce tampon associe l’adresse d’un branchement avec l’adresse de destination utilisée précédemment Dans les processeurs qui contiennent des mémoires caches pour les instructions directement sur le circuit (I-cache), on peut immédiatement aller chercher l’instruction de destination Dans les processeurs qui n’ont pas de I-cache (vieux), on a considéré mettre directement l’instruction dans le tampon. On appelle alors le tampon un Branch Target Address cache (BTAC)
Gestion des branchements: Branch Target Buffer
Gestion des branchements: utilisation du BTB
Gestion de branchements: quoi d’autre? Jusqu’ici, nous avons parlé de: Techniques de prédiction statiques (au niveau du compilateur) Techniques de prédictions dynamiques (prédiction à 2-bits, prédiction adaptive) Techniques pour conserver l’adresse de destination des branchements Qu’est-ce qui manque à notre arsenal? L’exécution spéculative
Gestion des branchements: spéculation Soit le code suivant: if (x == 0) { /* branche b1 */ a = b + c ; d = e – f ; } g = h * i ; /* instruction indépendante de b1 */ En supposant que la condition b1 soit faussement prédite prise, l’instruction « g = h * i » sera retranchée du processeur alors qu’elle doit être exécutée de toutes façons
Gestion des branchements: spéculation (2) Si le processeur le supporte, le compilateur pourrait réécrire la portion de code précédente comme suit: Pred = (x == 0) ; /* branche b1 */ if Pred then a = b + c ; /* Opération effectuée seulement si */ if Pred then d = e – f ; /* Pred est vrai */ g = h * i ; Cette technique est utilisée dans le processeur Itanium (aka Merced, IA64) de Intel / HP
Gestion des branchement: spéculation (3) Certains processeurs font aussi ce qu’on appelle du « eager » ou « multipath » execution: Le processeur exécute en parallèle les 2 branches d’une condition, et élimine celle qui est mauvaise lorsqu’on connaît le résultat de la condition Fonctionne bien pour des conditions qui ont peu d’instruction à l’intérieur En théorie, avec un nombre infini d’unités d’exécution, on peut paralléliser toutes les branches. En pratique, le nombre d’unités d’exécution du processeur limite le nombre de branches effectivement exécutées en parallèle
Gestion des branchement: spéculation (4) Puisque la spéculation est limitée par le matériel disponible, il faut choisir judicieusement les branches qu’on exécute de façon spéculative Comment? En intégrant un système de prédiction des branches!
Gestion des branchements – effets des langages orientés-objet Lorsqu’on examine le code exécutable de programmes écrits dans des langages de programmation orientés-objet (e.g. C++, Java), on s’aperçoit qu’il existe un bon nombre de branchements dont la destination n’est pas une constante Cela vient du fait que certaines méthodes sont décidées de façon dynamique, au cours de l’exécution du programme La destination des branchements étant variable, il devient intéressant d’utiliser un Branch Target Buffer (BTB), ou même de l’intégrer au Pattern History Table (PHT) pour pouvoir prédire rapidement la destination du branchement
Processeurs réels et gestion des branchements Technique Processeur Pas de prédiction Intel 8086 Prédiction statique non-prise prise prise (vers l’arrière) / non-prise (avant) Intel i486 Sun SuperSPARC HP-PA-7x00 Prédiction dynamique 1-bit 2-bits Adaptive DEC Alpha 21064, AMD-K5 PowerPC 604, MIPS R10000 Intel Pentium Pro, Pentium II, AMD K-6 Spéculation Intel / HP Itanium