En 32 bits, nous avions 8 registres «à usage général». Avec 64 bits, le montant double, mais cela semble indépendant du changement 64 bits lui-même.
Maintenant, si les registres sont si rapides (pas d'accès à la mémoire), pourquoi n'y en a-t-il pas plus naturellement? Les constructeurs de CPU ne devraient-ils pas travailler autant de registres que possible dans le CPU? Quelle est la restriction logique pour laquelle nous n'avons que le montant dont nous disposons?
88
Réponses:
Il y a de nombreuses raisons pour lesquelles vous n'avez pas seulement un grand nombre de registres:
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:
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.
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.
la source
Nous faisons avoir plus d'eux
Étant donné que presque chaque instruction doit sélectionner 1, 2 ou 3 registres architecturaux visibles, augmenter le nombre d'entre eux augmenterait la taille du code de plusieurs bits sur chaque instruction et réduirait ainsi la densité du code. Cela augmente également la quantité de contexte qui doit être enregistrée en tant qu'état de thread et partiellement enregistrée dans l' enregistrement d'activation d' une fonction . Ces opérations sont fréquentes. Les verrouillages de pipeline doivent vérifier un tableau de bord pour chaque registre, ce qui présente une complexité quadratique dans le temps et dans l'espace. Et peut-être que la principale raison est simplement la compatibilité avec le jeu d'instructions déjà défini.
Mais il s'avère que grâce au changement de nom des registres , nous avons vraiment beaucoup de registres disponibles, et nous n'avons même pas besoin de les sauvegarder. Le CPU a en fait de nombreux jeux de registres, et il bascule automatiquement entre eux lorsque votre code exeutes. Il le fait uniquement pour vous obtenir plus de registres.
Exemple:
Dans une architecture qui n'a que r0-r7, le code suivant peut être réécrit automatiquement par le CPU comme quelque chose comme:
Dans ce cas, r10 est un registre caché qui remplace temporairement r1. Le CPU peut dire que la valeur de r1 n'est plus jamais utilisée après le premier stockage. Cela permet de retarder le premier chargement (même un hit de cache sur puce prend généralement plusieurs cycles) sans nécessiter le retard du deuxième chargement ou du deuxième stockage.
la source
Ils ajoutent des registres tout le temps, mais ils sont souvent liés à des instructions spéciales (par exemple SIMD, SSE2, etc.) ou nécessitent une compilation sur une architecture de processeur spécifique, ce qui réduit la portabilité. Les instructions existantes fonctionnent souvent sur des registres spécifiques et ne pourraient pas profiter d'autres registres s'ils étaient disponibles. Ensemble d'instructions hérité et tout.
la source
Pour ajouter quelques informations intéressantes ici, vous remarquerez qu'avoir 8 registres de même taille permet aux opcodes de maintenir la cohérence avec la notation hexadécimale. Par exemple, l'instruction
push ax
est l'opcode 0x50 sur x86 et va jusqu'à 0x57 pour le dernier registre di. Ensuite, l'instructionpop ax
commence à 0x58 et monte à 0x5Fpop di
pour terminer la première base-16. La cohérence hexadécimale est maintenue avec 8 registres par taille.la source