D. Defour, B. Goossens, D. Parello A quoi servent les registres? D. Defour, B. Goossens et D. Parello
Plan de l'exposé Parallélisme dans le code source. Code machine et IPC. Renommage et IPC. Chargement/rangement et calcul. Pourquoi des registres? Un processeur sans registre. D. Defour, B. Goossens, D. Parello
Parallélisme dans le code source void saxpy( float x[], float y[], float a, uint n) { uint i; for (i=0; i<n; i++) y[i] += x[i]*a; } Le nombre d'Instructions exécutées Par Cycle est borné par le nombre d'opérateurs du processeur. n calculs indépendants. 2 lectures, puis 1 produit, puis 1 somme puis 1 écriture, le tout n fois. D. Defour, B. Goossens, D. Parello
Code machine d'architecture RISC saxpy: // x dans R1, y dans R2, a dans F1, n dans R3 CLRR8// i=0 CMPR9, R8, R3// i==n BEQR9, fin// if (i==n) return e: FLDF2, R1(R8)// F2=x[i] FMULF3, F2, F1// F3=a*x[i] FLDF4, R2(R8)// F4=y[i] FADDF5, F4, F3// F5=y[i]+a*x[i] FSTF5, R2(R8)// y[i]+=a*x[i] ADDR8, R8, 1// i++ CMPR9, R8, R3// i!=n BNER9, e// if (i!=n) goto e fin: RET D. Defour, B. Goossens, D. Parello
Caractéristiques du code machine RISC Les données en mémoire sont chargées en registre (chargement). Les sources et la destination de tout calcul sont des registres. Les résultats à conserver sont écrits en mémoire (rangement). D. Defour, B. Goossens, D. Parello
Quelles différences entre source et assembleur? L'indépendance des calculs a disparu. La (i)ème itération dépend de la (i-1)ème par le calcul de i et parce qu'elles emploient les mêmes registres. La dépendance sur i séquentialise le lancement des itérations au rythme du délai de l'additionneur. La dépendance sur les registres séquentialise les opérations au rythme des délais des opérateurs. D. Defour, B. Goossens, D. Parello
La dépendance sur i séquentialise le lancement des itérations au rythme du délai de l'additionneur. On ne peut démarrer les chargements de x[i] et de y[i], amorces de la chaîne des calculs du tour i, qu'une fois que i est établi, donc au plus tôt quand la valeur incrémentée de R8 sort de l'additionneur. Si l'additionneur a une latence d'1 cycle, on peut démarrer au plus une itération par cycle. L'IPC est borné par le nombre d'instructions par itération (dérouler la boucle pour augmenter l'IPC). D. Defour, B. Goossens, D. Parello
La dépendance sur les registres séquentialise les opérations au rythme des délais des opérateurs. Le chargement de x[i] dans F2 ne peut se faire qu'une fois que F2 est libre, c'est-à-dire quand l'instruction FMUL du tour i-1 a lu F2, donc quand x[i-1] a été chargé. Le chargement de x[i] ne commence qu'une fois que le chargement de x[i-1] est achevé. D. Defour, B. Goossens, D. Parello
Le produit a*x[i] dans F5 ne peut commencer qu'une fois que le produit a*x[i-1] a été lu par l'instruction FADD du tour i-1. La dépendance sur les registres séquentialise les opérations au rythme des délais des opérateurs. D. Defour, B. Goossens, D. Parello
(1)e: FLDF2, R1(R8) (2) FMULF3, F2, F1 (3) FLDF4, R2(R8) (4) FADDF5, F4, F3 (5) FSTF5, R2(R8) (6) ADDR8, R8, 1 (7) CMPR9, R8, R3 (8) BNER9, e Latences FLD:2 FST:2 FMUL:4 FADD:3 ADD:1 CMP:1 BNE:1 13,-,2,-,-,-,4,-,-,5,6,7,8 13,-,2,-,-,-,4,-,-,5 IPC <= 8/11 (0,73) D. Defour, B. Goossens, D. Parello
L'effet des registres En mappant l'espace des variables du programme sur l'espace des registres du processeur, on crée un ensemble de dépendances artificielles (la fonction de mappage est surjective, ce qui est inévitable puisque les variables sont plus nombreuses que les registres). D. Defour, B. Goossens, D. Parello
La parade: le renommage e: FLDF2, R1(R8)// FLDFF2-0, RR1-0(RR8-0) FMULF3, F2, F1// FMULFF3-0, FF2-0, FF1-0 FLDF4, R2(R8)// FLDFF4-0, RR2-0(RR8-0) FADDF5, F4, F3// FADDFF5-0, FF4-0, FF3-0 FSTF5, R2(R8)// FSTFF5-0, RR2-0(RR8-0) ADDR8, R8, 1// ADDRR8-1, RR8-0, 1 CMPR9, R8, R3// CMPRR9-1, RR8-1, RR3-0 BNER9, e// BNERR9-1, e e: FLDF2, R1(R8)// FLDFF2-1, RR1-0(RR8-1) FMULF3, F2, F1// FMULFF3-1, FF2-1, FF1-0 FLDF4, R2(R8)// FLDFF4-1, RR2-0(RR8-1) FADDF5, F4, F3// FADDFF5-1, FF4-1, FF3-1 FSTF5, R2(R8)// FSTFF5-1, RR2-0(RR8-1) ADDR8, R8, 1// ADDRR8-2, RR8-1, 1 CMPR9, R8, R3// CMPRR9-2, RR8-2, RR3-0 BNER9, e// BNERR9-2, e D. Defour, B. Goossens, D. Parello
(1)e: FLDF2, R1(R8) (2) FMULF3, F2, F1 (3) FLDF4, R2(R8) (4) FADDF5, F4, F3 (5) FSTF5, R2(R8) (6) ADDR8, R8, 1 (7) CMPR9, R8, R3 (8) BNER9, e Latences FLD:2 FST:2 FMUL:4 FADD:3 ADD:1 CMP:1 BNE:1 13,-,2,-,-,-,4,-,-,5 6,7,8 13,-,2,-,-,-,4,-,-,5 6,7,8 IPC <= 8 D. Defour, B. Goossens, D. Parello
Le renommage est matériel Le matériel alloue et libère les registres de renommage. A l'allocation, il puise dans une liste de registres libres. A la libération, il alimente la liste des registres libres. Il y a un nombre fixe de registres de renommage. Ce nombre est une contrainte sur l'ILP. D. Defour, B. Goossens, D. Parello
Le renommage augmente la surface du banc de registre L'ensemble des registres de renommage est une extension de l'ensemble des registres architecturaux. Plus le banc de registres de renommage est grand, plus on peut tirer parti du parallélisme d'instruction, mais plus le temps d'accès aux registres est grand. D. Defour, B. Goossens, D. Parello
L'effet du renommage Le renommage est une expansion de l'ensemble des registres du compilateur vers l'ensemble des registres du processeur. Le compilateur insère les variables dans les registres architecturaux et le matériel corrige l'excès de centralisation que cela induit en expansant les registres architecturaux sur les registres micro-architecturaux. D. Defour, B. Goossens, D. Parello
Architecture RISC (chargement/rangement) a = b + c;LDR1, M[b] LDR2, M[c] ADDR3, R1, R2 STR3, M[a] La hiérarchie mémoire se charge des échanges avec le banc de registres, commandés par les instructions de chargement et de rangement ajoutées par le compilateur. D. Defour, B. Goossens, D. Parello
Le chemin des données D. Defour, B. Goossens, D. Parello
Architecture mémoire/mémoire a = b + c; Le plus direct est de lire M[b] et M[c] et écrire M[a]. La hiérarchie mémoire doit transférer les sources du point le plus proche où il en existe une copie jusqu'à l'opérateur d'addition, puis elle doit ranger le résultat à la place libre la plus proche. D. Defour, B. Goossens, D. Parello
Remplacer les registres par L0 D. Defour, B. Goossens, D. Parello
Pourquoi des registres? Réponse n°1: c'est un intermédiaire intéressant qui rend l'exécution des instructions plus efficace. Ce qui est vraiment nécessaire, c'est une mémoire multi-ports ou multi-bancs (pour lire et écrire en parallèle) à laquelle les unités de calculs accèdent directement et située au premier niveau de la hiérarchie mémoire. D. Defour, B. Goossens, D. Parello
Pourquoi des registres? Réponse n°2: c'est un intermédiaire intéressant qui simplifie l'expression du code exécutable. Ce qui est vraiment nécessaire, c'est de pouvoir désigner de façon concise les sources et destinations des opérations. La concision des noms peut être obtenue en ne laissant dans le code qu'un préfixe (partie statique), le matériel ajoutant le suffixe. D. Defour, B. Goossens, D. Parello
Supprimer les registres? Remplacer les architectures registre/registre par une architecture mémoire/mémoire, c'est transgresser un tabou vieux de 25 ans! Si on tente en plus d'imposer un nouveau langage machine, il y a peu de chances qu'on puisse avoir du succès auprès des constructeurs! D. Defour, B. Goossens, D. Parello
Sémantique des noms Le compilateur, par les numéros de registres, désigne des mots en mémoire dont les adresses sont la concaténation du numéro de registre et d'un numéro d'exemplaire généré dynamiquement par le matériel. ADD R1, R2, R3// R[1:x] = R[2:y] + R[3:z] ADD R1, R1, 1// R[1:x+1] = R[1:x] + 1 // chaque écriture se fait dans un nouvel exemplaire // chaque lecture se fait dans l'exemplaire courant D. Defour, B. Goossens, D. Parello
Construction des noms Le compilateur fixe une partie du nom (le numéro de registre). Le matériel complète les noms des sources et des destinations des instructions extraites. Un tel mécanisme est déjà employé pour la composition des adresses: le compilateur en fixe une partie que le matériel complète (numéro de processus par exemple). D. Defour, B. Goossens, D. Parello
Construction des noms Pour une source Rs, le complément est le dernier numéro d'exemplaire Ex affecté à Rs. Le nom complet est (Rs, Ex). Pour une destination Rd, le dernier numéro Ey affecté à Rd est incrémenté. Le nom complet est (Rd, Ey). D. Defour, B. Goossens, D. Parello
Que représente le nom Rx? Rx est un ensemble de variables en mémoire (l'ensemble des exemplaires de Rx). Chaque exemplaire a une adresse unique qui le place dans la hiérarchie mémoire. Les exemplaires d'un processus sont distincts de ceux d'un autre processus (adresses différentes). D. Defour, B. Goossens, D. Parello
e: FLDF2, R1(R8)// FLDFF2-0, RR1-0(RR8-0) FMULF3, F2, F1// FMULFF3-0, FF2-0, FF1-0 FLDF4, R2(R8)// FLDFF4-0, RR2-0(RR8-0) FADDF5, F4, F3// FADDFF5-0, FF4-0, FF3-0 FSTF5, R2(R8)// FSTFF5-0, RR2-0(RR8-0) ADDR8, R8, 1// ADDRR8-1, RR8-0, 1 CMPR9, R8, R3// CMPRR9-1, RR8-1, RR3-0 BNER9, e// BNERR9-1, e e: FLDF2, R1(R8)// FLDFF2-1, RR1-0(RR8-1) FMULF3, F2, F1// FMULFF3-1, FF2-1, FF1-0 FLDF4, R2(R8)// FLDFF4-1, RR2-0(RR8-1) FADDF5, F4, F3// FADDFF5-1, FF4-1, FF3-1 FSTF5, R2(R8)// FSTFF5-1, RR2-0(RR8-1) ADDR8, R8, 1// ADDRR8-2, RR8-1, 1 CMPR9, R8, R3// CMPRR9-2, RR8-2, RR3-0 BNER9, e// BNERR9-2, e Exemple D. Defour, B. Goossens, D. Parello
Où est la différence? Ce n'est pas un renommage mais un ajout dynamique! Le renommage remplace l'ancien nom par un nouveau. Cela anihile une éventuelle stratégie de placement du compilateur. La complétion des adresses conserve le préfixe choisi par le compilateur. Par exemple, le compilateur peut distribuer sources et destinations pour équilibrer les accès aux partitions formant L0. D. Defour, B. Goossens, D. Parello
Allocation/libération Le renommage induit une allocation/libération de registres de renommage. La complétion des adresses induit une allocation (création d'une entrée de cache contenant l'exemplaire: première référence) et une libération (désallocation de la ligne de cache lorsqu'un nouvel exemplaire est alloué). (allocation/désallocation de lignes de cache) D. Defour, B. Goossens, D. Parello
La lecture des sources La latence des accès n'est plus constante. Elle dépend de la hiérarchie mémoire. Pour un degré superscalaire d, plutôt que de lire 2d sources et d'écrire d destinations par cycle d'une structure centralisée (lancement d'au plus d instructions prêtes par cycle), on effectue des accès en lecture et en écriture dans L0.
D. Defour, B. Goossens, D. Parello Combien de ports d'accès à L0? Cela dépend du nombre moyen de lectures et d'écritures par instruction. Avec simplescalar, on peut mesurer, pour un degré superscalaire d, le nombre de ports p en dessous duquel l'IPC est sérieusement dégradé. Des mesures préliminaires ont montré qu'en moyenne, pour un degré d, on effectue 0,8d écritures et 1,2d lectures.
Quel IPC pour saxpy? L'IPC du processeur avec renommage est borné par le nombre d'instructions du corps de boucle, à cause de la dépendance des calculs des valeurs successives de i. L'IPC potentiel du processeur sans registre n'est pas meilleur puisque la même contrainte n'est pas ôtée (l'IPC réel peut être meilleur grâce à une latence raccourcie). D. Defour, B. Goossens, D. Parello
Quelle limite au parallélisme d'instruction? On peut créer autant d'exemplaires d'un registre qu'on veut (limite de la taille des adresses). Il faut néanmoins limiter les exemplaires à la capacité du cache L0 (pour un accès en 1 cycle). Pour restreindre l'allocation de nouvelles lignes, il faut réutiliser les lignes désallouées, donc les numéros d'exemplaires antérieurs. D. Defour, B. Goossens, D. Parello
Quels avantages architecturaux? Cette micro-architecture sans registre préserve une compatibilité binaire totale avec les architectures antérieures. Dans une fonction, il n'y a pas de registre à sauver/restaurer. Lors d'un changement de contexte, il n'y a pas de registre à sauver/restaurer. D. Defour, B. Goossens, D. Parello
Quels avantages micro-architecturaux? Le chemin de données est simplifié: il n'est plus composé que de niveaux hiérarchiques dont le premier est accédé par les unités de calcul. En quelque sorte, le nouveau chemin est le chemin antérieur amputé du banc de registres. Dans une micro-architecture multi-thread, on ne duplique que le compteur de programme. Le cache L0 est partagé. D. Defour, B. Goossens, D. Parello
Quels avantages micro-architecturaux? Le compilateur peut attribuer les registres en appliquant une stratégie qui distribue les accès en lecture et en écriture. Le cache L0 peut être réparti en bancs, chaque banc étant une mémoire simple port. Les accès à deux bancs sont indépendants. En multipliant les bancs, on multiplie les ports et le degré d'ILP.
Comment valider cette proposition? Première étape: affiner la micro-architecture pour déterminer le découpage pipeliné du traitement des instructions. Comparer l'IPC à code exécutable et à cycle constants (simulation sim-outorder ou microlib). Seconde étape: modifier le compilateur pour produire un code allégé et comparer le nombre n d'instructions exécutées. T = (n / IPC) * c D. Defour, B. Goossens, D. Parello
Conclusion D. Defour, B. Goossens, D. Parello