« Fonctionnement d'un ordinateur/Exécution dans le désordre » : différence entre les versions

Contenu supprimé Contenu ajouté
mAucun résumé des modifications
Ligne 13 :
Avec le scoreboarding, la gestion et la détection des dépendances sont effectuées par un circuit : le scoreboard. Mais ce n'est pas le seul circuit nécessaire pour pouvoir exécuter nos instructions dans un ordre différent de celui imposé par le programme.
 
[[File:Pipeline d'un processeur utilisant le scoreboarding.png|centre|Pipeline d'un processeur utilisant le scoreboarding.]]
 
L'étape d'émission sert à décider si l'instruction en cours peut démarrer son exécution en fonction de ses dépendances. Plus précisément, elle vérifie la présence de dépendances structurelles et de dépendances WAW : c'est elle qui vérifie que l'ordre des écritures dans les registres est maintenu (relisez le chapitre sur les interruptions et exceptions dans le pipeline si vous ne voyez pas pourquoi cet ordre doit être maintenu).
Ligne 31 :
Au lieu de bloquer le pipeline à l'étape d'émission en cas de dépendances, pourquoi ne pas utiliser des instructions indépendantes dans la suite du programme ? Pour que cela fonctionne, on est obligé de faire en sorte que l'étage d'émission mette en attente les instructions, sans bloquer le pipeline (chose que fait le scoreboard). Généralement, cette mise en attente se fait en utilisant une ou plusieurs mémoires spécialisées, chaque mémoire étant composée d'entrées dédiées à une instruction chacune.
 
Le cas le plus simple n'utilise qu'une seule mémoire tampon : la '''fenêtre d'instruction'''. L'unité d'émission va alors :
 
* détecter les dépendances ;
Ligne 37 :
* répartir les instructions sur les unités de calcul.
 
[[File:Fenêtre d'instruction.png|centre|Fenêtre d'instruction.]]
 
Sur certains processeurs, la répartition des instructions à l’exécution sur les unités de calcul est prise en charge par un circuit séparé de l'étage d'émission, les deux étant séparés par une mémoire tampon. On doit ainsi utiliser plusieurs fenêtres d'instruction, souvent une par unité de calcul. Ainsi, l'émission se contente de répartir les instructions sur les unités de calcul, la détection des dépendances étant prise en charge par la fenêtre d'instruction. Ces fenêtres d'instruction spécialisées sont parfois appelées des stations de réservation. Dans ce qui va suivre, j'utiliserai le terme « fenêtre d'instruction » pour parler des stations de réservation, ainsi que de la fenêtre d'instruction.
 
[[File:Processeur avec plusieurs fenêtres d'instruction.png|centre|Processeur avec plusieurs fenêtres d'instruction.]]
 
L'avantage, c'est que chaque station de réservation est plus petite qu'une grosse fenêtre d'instruction. L'autre avantage, c'est qu'il est possible de démarrer l'exécution de plusieurs instructions simultanément : une instruction par station de réservation, contre une par fenêtre d'instruction. Mais les stations de réservation sont souvent sous-utilisées, partiellement remplies, contrairement aux fenêtres d'instruction.
Ligne 67 :
===Pipeline à répétition===
 
