Que fait le registermot-clé en langage C? J'ai lu qu'il est utilisé pour l'optimisation mais n'est clairement défini dans aucune norme. Est-il toujours pertinent et si oui, quand l'utiliseriez-vous?
C'est une indication pour le compilateur que la variable sera fortement utilisée et que vous recommandez qu'elle soit conservée dans un registre de processeur si possible.
La plupart des compilateurs modernes le font automatiquement et sont meilleurs à les choisir que nous, les humains.
Eh bien, j'ai expérimenté le registre pour modifier mes soumissions ACM, et parfois cela a vraiment aidé. Mais il faut vraiment faire attention, car les mauvais choix dégradent les performances.
ypnos
81
Une bonne raison de ne pas utiliser 'register': vous ne pouvez pas prendre l'adresse d'une variable déclarée 'register'
Adam Rosenfield
23
Notez que certains / plusieurs compilateurs ignoreront complètement le mot clé register (qui est parfaitement légal).
Euro Micelli
5
ypnos: En fait, la vitesse d'une solution aux problèmes ACM ICPC dépend beaucoup plus du choix de l'algorithme que de telles micro-optimisations. Le délai de 5 secondes est généralement suffisant pour une solution correcte, surtout lorsque vous utilisez C au lieu de Java.
Joey
66
@Euro: Vous le savez probablement, mais juste pour être explicite, le compilateur est nécessaire pour empêcher que l'adresse d'une registervariable soit prise; c'est le seul effet obligatoire du registermot - clé. Même cela est suffisant pour améliorer les optimisations, car il devient trivial de dire que la variable ne peut être modifiée que dans cette fonction.
Dale Hagglund
69
Je suis surpris que personne n'ait mentionné que vous ne pouvez pas prendre une adresse de variable de registre, même si le compilateur décide de garder la variable en mémoire plutôt que dans le registre.
Donc, en utilisant registervous ne gagnez rien (de toute façon le compilateur décidera par lui-même où mettre la variable) et perdez l' &opérateur - aucune raison de l'utiliser.
Il y a une raison en fait. Le simple fait que vous ne puissiez pas prendre l'adresse de la variable donne des opportunités d'optimisation: le compilateur peut prouver que la variable ne sera pas aliasée.
Alexandre C.
8
Les compilateurs sont notoirement terribles pour prouver que l'aliasing ne se produit pas dans les cas non triviaux, il registerest donc utile pour cela même si le compilateur ne le met pas dans un registre.
Miles Rout
2
@AlexandreC, Miles, les compilateurs sont parfaitement OK pour vérifier si & est pris d'une variable n'importe où. Donc, quelles que soient les autres difficultés liées à la détection de l'aliasing, le retraitement ne vous rapporte rien. Lorsque K + R a créé C pour la première fois, il était en effet utile de savoir à l'avance que & ne serait pas utilisé, car ce compilateur a effectivement pris la décision d'allocation de registre en voyant la déclaration, avant de regarder le code suivant. C'est pourquoi l'interdiction est en place. Le mot-clé «register» est essentiellement obsolète maintenant.
greggo
25
Par cette logique constest également inutile car il ne vous gagne rien, vous perdez seulement la possibilité de changer une variable. registerpourrait être utilisé pour s'assurer que personne ne prendra l'adresse d'une variable à l'avenir sans réfléchir. Je n'ai cependant jamais eu de raison de l'utiliser register.
Tor Klingberg
34
Il indique au compilateur d'essayer d'utiliser un registre CPU, au lieu de RAM, pour stocker la variable. Les registres sont dans le CPU et beaucoup plus rapides d'accès que la RAM. Mais ce n'est qu'une suggestion pour le compilateur, et cela peut ne pas suivre.
Ajouter une valeur pour les personnes utilisant C ++, C ++ vous permet de prendre l'adresse d'une variable de registre
Will
5
@Will: ... mais le compilateur finira probablement par ignorer le mot-clé. Voir ma réponse.
bwDraco
Oui, il semble que «register» soit un placebo en C ++, il est juste là pour permettre la compilation du code C en C ++. Et cela n'aurait pas beaucoup de sens d'interdire & var tout en permettant de le passer par référence, ou const-reference, et sans passer par référence, vous avez sérieusement cassé C ++.
greggo
22
Je sais que cette question concerne C, mais la même question pour C ++ a été fermée en tant que doublon exact de cette question. Cette réponse peut donc ne pas s'appliquer à C.
Le dernier projet de la norme C ++ 11, N3485 , le dit dans 7.1.1 / 3:
Un registerspécificateur est un indice de l'implémentation que la variable ainsi déclarée sera largement utilisée. [ note: L'indice peut être ignoré et dans la plupart des implémentations, il sera ignoré si l'adresse de la variable est prise. Cette utilisation est déconseillée ... —fin note ]
En C ++ (mais pas en C), la norme n'indique pas que vous ne pouvez pas prendre l'adresse d'une variable déclarée register; cependant, comme une variable stockée dans un registre CPU tout au long de sa durée de vie n'a pas d'emplacement mémoire associé, la tentative de prendre son adresse serait invalide et le compilateur ignorera le registermot clé pour autoriser la prise de l'adresse.
Cela n'a pas été pertinent depuis au moins 15 ans, car les optimiseurs prennent de meilleures décisions à ce sujet que vous ne le pouvez. Même quand c'était pertinent, cela avait beaucoup plus de sens sur une architecture CPU avec beaucoup de registres, comme SPARC ou M68000 que sur Intel avec sa rareté de registres, dont la plupart sont réservés par le compilateur à ses propres fins.
En fait, register indique au compilateur que la variable n'a pas d'alias avec quoi que ce soit d'autre dans le programme (pas même les caractères).
Cela peut être exploité par les compilateurs modernes dans diverses situations et peut aider le compilateur dans un code complexe - dans un code simple, les compilateurs peuvent le comprendre par eux-mêmes.
Sinon, il ne sert à rien et n'est pas utilisé pour l'attribution des registres. Il n'entraîne généralement pas de dégradation des performances pour le spécifier, tant que votre compilateur est suffisamment moderne.
"dit au compilateur .." non, ce n'est pas le cas. Toutes les variables automatiques ont cette propriété, sauf si vous prenez son adresse et l' utilisez d'une manière qui dépasse certaines utilisations analysables. Ainsi, le compilateur le sait grâce au code, que vous utilisiez ou non le mot-clé register. Il se trouve que le mot clé «register» rend l'écriture d'une telle construction illégale, mais si vous n'utilisez pas le mot clé et ne prenez pas l'adresse de cette manière, le compilateur sait toujours que c'est sûr. Ces informations sont cruciales pour l'optimisation.
greggo
1
@greggo: Dommage registerinterdit de prendre l'adresse du tout, car sinon il pourrait être utile d'informer les compilateurs des cas où un compilateur pourrait appliquer des optimisations de registre malgré le fait que l'adresse d'une variable soit passée à une fonction externe (la variable devrait être vidé en mémoire pour cet appel particulier , mais une fois la fonction de retour, le compilateur pourrait à nouveau la traiter comme une variable dont l'adresse n'a jamais été prise).
supercat
@supercat Je pense que ce serait encore une conversation très délicate à avoir avec le compilateur. Si c'est ce que vous voulez dire au compilateur, vous pouvez le faire en copiant la première variable dans une seconde qui n'a pas de '&' dessus, puis en n'utilisant plus jamais la première.
greggo
1
@greggo: Dire que si barest une registervariable, un compilateur peut à sa guise remplacer foo(&bar);par int temp=bar; foo(&temp); bar=temp;, mais prendre l'adresse de barserait interdit dans la plupart des autres contextes ne semblerait pas être une règle trop compliquée. Si la variable pouvait autrement être conservée dans un registre, la substitution rendrait le code plus petit. Si la variable devait de toute façon être conservée dans la RAM, la substitution augmenterait le code. Laisser la question de l'opportunité de faire la substitution au compilateur conduirait à un meilleur code dans les deux cas.
supercat
1
@greggo: Permettre une registerqualification sur des variables globales, que le compilateur permette ou non de prendre l'adresse, permettrait de belles optimisations dans les cas où une fonction en ligne qui utilise une variable globale est appelée à plusieurs reprises dans une boucle. Je ne peux pas penser à une autre façon de laisser cette variable dans un registre entre les itérations de boucle - pouvez-vous?
supercat
13
J'ai lu qu'il est utilisé pour l'optimisation mais n'est clairement défini dans aucune norme.
En fait, il est clairement défini par la norme C. Citant le projet de section 6.7.1 N1570 , paragraphe 6 (les autres versions ont le même libellé):
Une déclaration d'un identifiant pour un objet avec un spécificateur de classe de stockage registersuggère que l'accès à l'objet soit aussi rapide que possible. La mesure dans laquelle ces suggestions sont efficaces est définie par la mise en œuvre.
L' &opérateur unaire ne peut pas être appliqué à un objet défini avec registeret registerne peut pas être utilisé dans une déclaration externe.
Il existe quelques autres règles (assez obscures) spécifiques aux registerobjets qualifiés:
La définition d'un objet tableau avec registerun comportement indéfini. Correction: il est légal de définir un objet tableau avec register, mais vous ne pouvez rien faire d'utile avec un tel objet (l'indexation dans un tableau nécessite de prendre l'adresse de son élément initial).
Le _Alignasspécificateur (nouveau dans C11) ne peut pas être appliqué à un tel objet.
Si le nom de paramètre transmis à la va_startmacro est registerqualifié, le comportement n'est pas défini.
Il peut y en avoir quelques autres; téléchargez un brouillon de la norme et recherchez «s'inscrire» si vous êtes intéressé.
Comme son nom l'indique, la signification d' origine de registerétait d'exiger qu'un objet soit stocké dans un registre CPU. Mais avec l'amélioration de l'optimisation des compilateurs, cela est devenu moins utile. Les versions modernes de la norme C ne font pas référence aux registres du processeur, car elles ne supposent plus (ont besoin) qu'il existe une telle chose (il existe des architectures qui n'utilisent pas de registres). La sagesse courante est que l'application registerà une déclaration d'objet est plus susceptible d' aggraver le code généré, car elle interfère avec l'allocation de registre du compilateur. Il peut encore y avoir quelques cas où cela est utile (par exemple, si vous savez vraiment à quelle fréquence une variable sera consultée et que vos connaissances sont meilleures que ce qu'un compilateur d'optimisation moderne peut comprendre).
Le principal effet tangible de cela registerest qu'il empêche toute tentative de prendre l'adresse d'un objet. Cela n'est pas particulièrement utile comme indice d'optimisation, car il ne peut être appliqué qu'aux variables locales, et un compilateur d'optimisation peut constater par lui-même que l'adresse d'un tel objet n'est pas prise.
le comportement de ce programme est-il vraiment indéfini selon la norme C? Est-il bien défini en C ++? Je pense que c'est bien défini en C ++.
Destructor
@Destructor: Pourquoi ne serait-il pas défini? Il n'y a pas registerd'objet tableau qualifié, si c'est ce que vous pensez.
Keith Thompson
Oh désolé, j'ai oublié d'écrire le mot-clé de registre dans la déclaration de tableau dans main (). Est-il bien défini en C ++?
Destructor
J'avais tort de définir les registerobjets du tableau; voir le premier point mis à jour dans ma réponse. Il est légal de définir un tel objet, mais vous ne pouvez rien en faire. Si vous ajoutez registerà la définition de sdans votre exemple , le programme est illégal (une violation de contrainte) en C. C ++ ne place pas les mêmes restrictions register, donc le programme serait C ++ valide (mais l'utilisation registerserait inutile).
Keith Thompson
@KeithThompson: le registermot-clé pourrait être utile s'il était légal de prendre l'adresse d'une telle variable, mais uniquement dans les cas où la sémantique ne serait pas affectée en copiant la variable dans un temporaire lorsque son adresse est prise et en la rechargeant à partir du temporaire au point de séquence suivant. Cela permettrait aux compilateurs de supposer que la variable peut être conservée en toute sécurité dans un registre à travers tous les accès de pointeur à condition qu'elle soit vidée à tous les endroits où son adresse est prise.
supercat
9
L'heure du conte!
C, en tant que langage, est une abstraction d'un ordinateur. Il vous permet de faire des choses, en termes de ce que fait un ordinateur, c'est-à-dire manipuler la mémoire, faire des mathématiques, imprimer des choses, etc.
Mais C n'est qu'une abstraction. Et finalement, ce qu'il extrait de vous, c'est le langage d'assemblage. L'assemblage est le langage qu'un CPU lit, et si vous l'utilisez, vous faites les choses en termes de CPU. Que fait un CPU? Fondamentalement, il lit de la mémoire, fait des calculs et écrit dans la mémoire. Le CPU ne fait pas seulement des calculs sur les nombres en mémoire. Tout d'abord, vous devez déplacer un nombre de mémoire en mémoire à l'intérieur du CPU appelé registre. Une fois que vous avez fait tout ce que vous devez faire pour ce numéro, vous pouvez le ramener dans la mémoire système normale. Pourquoi utiliser la mémoire système? Les registres sont limités en nombre. Vous n'obtenez qu'une centaine d'octets dans les processeurs modernes, et les processeurs populaires plus anciens étaient encore plus fantastiquement limités (le 6502 avait 3 registres 8 bits pour votre utilisation gratuite). Ainsi, votre opération mathématique moyenne ressemble à:
load first number from memory
load second number from memory
add the two
store answer into memory
Beaucoup de cela n'est ... pas des mathématiques. Ces opérations de chargement et de stockage peuvent prendre jusqu'à la moitié de votre temps de traitement. C, étant une abstraction d'ordinateurs, a libéré le programmeur du souci d'utiliser et de jongler avec les registres, et puisque le nombre et le type varient selon les ordinateurs, C place la responsabilité de l'allocation des registres uniquement sur le compilateur. À une exception près.
Lorsque vous déclarez une variable register, vous dites au compilateur "Yo, j'ai l'intention que cette variable soit beaucoup utilisée et / ou soit de courte durée. Si j'étais vous, j'essaierais de la garder dans un registre." Lorsque la norme C dit que les compilateurs n'ont rien à faire, c'est parce que la norme C ne sait pas pour quel ordinateur vous compilez, et cela pourrait être comme le 6502 ci-dessus, où les 3 registres sont nécessaires uniquement pour fonctionner , et il n'y a pas de registre de réserve pour conserver votre numéro. Cependant, quand il dit que vous ne pouvez pas prendre l'adresse, c'est parce que les registres n'ont pas d'adresse. Ce sont les mains du processeur. Étant donné que le compilateur n'a pas à vous donner d'adresse, et comme il ne peut jamais avoir d'adresse du tout, plusieurs optimisations sont désormais ouvertes au compilateur. Il pourrait, par exemple, toujours conserver le numéro dans un registre. C'est pas ca' t avoir à se soucier de l'endroit où il est stocké dans la mémoire de l'ordinateur (au-delà de la nécessité de le récupérer à nouveau). Il pourrait même le punter dans une autre variable, le donner à un autre processeur, lui donner un emplacement changeant, etc.
tl; dr: variables de courte durée qui font beaucoup de calculs. N'en déclarez pas trop à la fois.
Vous jouez avec l'algorithme sophistiqué de coloration des graphes du compilateur. Ceci est utilisé pour l'attribution des registres. Enfin, surtout. Il agit comme un indice pour le compilateur - c'est vrai. Mais pas ignoré dans son intégralité car vous n'êtes pas autorisé à prendre l'adresse d'une variable de registre (rappelez-vous que le compilateur, à votre merci, essaiera d'agir différemment). Ce qui, d'une certaine manière, vous dit de ne pas l'utiliser.
Le mot-clé a été utilisé depuis très longtemps. Quand il y avait seulement si peu de registres qui pouvaient tous les compter avec votre index.
Mais, comme je l'ai dit, obsolète ne signifie pas que vous ne pouvez pas l'utiliser.
Certains des matériels plus anciens avaient plus de registres que les machines Intel modernes. Le nombre de registres n'a rien à voir avec l'âge et tout à voir avec l'architecture du processeur.
JUSTE MON AVIS correct
2
@JUSTMYcorrectOPINION En effet, X86 en a fondamentalement six en tout, laissant au plus 1 ou 2 pour dédicace à «s'inscrire». En fait, étant donné que tant de code a été écrit ou porté sur une machine pauvre en registres, je soupçonne que cela a grandement contribué à ce que le mot clé `` registre '' devienne un placebo - inutile de suggérer des registres lorsqu'il n'y en a pas. Ici, nous sommes 4+ ans plus tard et heureusement, x86_64 est passé à 14, et ARM est maintenant une grande chose.
greggo
4
Juste une petite démo (sans but réel) pour comparaison: lors de la suppression des registermots clés avant chaque variable, ce morceau de code prend 3,41 secondes sur mon i7 (GCC), avecregister le même code se termine en 0,7 secondes.
Avec gcc 4.8.4 et -O3, je n'ai aucune différence. Sans -O3 et 40000 itérations, j'obtiens peut-être 50 ms de moins sur un temps total de 1,5 s, mais je ne l'ai pas exécuté suffisamment de fois pour savoir si cela était même statistiquement significatif.
zstewart
Il n'y a aucune différence avec CLANG 5.0, la plate-forme est AMD64. (J'ai vérifié la sortie ASM.)
ern0
4
J'ai testé le mot clé register sous QNX 6.5.0 en utilisant le code suivant:
#include<stdlib.h>#include<stdio.h>#include<inttypes.h>#include<sys/neutrino.h>#include<sys/syspage.h>int main(int argc,char*argv[]){uint64_t cps, cycle1, cycle2, ncycles;double sec;registerint a=0, b =1, c =3, i;
cycle1 =ClockCycles();for(i =0; i <100000000; i++)
a =((a + b + c)* c)/2;
cycle2 =ClockCycles();
ncycles = cycle2 - cycle1;
printf("%lld cycles elapsed\n", ncycles);
cps = SYSPAGE_ENTRY(qtime)-> cycles_per_sec;
printf("This system has %lld cycles per second\n", cps);
sec =(double)ncycles/cps;
printf("The cycles in seconds is %f\n", sec);return EXIT_SUCCESS;}
Register informerait le compilateur que le codeur pensait que cette variable serait suffisamment écrite / lue pour justifier son stockage dans l'un des rares registres disponibles pour une utilisation variable. La lecture / écriture à partir des registres est généralement plus rapide et peut nécessiter un ensemble de codes d'opération plus petit.
De nos jours, cela n'est pas très utile, car la plupart des optimiseurs de compilateurs sont meilleurs que vous pour déterminer si un registre doit être utilisé pour cette variable et pour combien de temps.
Au cours des années soixante-dix, au tout début du langage C, le mot-clé register a été introduit afin de permettre au programmeur de donner des conseils au compilateur, lui disant que la variable serait utilisée très souvent, et qu'il devrait être judicieux de conserver sa valeur dans l'un des registres internes du processeur.
De nos jours, les optimiseurs sont beaucoup plus efficaces que les programmeurs pour déterminer les variables qui sont plus susceptibles d'être conservées dans les registres, et l'optimiseur ne prend pas toujours en compte l'indication du programmeur.
Tant de gens recommandent à tort de ne pas utiliser le mot-clé d'enregistrement.
Voyons pourquoi!
Le mot-clé de registre a un effet secondaire associé: vous ne pouvez pas référencer (obtenir l'adresse de) une variable de type de registre.
Les personnes conseillant aux autres de ne pas utiliser les registres prennent à tort cela comme un argument supplémentaire.
Cependant, le simple fait de savoir que vous ne pouvez pas prendre l'adresse d'une variable de registre, permet au compilateur (et à son optimiseur) de savoir que la valeur de cette variable ne peut pas être modifiée indirectement via un pointeur.
Lorsqu'à un certain point du flux d'instructions, une variable de registre a sa valeur affectée dans le registre d'un processeur et que le registre n'a pas été utilisé depuis pour obtenir la valeur d'une autre variable, le compilateur sait qu'il n'a pas besoin de recharger la valeur de la variable dans ce registre. Cela permet d'éviter un accès mémoire coûteux et inutile.
Faites vos propres tests et vous obtiendrez des améliorations de performances significatives dans vos boucles les plus internes.
Sur les compilateurs C pris en charge, il essaie d'optimiser le code afin que la valeur de la variable soit conservée dans un registre de processeur réel.
Le compilateur Visual C ++ de Microsoft ignore le registermot clé lorsque l'optimisation globale d'allocation de registre (l'indicateur de compilateur / Oe) est activée.
Le mot-clé Register indique au compilateur de stocker la variable particulière dans les registres CPU afin qu'elle puisse être accessible rapidement. Du point de vue d'un programmeur, le mot-clé de registre est utilisé pour les variables qui sont fortement utilisées dans un programme, afin que le compilateur puisse accélérer le code. Bien que cela dépende du compilateur de conserver la variable dans les registres CPU ou la mémoire principale.
Register indique au compilateur d'optimiser ce code en stockant cette variable particulière dans des registres puis en mémoire. c'est une demande au compilateur, le compilateur peut ou non considérer cette demande. Vous pouvez utiliser cette fonction au cas où certaines de vos variables sont consultées très fréquemment. Par exemple: une boucle.
Une autre chose est que si vous déclarez une variable comme registre, vous ne pouvez pas obtenir son adresse car elle n'est pas stockée en mémoire. il obtient son allocation dans le registre CPU.
sortie asm de gcc 9.3, sans utiliser de drapeaux d'optimisation (tout dans cette réponse fait référence à une compilation standard sans drapeaux d'optimisation):
#include<stdio.h>int main(void){int i =3;
i++;
printf("%d", i);return0;}
#include<stdio.h>int main(void){registerint i =3;
i++;
printf("%d", i);return0;}
.LC0:.string "%d"
main:
push rbp
mov rbp, rsp
push rbx
sub rsp,8
mov ebx,3
add ebx,1
mov esi, ebx
mov edi, OFFSET FLAT:.LC0
mov eax,0
call printf
add rsp,8
pop rbx
pop rbp
ret
Cela force ebxà être utilisé pour le calcul, ce qui signifie qu'il doit être poussé vers la pile et restauré à la fin de la fonction car il est sauvegardé. registerproduit plus de lignes de code et 1 écriture en mémoire et 1 lecture en mémoire (bien qu'en réalité, cela aurait pu être optimisé à 0 R / W si le calcul avait été fait en esi, ce qui se passe en utilisant C ++ const register). Le fait de ne pas utiliser registerentraîne 2 écritures et 1 lecture (bien que le stockage pour charger le transfert se produise lors de la lecture). En effet, la valeur doit être présente et mise à jour directement sur la pile afin que la valeur correcte puisse être lue par adresse (pointeur). registern'a pas cette exigence et ne peut pas être pointé. constet registersont fondamentalement l'opposé de volatileet en utilisantvolatileremplacera les optimisations const à la portée du fichier et du bloc et les registeroptimisations à la portée du bloc. const registeret registerproduira des sorties identiques parce que const ne fait rien sur C à l'étendue du bloc, donc seules les registeroptimisations s'appliquent.
Sur clang, registerest ignoré mais des constoptimisations se produisent toujours.
register
variable.Réponses:
C'est une indication pour le compilateur que la variable sera fortement utilisée et que vous recommandez qu'elle soit conservée dans un registre de processeur si possible.
La plupart des compilateurs modernes le font automatiquement et sont meilleurs à les choisir que nous, les humains.
la source
register
variable soit prise; c'est le seul effet obligatoire duregister
mot - clé. Même cela est suffisant pour améliorer les optimisations, car il devient trivial de dire que la variable ne peut être modifiée que dans cette fonction.Je suis surpris que personne n'ait mentionné que vous ne pouvez pas prendre une adresse de variable de registre, même si le compilateur décide de garder la variable en mémoire plutôt que dans le registre.
Donc, en utilisant
register
vous ne gagnez rien (de toute façon le compilateur décidera par lui-même où mettre la variable) et perdez l'&
opérateur - aucune raison de l'utiliser.la source
register
est donc utile pour cela même si le compilateur ne le met pas dans un registre.const
est également inutile car il ne vous gagne rien, vous perdez seulement la possibilité de changer une variable.register
pourrait être utilisé pour s'assurer que personne ne prendra l'adresse d'une variable à l'avenir sans réfléchir. Je n'ai cependant jamais eu de raison de l'utiliserregister
.Il indique au compilateur d'essayer d'utiliser un registre CPU, au lieu de RAM, pour stocker la variable. Les registres sont dans le CPU et beaucoup plus rapides d'accès que la RAM. Mais ce n'est qu'une suggestion pour le compilateur, et cela peut ne pas suivre.
la source
Je sais que cette question concerne C, mais la même question pour C ++ a été fermée en tant que doublon exact de cette question. Cette réponse peut donc ne pas s'appliquer à C.
Le dernier projet de la norme C ++ 11, N3485 , le dit dans 7.1.1 / 3:
En C ++ (mais pas en C), la norme n'indique pas que vous ne pouvez pas prendre l'adresse d'une variable déclarée
register
; cependant, comme une variable stockée dans un registre CPU tout au long de sa durée de vie n'a pas d'emplacement mémoire associé, la tentative de prendre son adresse serait invalide et le compilateur ignorera leregister
mot clé pour autoriser la prise de l'adresse.la source
Cela n'a pas été pertinent depuis au moins 15 ans, car les optimiseurs prennent de meilleures décisions à ce sujet que vous ne le pouvez. Même quand c'était pertinent, cela avait beaucoup plus de sens sur une architecture CPU avec beaucoup de registres, comme SPARC ou M68000 que sur Intel avec sa rareté de registres, dont la plupart sont réservés par le compilateur à ses propres fins.
la source
En fait, register indique au compilateur que la variable n'a pas d'alias avec quoi que ce soit d'autre dans le programme (pas même les caractères).
Cela peut être exploité par les compilateurs modernes dans diverses situations et peut aider le compilateur dans un code complexe - dans un code simple, les compilateurs peuvent le comprendre par eux-mêmes.
Sinon, il ne sert à rien et n'est pas utilisé pour l'attribution des registres. Il n'entraîne généralement pas de dégradation des performances pour le spécifier, tant que votre compilateur est suffisamment moderne.
la source
register
interdit de prendre l'adresse du tout, car sinon il pourrait être utile d'informer les compilateurs des cas où un compilateur pourrait appliquer des optimisations de registre malgré le fait que l'adresse d'une variable soit passée à une fonction externe (la variable devrait être vidé en mémoire pour cet appel particulier , mais une fois la fonction de retour, le compilateur pourrait à nouveau la traiter comme une variable dont l'adresse n'a jamais été prise).bar
est uneregister
variable, un compilateur peut à sa guise remplacerfoo(&bar);
parint temp=bar; foo(&temp); bar=temp;
, mais prendre l'adresse debar
serait interdit dans la plupart des autres contextes ne semblerait pas être une règle trop compliquée. Si la variable pouvait autrement être conservée dans un registre, la substitution rendrait le code plus petit. Si la variable devait de toute façon être conservée dans la RAM, la substitution augmenterait le code. Laisser la question de l'opportunité de faire la substitution au compilateur conduirait à un meilleur code dans les deux cas.register
qualification sur des variables globales, que le compilateur permette ou non de prendre l'adresse, permettrait de belles optimisations dans les cas où une fonction en ligne qui utilise une variable globale est appelée à plusieurs reprises dans une boucle. Je ne peux pas penser à une autre façon de laisser cette variable dans un registre entre les itérations de boucle - pouvez-vous?En fait, il est clairement défini par la norme C. Citant le projet de section 6.7.1 N1570 , paragraphe 6 (les autres versions ont le même libellé):
L'
&
opérateur unaire ne peut pas être appliqué à un objet défini avecregister
etregister
ne peut pas être utilisé dans une déclaration externe.Il existe quelques autres règles (assez obscures) spécifiques aux
register
objets qualifiés:La définition d'un objet tableau avecregister
un comportement indéfini.Correction: il est légal de définir un objet tableau avec
register
, mais vous ne pouvez rien faire d'utile avec un tel objet (l'indexation dans un tableau nécessite de prendre l'adresse de son élément initial)._Alignas
spécificateur (nouveau dans C11) ne peut pas être appliqué à un tel objet.va_start
macro estregister
qualifié, le comportement n'est pas défini.Il peut y en avoir quelques autres; téléchargez un brouillon de la norme et recherchez «s'inscrire» si vous êtes intéressé.
Comme son nom l'indique, la signification d' origine de
register
était d'exiger qu'un objet soit stocké dans un registre CPU. Mais avec l'amélioration de l'optimisation des compilateurs, cela est devenu moins utile. Les versions modernes de la norme C ne font pas référence aux registres du processeur, car elles ne supposent plus (ont besoin) qu'il existe une telle chose (il existe des architectures qui n'utilisent pas de registres). La sagesse courante est que l'applicationregister
à une déclaration d'objet est plus susceptible d' aggraver le code généré, car elle interfère avec l'allocation de registre du compilateur. Il peut encore y avoir quelques cas où cela est utile (par exemple, si vous savez vraiment à quelle fréquence une variable sera consultée et que vos connaissances sont meilleures que ce qu'un compilateur d'optimisation moderne peut comprendre).Le principal effet tangible de cela
register
est qu'il empêche toute tentative de prendre l'adresse d'un objet. Cela n'est pas particulièrement utile comme indice d'optimisation, car il ne peut être appliqué qu'aux variables locales, et un compilateur d'optimisation peut constater par lui-même que l'adresse d'un tel objet n'est pas prise.la source
register
d'objet tableau qualifié, si c'est ce que vous pensez.register
objets du tableau; voir le premier point mis à jour dans ma réponse. Il est légal de définir un tel objet, mais vous ne pouvez rien en faire. Si vous ajoutezregister
à la définition des
dans votre exemple , le programme est illégal (une violation de contrainte) en C. C ++ ne place pas les mêmes restrictionsregister
, donc le programme serait C ++ valide (mais l'utilisationregister
serait inutile).register
mot-clé pourrait être utile s'il était légal de prendre l'adresse d'une telle variable, mais uniquement dans les cas où la sémantique ne serait pas affectée en copiant la variable dans un temporaire lorsque son adresse est prise et en la rechargeant à partir du temporaire au point de séquence suivant. Cela permettrait aux compilateurs de supposer que la variable peut être conservée en toute sécurité dans un registre à travers tous les accès de pointeur à condition qu'elle soit vidée à tous les endroits où son adresse est prise.L'heure du conte!
C, en tant que langage, est une abstraction d'un ordinateur. Il vous permet de faire des choses, en termes de ce que fait un ordinateur, c'est-à-dire manipuler la mémoire, faire des mathématiques, imprimer des choses, etc.
Mais C n'est qu'une abstraction. Et finalement, ce qu'il extrait de vous, c'est le langage d'assemblage. L'assemblage est le langage qu'un CPU lit, et si vous l'utilisez, vous faites les choses en termes de CPU. Que fait un CPU? Fondamentalement, il lit de la mémoire, fait des calculs et écrit dans la mémoire. Le CPU ne fait pas seulement des calculs sur les nombres en mémoire. Tout d'abord, vous devez déplacer un nombre de mémoire en mémoire à l'intérieur du CPU appelé registre. Une fois que vous avez fait tout ce que vous devez faire pour ce numéro, vous pouvez le ramener dans la mémoire système normale. Pourquoi utiliser la mémoire système? Les registres sont limités en nombre. Vous n'obtenez qu'une centaine d'octets dans les processeurs modernes, et les processeurs populaires plus anciens étaient encore plus fantastiquement limités (le 6502 avait 3 registres 8 bits pour votre utilisation gratuite). Ainsi, votre opération mathématique moyenne ressemble à:
Beaucoup de cela n'est ... pas des mathématiques. Ces opérations de chargement et de stockage peuvent prendre jusqu'à la moitié de votre temps de traitement. C, étant une abstraction d'ordinateurs, a libéré le programmeur du souci d'utiliser et de jongler avec les registres, et puisque le nombre et le type varient selon les ordinateurs, C place la responsabilité de l'allocation des registres uniquement sur le compilateur. À une exception près.
Lorsque vous déclarez une variable
register
, vous dites au compilateur "Yo, j'ai l'intention que cette variable soit beaucoup utilisée et / ou soit de courte durée. Si j'étais vous, j'essaierais de la garder dans un registre." Lorsque la norme C dit que les compilateurs n'ont rien à faire, c'est parce que la norme C ne sait pas pour quel ordinateur vous compilez, et cela pourrait être comme le 6502 ci-dessus, où les 3 registres sont nécessaires uniquement pour fonctionner , et il n'y a pas de registre de réserve pour conserver votre numéro. Cependant, quand il dit que vous ne pouvez pas prendre l'adresse, c'est parce que les registres n'ont pas d'adresse. Ce sont les mains du processeur. Étant donné que le compilateur n'a pas à vous donner d'adresse, et comme il ne peut jamais avoir d'adresse du tout, plusieurs optimisations sont désormais ouvertes au compilateur. Il pourrait, par exemple, toujours conserver le numéro dans un registre. C'est pas ca' t avoir à se soucier de l'endroit où il est stocké dans la mémoire de l'ordinateur (au-delà de la nécessité de le récupérer à nouveau). Il pourrait même le punter dans une autre variable, le donner à un autre processeur, lui donner un emplacement changeant, etc.tl; dr: variables de courte durée qui font beaucoup de calculs. N'en déclarez pas trop à la fois.
la source
Vous jouez avec l'algorithme sophistiqué de coloration des graphes du compilateur. Ceci est utilisé pour l'attribution des registres. Enfin, surtout. Il agit comme un indice pour le compilateur - c'est vrai. Mais pas ignoré dans son intégralité car vous n'êtes pas autorisé à prendre l'adresse d'une variable de registre (rappelez-vous que le compilateur, à votre merci, essaiera d'agir différemment). Ce qui, d'une certaine manière, vous dit de ne pas l'utiliser.
Le mot-clé a été utilisé depuis très longtemps. Quand il y avait seulement si peu de registres qui pouvaient tous les compter avec votre index.
Mais, comme je l'ai dit, obsolète ne signifie pas que vous ne pouvez pas l'utiliser.
la source
Juste une petite démo (sans but réel) pour comparaison: lors de la suppression des
register
mots clés avant chaque variable, ce morceau de code prend 3,41 secondes sur mon i7 (GCC), avecregister
le même code se termine en 0,7 secondes.la source
J'ai testé le mot clé register sous QNX 6.5.0 en utilisant le code suivant:
J'ai obtenu les résultats suivants:
-> 807679611 cycles écoulés
-> Ce système a 3300830000 cycles par seconde
-> Les cycles en secondes sont ~ 0.244600
Et maintenant sans vous inscrire:
J'ai eu:
-> 1421694077 cycles écoulés
-> Ce système a 3300830000 cycles par seconde
-> Le nombre de cycles en secondes est ~ 0.430700
la source
Register informerait le compilateur que le codeur pensait que cette variable serait suffisamment écrite / lue pour justifier son stockage dans l'un des rares registres disponibles pour une utilisation variable. La lecture / écriture à partir des registres est généralement plus rapide et peut nécessiter un ensemble de codes d'opération plus petit.
De nos jours, cela n'est pas très utile, car la plupart des optimiseurs de compilateurs sont meilleurs que vous pour déterminer si un registre doit être utilisé pour cette variable et pour combien de temps.
la source
Au cours des années soixante-dix, au tout début du langage C, le mot-clé register a été introduit afin de permettre au programmeur de donner des conseils au compilateur, lui disant que la variable serait utilisée très souvent, et qu'il devrait être judicieux de conserver sa valeur dans l'un des registres internes du processeur.
De nos jours, les optimiseurs sont beaucoup plus efficaces que les programmeurs pour déterminer les variables qui sont plus susceptibles d'être conservées dans les registres, et l'optimiseur ne prend pas toujours en compte l'indication du programmeur.
Tant de gens recommandent à tort de ne pas utiliser le mot-clé d'enregistrement.
Voyons pourquoi!
Le mot-clé de registre a un effet secondaire associé: vous ne pouvez pas référencer (obtenir l'adresse de) une variable de type de registre.
Les personnes conseillant aux autres de ne pas utiliser les registres prennent à tort cela comme un argument supplémentaire.
Cependant, le simple fait de savoir que vous ne pouvez pas prendre l'adresse d'une variable de registre, permet au compilateur (et à son optimiseur) de savoir que la valeur de cette variable ne peut pas être modifiée indirectement via un pointeur.
Lorsqu'à un certain point du flux d'instructions, une variable de registre a sa valeur affectée dans le registre d'un processeur et que le registre n'a pas été utilisé depuis pour obtenir la valeur d'une autre variable, le compilateur sait qu'il n'a pas besoin de recharger la valeur de la variable dans ce registre. Cela permet d'éviter un accès mémoire coûteux et inutile.
Faites vos propres tests et vous obtiendrez des améliorations de performances significatives dans vos boucles les plus internes.
c_register_side_effect_performance_boost
la source
Sur les compilateurs C pris en charge, il essaie d'optimiser le code afin que la valeur de la variable soit conservée dans un registre de processeur réel.
la source
Le compilateur Visual C ++ de Microsoft ignore le
register
mot clé lorsque l'optimisation globale d'allocation de registre (l'indicateur de compilateur / Oe) est activée.Voir enregistrer un mot-clé sur MSDN.
la source
Le mot-clé Register indique au compilateur de stocker la variable particulière dans les registres CPU afin qu'elle puisse être accessible rapidement. Du point de vue d'un programmeur, le mot-clé de registre est utilisé pour les variables qui sont fortement utilisées dans un programme, afin que le compilateur puisse accélérer le code. Bien que cela dépende du compilateur de conserver la variable dans les registres CPU ou la mémoire principale.
la source
Register indique au compilateur d'optimiser ce code en stockant cette variable particulière dans des registres puis en mémoire. c'est une demande au compilateur, le compilateur peut ou non considérer cette demande. Vous pouvez utiliser cette fonction au cas où certaines de vos variables sont consultées très fréquemment. Par exemple: une boucle.
Une autre chose est que si vous déclarez une variable comme registre, vous ne pouvez pas obtenir son adresse car elle n'est pas stockée en mémoire. il obtient son allocation dans le registre CPU.
la source
sortie asm de gcc 9.3, sans utiliser de drapeaux d'optimisation (tout dans cette réponse fait référence à une compilation standard sans drapeaux d'optimisation):
Cela force
ebx
à être utilisé pour le calcul, ce qui signifie qu'il doit être poussé vers la pile et restauré à la fin de la fonction car il est sauvegardé.register
produit plus de lignes de code et 1 écriture en mémoire et 1 lecture en mémoire (bien qu'en réalité, cela aurait pu être optimisé à 0 R / W si le calcul avait été fait enesi
, ce qui se passe en utilisant C ++const register
). Le fait de ne pas utiliserregister
entraîne 2 écritures et 1 lecture (bien que le stockage pour charger le transfert se produise lors de la lecture). En effet, la valeur doit être présente et mise à jour directement sur la pile afin que la valeur correcte puisse être lue par adresse (pointeur).register
n'a pas cette exigence et ne peut pas être pointé.const
etregister
sont fondamentalement l'opposé devolatile
et en utilisantvolatile
remplacera les optimisations const à la portée du fichier et du bloc et lesregister
optimisations à la portée du bloc.const register
etregister
produira des sorties identiques parce que const ne fait rien sur C à l'étendue du bloc, donc seules lesregister
optimisations s'appliquent.Sur clang,
register
est ignoré mais desconst
optimisations se produisent toujours.la source