En général, toutes les unités centrales, les microprocesseurs monopuces ou les implémentations multi-puces exécutent des programmes en suivant les étapes suivantes :
- Lisez une instruction et décodez-la.
- Trouvez toutes les données associées nécessaires au traitement de l'instruction.
- Traiter l'instruction.
- Rédigez les résultats.
Cette série d'étapes d'apparence simple est compliquée par le fait que la hiérarchie de la mémoire, qui comprend la mise en cache, la mémoire principale et le stockage non volatil comme les disques durs (où se trouvent les instructions des programmes et les données) a toujours été plus lente que le processeur lui-même. L'étape (2) introduit souvent un retard (en termes de CPU, souvent appelé "stall") pendant que les données arrivent par le bus de l'ordinateur. De nombreuses recherches ont été menées pour concevoir des systèmes qui évitent autant que possible ces retards. Au fil des ans, un objectif central de la conception a été d'exécuter davantage d'instructions en parallèle, augmentant ainsi la vitesse d'exécution effective d'un programme. Ces efforts ont introduit des structures de logique et de circuit compliquées. Dans le passé, ces techniques ne pouvaient être mises en œuvre que sur des ordinateurs centraux ou des superordinateurs coûteux en raison de la quantité de circuits nécessaires à ces techniques. Avec les progrès de la fabrication des semi-conducteurs, de plus en plus de ces techniques ont pu être mises en œuvre sur une seule puce semi-conductrice.
Ce qui suit est une étude des techniques micro-architecturales qui sont courantes dans les unités centrales modernes.
Instruction Choix de l'ensemble
Le choix de l'architecture du jeu d'instructions à utiliser influe grandement sur la complexité de la mise en œuvre de dispositifs à hautes performances. Au fil des ans, les concepteurs d'ordinateurs ont fait de leur mieux pour simplifier les jeux d'instructions, afin de permettre des mises en œuvre plus performantes en leur faisant gagner du temps et des efforts pour les fonctionnalités qui améliorent les performances plutôt que de les gaspiller dans la complexité du jeu d'instructions.
La conception des jeux d'instructions a progressé à partir des types CISC, RISC, VLIW, EPIC. Les architectures qui traitent du parallélisme des données comprennent les SIMD et les Vecteurs.
Conduite d'instruction
L'une des premières techniques, et la plus puissante, pour améliorer les performances est l'utilisation du pipeline d'instructions. Les premières conceptions de processeurs exécutaient toutes les étapes ci-dessus sur une instruction avant de passer à la suivante. De grandes parties du circuit du processeur étaient laissées inactives à chaque étape ; par exemple, le circuit de décodage des instructions était inactif pendant l'exécution, etc.
Les pipelines améliorent les performances en permettant à un certain nombre d'instructions de passer simultanément dans le processeur. Dans le même exemple de base, le processeur commencerait à décoder (étape 1) une nouvelle instruction alors que la dernière attend des résultats. Cela permettrait à un maximum de quatre instructions d'être "en vol" en même temps, ce qui ferait paraître le processeur quatre fois plus rapide. Bien qu'une instruction prenne autant de temps pour être exécutée (il y a toujours quatre étapes), le processeur dans son ensemble "retire" les instructions beaucoup plus rapidement et peut être exécuté à une vitesse d'horloge beaucoup plus élevée.
Cache
Les améliorations apportées à la fabrication des puces ont permis de placer davantage de circuits sur la même puce, et les concepteurs ont commencé à chercher des moyens de l'utiliser. L'un des moyens les plus courants consistait à ajouter une quantité toujours plus importante de mémoire cache sur la puce. Le cache est une mémoire très rapide, une mémoire à laquelle on peut accéder en quelques cycles par rapport à ce qui est nécessaire pour parler à la mémoire principale. L'unité centrale comprend un contrôleur de cache qui automatise la lecture et l'écriture à partir du cache. Si les données sont déjà dans le cache, elles "apparaissent" simplement, tandis que si elles ne le sont pas, le processeur est "bloqué" pendant que le contrôleur de cache les lit.
Les conceptions RISC ont commencé à ajouter du cache au milieu et à la fin des années 1980, souvent avec seulement 4 Ko au total. Ce nombre a augmenté avec le temps, et les processeurs typiques ont maintenant environ 512 Ko, tandis que les processeurs plus puissants ont 1 ou 2, voire 4, 6, 8 ou 12 Mo, organisés en plusieurs niveaux d'une hiérarchie de mémoire. D'une manière générale, plus de cache signifie plus de vitesse.
Les caches et les pipelines se complètent parfaitement. Auparavant, il n'était pas très judicieux de construire un pipeline capable de fonctionner plus rapidement que la latence d'accès de la mémoire de caisse hors puce. L'utilisation d'une mémoire cache sur puce signifiait qu'un pipeline pouvait fonctionner à la même vitesse que la latence d'accès au cache, c'est-à-dire sur une durée beaucoup plus courte. Cela a permis aux fréquences de fonctionnement des processeurs d'augmenter à un rythme beaucoup plus rapide que celui de la mémoire hors puce.
Prévision de la branche et exécution spéculative
Les décrochages de pipelines et les chasses d'eau dues aux branches sont les deux principaux éléments qui empêchent d'obtenir de meilleures performances grâce au parallélisme des niveaux d'instruction. Entre le moment où le décodeur d'instructions du processeur découvre qu'il a rencontré une instruction de branchement conditionnel et le moment où la valeur du registre de saut décisif peut être lue, le pipeline peut être bloqué pendant plusieurs cycles. En moyenne, une instruction sur cinq exécutée est un branchement, ce qui représente un nombre élevé de blocages. Si la branche est prise, c'est encore pire, car toutes les instructions suivantes qui étaient dans le pipeline doivent être supprimées.
Des techniques telles que la prédiction de branche et l'exécution spéculative sont utilisées pour réduire ces sanctions de branche. La prédiction de branche est le moyen par lequel le matériel fait des suppositions éclairées sur le fait qu'une branche particulière sera prise ou non. La supposition permet au matériel de prélever des instructions sans attendre la lecture du registre. L'exécution spéculative est une autre amélioration dans laquelle le code le long du chemin prédit est exécuté avant que l'on sache si la branche doit être prise ou non.
Exécution hors délai
L'ajout de caches réduit la fréquence ou la durée des blocages dus à l'attente de la récupération des données dans la hiérarchie de la mémoire principale, mais ne permet pas de se débarrasser entièrement de ces blocages. Dans les premières conceptions, un échec de la mémoire cache obligeait le contrôleur de la mémoire cache à bloquer le processeur et à attendre. Bien sûr, il peut y avoir d'autres instructions dans le programme dont les données sont disponibles dans le cache à ce moment-là. L'exécution hors service permet de traiter cette instruction prête à l'emploi pendant qu'une instruction plus ancienne attend dans la mémoire cache, puis de réordonner les résultats pour faire croire que tout s'est passé dans l'ordre programmé.
Superscalar
Malgré toute la complexité et les portes supplémentaires nécessaires pour soutenir les concepts décrits ci-dessus, les améliorations dans la fabrication des semi-conducteurs ont rapidement permis d'utiliser encore plus de portes logiques.
Dans le schéma ci-dessus, le processeur traite des parties d'une seule instruction à la fois. Les programmes informatiques pourraient être exécutés plus rapidement si plusieurs instructions étaient traitées simultanément. C'est ce que les processeurs superscalaires réalisent, en reproduisant des unités fonctionnelles telles que les ALU. La réplication des unités fonctionnelles n'a été rendue possible que lorsque la zone du circuit intégré (parfois appelée "matrice") d'un processeur à émission unique ne dépassait plus les limites de ce qui pouvait être fabriqué de manière fiable. À la fin des années 1980, les conceptions superscalaires ont commencé à faire leur apparition sur le marché.
Dans les conceptions modernes, il est courant de trouver deux unités de charge, une unité de stockage (de nombreuses instructions n'ont pas de résultats à stocker), deux unités mathématiques entières ou plus, deux unités à virgule flottante ou plus, et souvent une unité SIMD d'une certaine sorte. La logique d'émission des instructions devient de plus en plus complexe en lisant une énorme liste d'instructions en mémoire et en les transmettant aux différentes unités d'exécution qui sont inactives à ce moment-là. Les résultats sont ensuite collectés et réordonnés à la fin.
Renommer le registre
Le renommage des registres fait référence à une technique utilisée pour éviter l'exécution en série inutile d'instructions de programme en raison de la réutilisation des mêmes registres par ces instructions. Supposons que nous ayons des groupes d'instructions qui utiliseront le même registre, un ensemble d'instructions est exécuté en premier pour laisser le registre à l'autre ensemble, mais si l'autre ensemble est affecté à un registre similaire différent, les deux ensembles d'instructions peuvent être exécutés en parallèle.
Multitraitement et multithreading
En raison de l'écart croissant entre les fréquences de fonctionnement du CPU et les temps d'accès aux DRAM, aucune des techniques qui améliorent le parallélisme au niveau des instructions (ILP) dans un programme ne pouvait surmonter les longs blocages (retards) qui se produisaient lorsqu'il fallait récupérer des données dans la mémoire principale. De plus, le nombre important de transistors et les fréquences de fonctionnement élevées nécessaires aux techniques ILP plus avancées nécessitaient des niveaux de dissipation de puissance qui ne pouvaient plus être refroidis à moindre coût. Pour ces raisons, les nouvelles générations d'ordinateurs ont commencé à utiliser des niveaux de parallélisme plus élevés qui existent en dehors d'un seul programme ou fil de programme.
Cette tendance est parfois connue sous le nom de "calcul à haut débit". Cette idée est née sur le marché des ordinateurs centraux où le traitement des transactions en ligne mettait l'accent non seulement sur la vitesse d'exécution d'une transaction, mais aussi sur la capacité à traiter un grand nombre de transactions en même temps. Les applications basées sur les transactions, telles que le routage en réseau et la desserte de sites web, ayant fortement augmenté au cours de la dernière décennie, l'industrie informatique a de nouveau mis l'accent sur les questions de capacité et de débit.
L'une des techniques permettant d'atteindre ce parallélisme est le recours à des systèmes multiprocesseurs, c'est-à-dire des systèmes informatiques dotés de plusieurs unités centrales. Autrefois, cette technique était réservée aux ordinateurs centraux haut de gamme, mais aujourd'hui, les serveurs multiprocesseurs à petite échelle (2 à 8) sont devenus monnaie courante pour le marché des petites entreprises. Pour les grandes entreprises, les multiprocesseurs à grande échelle (16-256) sont courants. Même les ordinateurs personnels équipés de plusieurs processeurs sont apparus depuis les années 1990.
Les progrès de la technologie des semi-conducteurs ont permis de réduire la taille des transistors ; des processeurs multicœurs sont apparus lorsque plusieurs processeurs sont mis en œuvre sur une même puce de silicium. Initialement, les puces intégrées étaient destinées aux marchés de l'embarqué, où des processeurs plus simples et plus petits permettaient de faire tenir plusieurs instanciations sur une seule pièce de silicium. En 2005, la technologie des semi-conducteurs a permis de fabriquer en volume des puces CMP à double CPU haut de gamme pour ordinateurs de bureau. Certaines conceptions, telles que UltraSPARC T1, utilisaient une conception plus simple (scalaire, en ordre) afin de faire tenir plus de processeurs sur une seule pièce de silicium.
Récemment, une autre technique qui est devenue plus populaire est le multithreading. Dans le cas du multithreading, lorsque le processeur doit aller chercher des données dans une mémoire système lente, au lieu de temporiser pour que les données arrivent, le processeur passe à un autre programme ou à un autre thread de programme qui est prêt à s'exécuter. Bien que cela n'accélère pas un programme/thread particulier, cela augmente le débit global du système en réduisant le temps d'inactivité du CPU.
Conceptuellement, le multithreading est équivalent à un changement de contexte au niveau du système d'exploitation. La différence est qu'un CPU multithreading peut effectuer un changement de thread en un seul cycle de CPU au lieu des centaines ou milliers de cycles de CPU qu'un changement de contexte nécessite normalement. Ceci est réalisé en répliquant le matériel d'état (tel que le fichier de registre et le compteur de programme) pour chaque thread actif.
Une autre amélioration est le multithreading simultané. Cette technique permet à des unités centrales superscalaires d'exécuter simultanément des instructions provenant de différents programmes/threads dans le même cycle.