Il y a de nombreuses raisons pour lesquelles vous n'avez pas seulement un grand nombre de registres:
- Ils sont étroitement liés à la plupart des étapes du pipeline. Pour commencer, vous devez suivre leur durée de vie et renvoyer les résultats aux étapes précédentes. La complexité devient intraitable très rapidement, et le nombre de fils (littéralement) impliqués croît au même rythme. C'est cher sur la surface, ce qui signifie finalement que c'est cher en puissance, en prix et en performances après un certain point.
- Il prend de l'espace d'encodage d'instructions. 16 registres occupent 4 bits pour la source et la destination, et 4 autres si vous avez des instructions à 3 opérandes (par exemple ARM). C'est énormément d'espace d'encodage de jeu d'instructions occupé juste pour spécifier le registre. Cela a finalement un impact sur le décodage, la taille du code et encore une fois la complexité.
- Il y a de meilleures façons d'obtenir le même résultat ...
De nos jours, nous avons vraiment beaucoup de registres - ils ne sont tout simplement pas explicitement programmés. Nous avons "renommer le registre". Bien que vous n'accédiez qu'à un petit ensemble (8-32 registres), ils sont en fait soutenus par un ensemble beaucoup plus grand (par exemple 64-256). La CPU suit ensuite la visibilité de chaque registre et les attribue à l'ensemble renommé. Par exemple, vous pouvez charger, modifier, puis stocker dans un registre plusieurs fois de suite, et faire exécuter chacune de ces opérations indépendamment en fonction des échecs de cache, etc. Dans ARM:
ldr r0, [r4]
add r0, r0, #1
str r0, [r4]
ldr r0, [r5]
add r0, r0, #1
str r0, [r5]
Les cœurs Cortex A9 renomment les registres, donc le premier chargement vers "r0" va en fait vers un registre virtuel renommé - appelons-le "v0". Le chargement, l'incrémentation et le stockage se produisent sur "v0". Pendant ce temps, nous effectuons à nouveau un chargement / modification / stockage sur r0, mais cela sera renommé en "v1" car il s'agit d'une séquence entièrement indépendante utilisant r0. Disons que la charge du pointeur dans "r4" est bloquée en raison d'un manque de cache. Ce n'est pas grave - nous n'avons pas besoin d'attendre que "r0" soit prêt. Parce qu'il est renommé, nous pouvons exécuter la séquence suivante avec "v1" (également mappé sur r0) - et peut-être que c'est un succès de cache et que nous venons d'avoir une énorme victoire en termes de performances.
ldr v0, [v2]
add v0, v0, #1
str v0, [v2]
ldr v1, [v3]
add v1, v1, #1
str v1, [v3]
Je pense que x86 est jusqu'à un nombre gigantesque de registres renommés ces jours-ci (environ 256). Cela signifierait avoir 8 bits fois 2 pour chaque instruction juste pour dire quelle est la source et la destination. Cela augmenterait massivement le nombre de fils nécessaires à travers le noyau et sa taille. Il y a donc un sweet spot autour de 16-32 registres que la plupart des concepteurs ont choisi, et pour les conceptions de CPU en désordre, le changement de nom de registre est le moyen de l'atténuer.
Edit : L'importance de l'exécution dans le désordre et du renommage du registre à ce sujet. Une fois que vous avez OOO, le nombre de registres n'a pas tant d'importance, car ce ne sont que des "balises temporaires" et sont renommées en un ensemble de registres virtuels beaucoup plus grand. Vous ne voulez pas que le nombre soit trop petit, car il devient difficile d'écrire de petites séquences de code. C'est un problème pour x86-32, car les 8 registres limités signifient que beaucoup de temporaires finissent par passer par la pile, et le noyau a besoin d'une logique supplémentaire pour transférer les lectures / écritures vers la mémoire. Si vous n'avez pas OOO, vous parlez généralement d'un petit noyau, auquel cas un grand jeu de registres est un faible avantage en termes de coût / performance.
Il existe donc un sweet spot naturel pour la taille de la banque de registres, qui atteint au maximum environ 32 registres architecturés pour la plupart des classes de CPU. x86-32 a 8 registres et il est définitivement trop petit. ARM est allé avec 16 registres et c'est un bon compromis. 32 registres, c'est un peu trop, voire pas du tout - vous finissez par ne pas avoir besoin des 10 derniers environ.
Rien de tout cela ne touche aux registres supplémentaires que vous obtenez pour SSE et d'autres coprocesseurs vectoriels à virgule flottante. Ceux-ci ont du sens en tant qu'ensemble supplémentaire car ils fonctionnent indépendamment du cœur entier et n'augmentent pas la complexité du processeur de manière exponentielle.