Certains processeurs sont beaucoup plus agressifs, et prédisent que aucune instruction ne fait de défaut de cache. Évidemment, ces processeurs devraient en théorie vider leurs pipelines assez souvent, mais ils utilisent une technique élégante pour gérer ces ratés de prédiction : ils utilisent ce qu'on appelle des '''pipelines à répétition''' (replay pipeline).
 
Le principe qui se cache derrière ces pipeline est assez simple : on lance une instruction sans savoir quelle est la latence et on réexécute celle-ci en boucle tant que son résultat n'est pas valide. Pour réexécuter une instruction en boucle, le pipeline doit être modifié : celui-ci contient une sorte de boucle, en plus du pipeline normal. Les instructions vont se propager à la fois dans la boucle, et dans le pipeline normal. L'étage final de la boucle vérifie que l'instruction n'a pas été émise trop tôt avec un scoreboard, et il regarde si l'instruction a donné lieu à un défaut de cache ou non.
Ligne 73 :
Si l'instruction a donné un bon résultat, une nouvelle instruction est envoyée dans le pipeline. Dans le cas contraire, l'instruction refera encore un tour dans le pipeline. Dans ce cas, l'unité de vérification va devoir envoyer un signal à l'unité d'émission pour lui dire « réserve un cycle pour l'instruction que je vais faire boucler ».
 
[[File:Pipeline à répétition.png|centre|Pipeline à répétition.]]
 
Les étages de la boucle servent juste à propager les signaux de commande de l'instruction, sans rien faire de spécial. Dans le pipeline qui exécute l'instruction, ces signaux de commande sont consommés au fur et à mesure, ce qui fait qu'à la fin du pipeline, il ne reste plus rien de l'instruction originale. D'où la présence de la boucle, qui sert à conserver les signaux de commande.
Ligne 79 :
Toutefois, ce pipeline est loin d'être optimal avec des hiérarchies de cache. Prenons un exemple : un succès de cache dans le cache L1 dure 3 cycles d'horloge, un succès de cache dans le L2 dure 8 cycles, et un défaut de cache 12 cycles. Regardons ce qui se passe avec une instruction qui fait un défaut de cache dans le L1, et un succès de cache dans le L2. La boucle de 3 cycles utilisée pour le L1 ne permettra pas de gérer efficacement la latence de 8 cycles du L2 : l'instruction devra faire trois tours, soit 9 cycles d'attente, contre 8 idéalement. La solution consiste à retarder le second tour de boucle de quelques cycles, en rajoutant des étages vides dans la boucle. Dans notre exemple, il faut retarder de deux cycles : 8 cycles, dont trois en moins pour le premier tour, et trois en moins pour le trajet fenêtre d'instruction → unité de calcul.
 
[[File:Pipeline à répétition avec une latence de 3 cycles pour le L1, et 8 cycles pour le L2.png|centre|Pipeline à répétition avec une latence de 3 cycles pour le L1, et 8 cycles pour le L2.]]
 
Le même principe peut s'appliquer pour gérer les latences avec des niveaux de cache supérieurs : il faut alors utiliser plusieurs boucles de tailles différentes. Ce principe peut aussi s'appliquer dans d'autres cas assez spécifiques, dans lesquels l'instruction a été émise trop tôt, sans que cela fasse intervenir les mémoires caches. Les étages situés avant l'étage de vérification seront partagés, mais un multiplexeur se chargera de choisir vers quelle boucle envoyer l’instruction, suivant le cache dans lequel on fait défaut.
 
[[File:Pipeline à répétition avec hiérarchie de cache multiples et exceptions.png|centre|Pipeline à répétition pour une hiérarchie de cache.]]
 
Dans ces conditions, il arrive que plusieurs boucles veuillent faire rentrer une instruction dans le pipeline en même temps. Pour cela, une seule boucle pourra réémettre son instruction, les autres étant mises en attente. Divers mécanismes d'arbitrage, de choix de la boucle sélectionnée pour l'émission, sont possible : privilégier la boucle dont l'instruction est la plus ancienne (et donc la boucle la plus longue) est la technique la plus fréquente. Mais dans certains cas, mettre une boucle en attente peut bloquer tous les étages précédents, ce qui peut bloquer l'émission de la nouvelle instruction : le processeur se retrouve définitivement bloqué. Dans ce cas, le processeur doit disposer d'un système de détection de ces blocages, ainsi que d'un moyen pour s'en sortir et revenir à la normale (en vidant le pipeline, par exemple).
Ligne 89 :
Pour gérer au mieux les accès à la mémoire RAM, on remplace la boucle dédiée à la latence mémoire par une FIFO, dans laquelle les instructions sont accumulées en attendant le retour de la donnée en mémoire. Quand la donnée est disponible, lue ou écrite en mémoire, un signal est envoyé à cette mémoire, et l'instruction est envoyée directement dans le pipeline. Là encore, il faut gérer les conflits d'accès à l'émission entre les différentes boucles et la file d’attente de répétition, qui peuvent vouloir émettre une instruction en même temps.
 
[[File:Gestion des accès RAM sur un pipeline à répétition.png|centre|Gestion des accès RAM sur un pipeline à répétition.]]
 
On peut aussi adapter le pipeline à répétition pour qu'il gère certaines exceptions : certaines exceptions sont en effet récupérables, et disparaissent si on réexécute l'instruction. Ces exceptions peuvent provenir d'une erreur de prédiction de dépendance entre instructions (on a émis une instruction sans savoir si ses opérandes étaient prêts), ou d'autres problèmes dans le genre. Si jamais une exception récupérable a eu lieu, l'instruction doit être réexécutée, et donc réémise. Elle doit refaire une boucle dans le pipeline. Seul problème : ces exceptions se détectent à des étages très différents dans le pipeline. Dans ce cas, on peut adapter le pipeline pour que chaque exception soit vérifiée et éventuellement réémise dès que possible. On doit donc ajouter plusieurs étages de vérification, ainsi que de nombreuses boucles de réémission.