Was macht das registerSchlüsselwort in C-Sprache? Ich habe gelesen, dass es zur Optimierung verwendet wird, aber in keinem Standard klar definiert ist. Ist es noch relevant und wenn ja, wann würden Sie es verwenden?
Dies ist ein Hinweis für den Compiler, dass die Variable häufig verwendet wird und dass Sie empfehlen, sie nach Möglichkeit in einem Prozessorregister zu speichern.
Die meisten modernen Compiler tun dies automatisch und können sie besser auswählen als wir Menschen.
Nun, ich habe mit register experimentiert, um meine ACM-Einreichungen zu optimieren, und manchmal hat es wirklich geholfen. Aber man muss wirklich vorsichtig sein, weil schlechte Entscheidungen die Leistung beeinträchtigen.
Ypnos
80
Ein guter Grund , 'register' nicht zu verwenden: Sie können die Adresse einer als 'register' deklarierten Variablen nicht übernehmen
Adam Rosenfield
22
Beachten Sie, dass einige / viele Compiler das Register-Schlüsselwort vollständig ignorieren (was vollkommen legal ist).
Euro Micelli
4
ypnos: Tatsächlich hängt die Geschwindigkeit einer Lösung für ACM-ICPC-Probleme viel mehr von der Wahl des Algorithmus ab als von solchen Mikrooptimierungen. Das Zeitlimit von 5 Sekunden reicht normalerweise für eine korrekte Lösung aus, insbesondere wenn C anstelle von Java verwendet wird.
Joey
65
@Euro: Sie wissen das wahrscheinlich, aber um es explizit zu machen, muss der Compiler verhindern, dass die Adresse einer registerVariablen verwendet wird. Dies ist der einzige obligatorische Effekt des registerSchlüsselworts. Auch dies reicht aus, um Optimierungen zu verbessern, da es trivial wird zu sagen, dass die Variable nur innerhalb dieser Funktion geändert werden kann.
Dale Hagglund
69
Ich bin überrascht, dass niemand erwähnt hat, dass Sie keine Adresse der Registervariablen verwenden können, selbst wenn der Compiler beschließt, die Variable im Speicher und nicht im Register zu belassen.
Wenn registerSie also verwenden , gewinnen Sie nichts (der Compiler entscheidet ohnehin selbst, wo die Variable abgelegt werden soll) und verlieren den &Operator - kein Grund, sie zu verwenden.
Es gibt tatsächlich einen Grund. Die bloße Tatsache, dass Sie keine Adresse für die Variable verwenden können, bietet einige Optimierungsmöglichkeiten: Der Compiler kann nachweisen, dass die Variable kein Aliasing aufweist.
Alexandre C.
8
Compiler sind notorisch schrecklich darin zu beweisen, dass Aliasing in nicht trivialen Fällen nicht auftritt. registerDies ist auch dann nützlich, wenn der Compiler es nicht in ein Register einfügt.
Miles Rout
2
@AlexandreC, Miles, Compiler können vollkommen überprüfen, ob & von einer Variablen irgendwo übernommen wird. Unabhängig von anderen Schwierigkeiten beim Erkennen von Aliasing bringt Ihnen das Wiederholen nichts. Als K + R C zum ersten Mal erstellte, war es in der Tat nützlich, im Voraus zu wissen , dass & nicht verwendet werden würde, da dieser Compiler tatsächlich die Entscheidung über die Registerzuordnung beim Anzeigen der Deklaration traf, bevor er sich den folgenden Code ansah. Deshalb ist das Verbot in Kraft. Das Schlüsselwort 'register' ist jetzt im Wesentlichen veraltet.
Greggo
25
Durch diese Logik constist auch nutzlos, da es Ihnen nichts bringt, Sie verlieren nur die Fähigkeit, eine Variable zu ändern. registerkönnte verwendet werden, um sicherzustellen, dass in Zukunft niemand mehr die Adresse einer Variablen nimmt, ohne darüber nachzudenken. Ich hatte jedoch nie Grund, es zu benutzen register.
Tor Klingberg
34
Es weist den Compiler an, zu versuchen, ein CPU-Register anstelle von RAM zum Speichern der Variablen zu verwenden. Register befinden sich in der CPU und sind viel schneller zugänglich als RAM. Dies ist jedoch nur ein Vorschlag an den Compiler, der möglicherweise nicht umgesetzt wird.
Mit C ++ können Sie die Adresse einer Registervariablen übernehmen
Will
5
@ Will: ... aber der Compiler wird wahrscheinlich das Schlüsselwort ignorieren. Siehe meine Antwort.
bwDraco
Ja, es scheint, dass 'register' ein Placebo in C ++ ist. Es dient nur dazu, C-Code als C ++ zu kompilieren. Und es würde nicht viel Sinn machen, & var zu verbieten, während es als Referenz oder const-Referenz übergeben wird, und ohne Referenzübergabe haben Sie C ++ ernsthaft gebrochen.
Greggo
22
Ich weiß, dass es sich bei dieser Frage um C handelt, aber dieselbe Frage für C ++ wurde als genaues Duplikat dieser Frage geschlossen. Diese Antwort gilt daher möglicherweise nicht für C.
Der neueste Entwurf des C ++ 11-Standards, N3485 , besagt dies in 7.1.1 / 3:
Ein registerBezeichner ist ein Hinweis auf die Implementierung, dass die so deklarierte Variable häufig verwendet wird. [ Hinweis: Der Hinweis kann ignoriert werden und wird in den meisten Implementierungen ignoriert, wenn die Adresse der Variablen verwendet wird. Diese Verwendung ist veraltet ... —end note ]
In C ++ (aber nicht in C) gibt der Standard nicht an, dass Sie die Adresse einer deklarierten Variablen nicht übernehmen können register. Da jedoch einer Variablen, die während ihrer gesamten Lebensdauer in einem CPU-Register gespeichert ist, kein Speicherort zugeordnet ist, ist der Versuch, ihre Adresse zu übernehmen, ungültig, und der Compiler ignoriert das registerSchlüsselwort, um die Adresse zu übernehmen.
Es ist seit mindestens 15 Jahren nicht mehr relevant, da Optimierer diesbezüglich bessere Entscheidungen treffen als Sie. Selbst wenn es relevant war, machte es auf einer CPU-Architektur mit vielen Registern wie SPARC oder M68000 viel mehr Sinn als auf Intel mit seinem Mangel an Registern, von denen die meisten vom Compiler für seine eigenen Zwecke reserviert werden.
Tatsächlich teilt register dem Compiler mit, dass die Variable keinen Alias mit irgendetwas anderem im Programm hat (nicht einmal mit Zeichen).
Dies kann von modernen Compilern in einer Vielzahl von Situationen ausgenutzt werden und dem Compiler bei komplexem Code erheblich helfen - in einfachem Code können die Compiler dies selbst herausfinden.
Andernfalls hat es keinen Zweck und wird nicht für die Registerzuordnung verwendet. Normalerweise führt dies nicht zu Leistungseinbußen bei der Angabe, solange Ihr Compiler modern genug ist.
"sagt dem Compiler .." nein, tut es nicht. Alle automatischen Variablen haben diese Eigenschaft, es sei denn, Sie nehmen ihre Adresse und verwenden sie auf eine Weise, die bestimmte analysierbare Verwendungen überschreitet. Der Compiler kennt dies also aus dem Code, unabhängig davon, ob Sie das Schlüsselwort register verwenden. Nun ist es so , dass das ‚Register‘ Keyword macht es illegal ein solches Konstrukt zu schreiben, aber wenn Sie nicht das Schlüsselwort verwenden und nicht die Adresse in einer solchen Art und Weise nehmen, dann noch der Compiler weiß , es ist sicher. Solche Informationen sind für die Optimierung von entscheidender Bedeutung.
Greggo
1
@greggo: Schade, dass registeres überhaupt nicht möglich ist, die Adresse zu übernehmen, da es sonst nützlich sein könnte, Compiler über Fälle zu informieren, in denen ein Compiler Registeroptimierungen anwenden kann, obwohl die Adresse einer Variablen an eine externe Funktion übergeben wird (die Variable müsste) für diesen bestimmten Aufruf in den Speicher geleert werden , aber sobald die Funktion zurückkehrt, kann der Compiler sie wieder als Variable behandeln, deren Adresse nie übernommen wurde.
Superkatze
@supercat Ich denke, es wäre immer noch ein sehr kniffliges Gespräch mit dem Compiler. Wenn Sie dies dem Compiler mitteilen möchten, können Sie dies tun, indem Sie die erste Variable in eine zweite Variable kopieren, auf der kein '&' steht, und dann die erste nie wieder verwenden.
Greggo
1
@greggo: Zu sagen , dass , wenn bareine ist registervariabel, ein Compiler an seiner Freizeit kann ersetzen foo(&bar);mit int temp=bar; foo(&temp); bar=temp;, aber unter der Adresse barwürde in den meisten anderen Kontexten verboten würde nicht wie eine allzu komplizierte Regel zu sein scheint. Wenn die Variable andernfalls in einem Register gespeichert werden könnte, würde die Ersetzung den Code verkleinern. Wenn die Variable ohnehin im RAM bleiben müsste, würde die Ersetzung den Code vergrößern. Die Frage, ob die Ersetzung dem Compiler überlassen werden soll, würde in beiden Fällen zu besserem Code führen.
Supercat
1
@greggo: Das Zulassen einer registerQualifizierung für globale Variablen, unabhängig davon, ob der Compiler die Verwendung der Adresse zulässt oder nicht, würde einige nette Optimierungen ermöglichen, wenn eine Inline-Funktion, die eine globale Variable verwendet, wiederholt in einer Schleife aufgerufen wird. Ich kann mir keine andere Möglichkeit vorstellen, diese Variable zwischen Schleifeniterationen in einem Register zu halten - können Sie?
Supercat
13
Ich habe gelesen, dass es zur Optimierung verwendet wird, aber in keinem Standard klar definiert ist.
Tatsächlich ist es durch den C-Standard klar definiert. Zitieren des N1570-Entwurfs, Abschnitt 6.7.1, Absatz 6 (andere Versionen haben den gleichen Wortlaut):
Eine Deklaration eines Bezeichners für ein Objekt mit einem Speicherklassenspezifizierer legt registernahe, dass der Zugriff auf das Objekt so schnell wie möglich erfolgt. Inwieweit solche Vorschläge wirksam sind, ist umsetzungsdefiniert.
Der unäre &Operator darf nicht auf ein mit definiertes Objekt angewendet registerund registernicht in einer externen Deklaration verwendet werden.
Es gibt einige andere (ziemlich undurchsichtige) Regeln, die für registerqualifizierte Objekte spezifisch sind:
Das Definieren eines Array-Objekts mit registerhat ein undefiniertes Verhalten. Korrektur: Es ist legal, ein Array-Objekt mit zu definieren register, aber Sie können mit einem solchen Objekt nichts Nützliches tun (für die Indizierung in ein Array muss die Adresse seines ursprünglichen Elements verwendet werden).
Der _AlignasBezeichner (neu in C11) kann möglicherweise nicht auf ein solches Objekt angewendet werden.
Wenn der an das va_startMakro registerübergebene Parametername -qualifiziert ist, ist das Verhalten undefiniert.
Es kann einige andere geben; Laden Sie einen Entwurf des Standards herunter und suchen Sie bei Interesse nach "Registrieren".
Wie der Name schon sagt, bestand die ursprüngliche Bedeutung von darin register, dass ein Objekt in einem CPU-Register gespeichert werden muss. Mit Verbesserungen bei der Optimierung von Compilern ist dies jedoch weniger nützlich geworden. Moderne Versionen des C-Standards beziehen sich nicht auf CPU-Register, da sie nicht mehr davon ausgehen (müssen), dass es so etwas gibt (es gibt Architekturen, die keine Register verwenden). Es ist allgemein bekannt, dass das Anwenden registerauf eine Objektdeklaration den generierten Code mit größerer Wahrscheinlichkeit verschlechtert , da dies die Registerzuordnung des Compilers beeinträchtigt. Es kann immer noch einige Fälle geben, in denen dies nützlich ist (z. B. wenn Sie wirklich wissen, wie oft auf eine Variable zugegriffen wird und Ihr Wissen besser ist als das, was ein moderner Optimierungscompiler herausfinden kann).
Der wichtigste greifbare Effekt registerbesteht darin, dass verhindert wird, dass versucht wird, die Adresse eines Objekts zu übernehmen. Dies ist als Optimierungshinweis nicht besonders nützlich, da es nur auf lokale Variablen angewendet werden kann und ein Optimierungscompiler selbst feststellen kann, dass die Adresse eines solchen Objekts nicht verwendet wird.
Ist das Verhalten dieses Programms gemäß C-Standard wirklich undefiniert? Ist es in C ++ gut definiert? Ich denke, dass es in C ++ gut definiert ist.
Zerstörer
@Destructor: Warum sollte es undefiniert sein? Es gibt kein registerqualifiziertes Array-Objekt, wenn Sie das denken.
Keith Thompson
Oh, tut mir leid, ich habe vergessen, das Schlüsselwort register in die Array-Deklaration in main () zu schreiben. Ist es in C ++ gut definiert?
Zerstörer
Ich habe mich beim Definieren von registerArray-Objekten geirrt . Siehe den aktualisierten ersten Punkt in meiner Antwort. Es ist legal, ein solches Objekt zu definieren, aber Sie können nichts damit anfangen. Wenn Sie registerdie Definition von sin Ihrem Beispiel ergänzen , ist das Programm in C illegal (eine Einschränkungsverletzung). C ++ unterliegt nicht denselben Einschränkungen register, sodass das Programm in C ++ gültig wäre (die Verwendung registerwäre jedoch sinnlos).
Keith Thompson
@KeithThompson: Das registerSchlüsselwort könnte einen nützlichen Zweck erfüllen, wenn es legal wäre, die Adresse einer solchen Variablen zu übernehmen, jedoch nur in Fällen, in denen die Semantik nicht beeinflusst würde, wenn die Variable bei der Übernahme in eine temporäre Adresse kopiert und aus der temporären Adresse neu geladen wird am nächsten Sequenzpunkt. Dies würde es Compilern ermöglichen anzunehmen, dass die Variable über alle Zeigerzugriffe hinweg sicher in einem Register gespeichert werden kann, vorausgesetzt, sie wird an einer beliebigen Stelle gelöscht, an der ihre Adresse verwendet wird.
Supercat
9
Märchenstunde!
C als Sprache ist eine Abstraktion eines Computers. Sie können damit Dinge tun, die ein Computer tut, dh den Speicher manipulieren, rechnen, Dinge drucken usw.
Aber C ist nur eine Abstraktion. Und letztendlich extrahiert es aus Ihnen die Assemblersprache. Assembly ist die Sprache, die eine CPU liest, und wenn Sie sie verwenden, tun Sie Dinge in Bezug auf die CPU. Was macht eine CPU? Grundsätzlich liest es aus dem Speicher, rechnet und schreibt in den Speicher. Die CPU berechnet nicht nur Zahlen im Speicher. Zuerst müssen Sie eine Zahl aus dem Speicher in den Speicher innerhalb der CPU verschieben, die als Register bezeichnet wird. Sobald Sie mit dieser Nummer fertig sind, können Sie sie wieder in den normalen Systemspeicher verschieben. Warum überhaupt Systemspeicher verwenden? Die Anzahl der Register ist begrenzt. In modernen Prozessoren erhalten Sie nur etwa hundert Bytes, und ältere, beliebte Prozessoren waren noch fantastischer eingeschränkt (der 6502 verfügte über 3 8-Bit-Register für Ihre kostenlose Verwendung). Ihre durchschnittliche mathematische Operation sieht also so aus:
load first number from memory
load second number from memory
add the two
store answer into memory
Vieles davon ist ... nicht Mathe. Diese Lade- und Speichervorgänge können bis zur Hälfte Ihrer Verarbeitungszeit in Anspruch nehmen. C, eine Abstraktion von Computern, befreite den Programmierer von der Sorge, Register zu verwenden und zu jonglieren, und da Anzahl und Typ zwischen Computern variieren, überträgt C die Verantwortung für die Registerzuweisung ausschließlich auf den Compiler. Mit einer Ausnahme.
Wenn Sie eine Variable deklarieren registerSie sagen dem Compiler: "Yo, ich beabsichtige, dass diese Variable häufig verwendet wird und / oder nur von kurzer Dauer ist. Wenn ich Sie wäre, würde ich versuchen, sie in einem Register zu führen." Wenn der C-Standard besagt, dass Compiler eigentlich nichts tun müssen, liegt das daran, dass der C-Standard nicht weiß, für welchen Computer Sie kompilieren, und es könnte wie beim 6502 oben sein, bei dem alle drei Register nur für den Betrieb benötigt werden und es gibt kein Ersatzregister, um Ihre Nummer zu behalten. Wenn jedoch angegeben wird, dass Sie die Adresse nicht übernehmen können, liegt dies daran, dass Register keine Adressen haben. Sie sind die Hände des Prozessors. Da der Compiler Ihnen keine Adresse geben muss und überhaupt keine Adresse haben kann, stehen dem Compiler jetzt mehrere Optimierungen offen. Es könnte zum Beispiel die Nummer immer in einem Register behalten. Tut es nicht' Sie müssen sich keine Gedanken darüber machen, wo es im Computerspeicher gespeichert ist (außer dass Sie es wieder zurückerhalten müssen). Es könnte es sogar in eine andere Variable stempeln, es einem anderen Prozessor geben, ihm einen sich ändernden Ort geben usw.
tl; dr: Kurzlebige Variablen, die viel rechnen. Deklarieren Sie nicht zu viele auf einmal.
Sie spielen mit dem ausgeklügelten Graph-Coloring-Algorithmus des Compilers. Dies wird für die Registerzuordnung verwendet. Meistens. Es dient als Hinweis für den Compiler - das stimmt. Aber nicht in seiner Gesamtheit ignoriert, da Sie nicht die Adresse einer Registervariablen annehmen dürfen (denken Sie daran, dass der Compiler, der jetzt Ihrer Gnade ausgeliefert ist, versuchen wird, anders zu handeln). Was Ihnen in gewisser Weise sagt, dass Sie es nicht verwenden sollen.
Das Schlüsselwort wurde lange, lange zurück verwendet. Wenn es nur so wenige Register gab, die sie alle mit Ihrem Zeigefinger zählen konnten.
Aber wie gesagt, veraltet heißt nicht, dass Sie es nicht verwenden können.
Einige der älteren Hardware hatten mehr Register als die modernen Intel-Maschinen. Registerzählungen haben nichts mit dem Alter und alles mit der CPU-Architektur zu tun.
NUR MEINE RICHTIGE MEINUNG
2
@JUSTMYcorrectOPINION Tatsächlich hat X86 im Grunde genommen insgesamt sechs, so dass höchstens 1 oder 2 für die Registrierung übrig bleiben. Da so viel Code geschrieben oder auf einen registrierungsarmen Computer portiert wurde, habe ich den Verdacht, dass dies wesentlich dazu beigetragen hat, dass das Schlüsselwort 'register' zu einem Placebo wurde - es macht keinen Sinn, Register anzudeuten, wenn es keine gibt. Hier sind wir 4+ Jahre später und zum Glück hat x86_64 es auf 14 erhöht, und ARM ist jetzt auch eine große Sache.
Greggo
4
Nur eine kleine Demo (ohne realen Zweck) zum Vergleich: Wenn Sie die registerSchlüsselwörter vor jeder Variablen entfernen , dauert dieser Code auf meinem i7 (GCC) 3,41 Sekunden, wobeiregister derselbe Code in 0,7 Sekunden abgeschlossen ist.
Mit gcc 4.8.4 und -O3 bekomme ich keinen Unterschied. Ohne -O3- und 40000-Iterationen bekomme ich bei einer Gesamtzeit von 1,5 Sekunden vielleicht 50 ms weniger, aber ich habe es nicht oft genug ausgeführt, um zu wissen, ob dies überhaupt statistisch signifikant war.
Zstewart
Es gibt keinen Unterschied zu CLANG 5.0, die Plattform ist AMD64. (Ich habe die ASM-Ausgabe überprüft.)
ern0
4
Ich habe das Schlüsselwort register unter QNX 6.5.0 mit dem folgenden Code getestet:
#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;}
Ich habe folgende Ergebnisse erhalten:
-> 807679611 Zyklen verstrichen
-> Dieses System hat 3300830000 Zyklen pro Sekunde
-> Die Zyklen in Sekunden betragen ~ 0,244600
Und jetzt ohne Register int:
int a=0, b =1, c =3, i;
Ich habe:
-> 1421694077 verstrichene Zyklen
-> Dieses System hat 3300830000 Zyklen pro Sekunde
Register würde den Compiler benachrichtigen, dass der Codierer glaubte, diese Variable würde genug geschrieben / gelesen, um ihre Speicherung in einem der wenigen Register zu rechtfertigen, die für die Verwendung von Variablen verfügbar sind. Das Lesen / Schreiben aus Registern ist normalerweise schneller und erfordert möglicherweise einen kleineren Op-Code-Satz.
Heutzutage ist dies nicht sehr nützlich, da die Optimierer der meisten Compiler besser als Sie bestimmen können, ob und wie lange ein Register für diese Variable verwendet werden soll.
In den siebziger Jahren, ganz am Anfang der C-Sprache, wurde das Schlüsselwort register eingeführt, damit der Programmierer dem Compiler Hinweise geben kann, dass die Variable sehr häufig verwendet wird und dies sinnvoll sein sollte Behalten Sie den Wert in einem der internen Register des Prozessors.
Heutzutage sind Optimierer viel effizienter als Programmierer, um Variablen zu bestimmen, die mit größerer Wahrscheinlichkeit in Registern gespeichert werden, und der Optimierer berücksichtigt nicht immer den Hinweis des Programmierers.
So viele Leute empfehlen fälschlicherweise, das Schlüsselwort register nicht zu verwenden.
Mal sehen warum!
Das Schlüsselwort register hat einen damit verbundenen Nebeneffekt: Sie können nicht auf eine Registertypvariable verweisen (deren Adresse abrufen).
Menschen, die anderen raten, keine Register zu verwenden, nehmen dies fälschlicherweise als zusätzliches Argument.
Die einfache Tatsache, dass Sie die Adresse einer Registervariablen nicht übernehmen können, ermöglicht es dem Compiler (und seinem Optimierer) jedoch zu wissen, dass der Wert dieser Variablen nicht indirekt über einen Zeiger geändert werden kann.
Wenn an einem bestimmten Punkt des Befehlsstroms einer Registervariable der Wert im Register eines Prozessors zugewiesen wird und das Register nicht verwendet wurde, um den Wert einer anderen Variablen abzurufen, weiß der Compiler, dass er nicht erneut geladen werden muss der Wert der Variablen in diesem Register. Dies ermöglicht es, teuren nutzlosen Speicherzugriff zu vermeiden.
Wenn Sie Ihre eigenen Tests durchführen, erhalten Sie signifikante Leistungsverbesserungen in Ihren innersten Schleifen.
Auf unterstützten C-Compilern wird versucht, den Code so zu optimieren, dass der Wert der Variablen in einem tatsächlichen Prozessorregister gespeichert wird.
Der Visual C ++ - Compiler von Microsoft ignoriert das registerSchlüsselwort, wenn die globale Optimierung der Registerzuordnung (das Compiler-Flag / Oe) aktiviert ist.
Das Schlüsselwort Register weist den Compiler an, die bestimmte Variable in CPU-Registern zu speichern, damit schnell darauf zugegriffen werden kann. Aus Sicht eines Programmierers wird das Schlüsselwort register für die Variablen verwendet, die in einem Programm häufig verwendet werden, damit der Compiler den Code beschleunigen kann. Obwohl es vom Compiler abhängt, ob die Variable in CPU-Registern oder im Hauptspeicher gespeichert werden soll.
Register gibt dem Compiler an, diesen Code zu optimieren, indem diese bestimmte Variable in Registern und dann im Speicher gespeichert wird. Es handelt sich um eine Anforderung an den Compiler. Der Compiler kann diese Anforderung berücksichtigen oder nicht. Sie können diese Funktion verwenden, wenn auf einige Ihrer Variablen sehr häufig zugegriffen wird. Zum Beispiel: Eine Schleife.
Eine weitere Sache ist, dass wenn Sie eine Variable als Register deklarieren, Sie ihre Adresse nicht erhalten können, da sie nicht im Speicher gespeichert ist. es erhält seine Zuordnung im CPU-Register.
#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
Dies erzwingt ebxdie Verwendung für die Berechnung, was bedeutet, dass es auf den Stapel verschoben und am Ende der Funktion wiederhergestellt werden muss, da es als Angerufene gespeichert wird. registererzeugt mehr Codezeilen und 1 Speicherschreib- und 1 Speicherlesevorgang (obwohl dies realistisch gesehen auf 0 R / Ws optimiert werden könnte, wenn die Berechnung in durchgeführt worden wäre esi, was mit C ++ geschieht const register). Wenn Sie nicht verwenden, werden register2 Schreibvorgänge und 1 Lesevorgang ausgeführt (obwohl beim Lesen eine Weiterleitung zum Laden zum Laden erfolgt). Dies liegt daran, dass der Wert direkt auf dem Stapel vorhanden sein und aktualisiert werden muss, damit der richtige Wert anhand der Adresse (Zeiger) gelesen werden kann. registerhat diese Anforderung nicht und kann nicht darauf hingewiesen werden. constund registersind im Grunde das Gegenteil von volatileund verwendenvolatileüberschreibt die const-Optimierungen im Datei- und Blockbereich und die registerOptimierungen im Blockbereich. const registerund registererzeugt identische Ausgaben, da const im Blockbereich nichts für C tut, sodass nur die registerOptimierungen gelten.
Beim Klirren registerwird ignoriert, es consttreten jedoch weiterhin Optimierungen auf.
register
Variablen zu erhalten.Antworten:
Dies ist ein Hinweis für den Compiler, dass die Variable häufig verwendet wird und dass Sie empfehlen, sie nach Möglichkeit in einem Prozessorregister zu speichern.
Die meisten modernen Compiler tun dies automatisch und können sie besser auswählen als wir Menschen.
quelle
register
Variablen verwendet wird. Dies ist der einzige obligatorische Effekt desregister
Schlüsselworts. Auch dies reicht aus, um Optimierungen zu verbessern, da es trivial wird zu sagen, dass die Variable nur innerhalb dieser Funktion geändert werden kann.Ich bin überrascht, dass niemand erwähnt hat, dass Sie keine Adresse der Registervariablen verwenden können, selbst wenn der Compiler beschließt, die Variable im Speicher und nicht im Register zu belassen.
Wenn
register
Sie also verwenden , gewinnen Sie nichts (der Compiler entscheidet ohnehin selbst, wo die Variable abgelegt werden soll) und verlieren den&
Operator - kein Grund, sie zu verwenden.quelle
register
Dies ist auch dann nützlich, wenn der Compiler es nicht in ein Register einfügt.const
ist auch nutzlos, da es Ihnen nichts bringt, Sie verlieren nur die Fähigkeit, eine Variable zu ändern.register
könnte verwendet werden, um sicherzustellen, dass in Zukunft niemand mehr die Adresse einer Variablen nimmt, ohne darüber nachzudenken. Ich hatte jedoch nie Grund, es zu benutzenregister
.Es weist den Compiler an, zu versuchen, ein CPU-Register anstelle von RAM zum Speichern der Variablen zu verwenden. Register befinden sich in der CPU und sind viel schneller zugänglich als RAM. Dies ist jedoch nur ein Vorschlag an den Compiler, der möglicherweise nicht umgesetzt wird.
quelle
Ich weiß, dass es sich bei dieser Frage um C handelt, aber dieselbe Frage für C ++ wurde als genaues Duplikat dieser Frage geschlossen. Diese Antwort gilt daher möglicherweise nicht für C.
Der neueste Entwurf des C ++ 11-Standards, N3485 , besagt dies in 7.1.1 / 3:
In C ++ (aber nicht in C) gibt der Standard nicht an, dass Sie die Adresse einer deklarierten Variablen nicht übernehmen können
register
. Da jedoch einer Variablen, die während ihrer gesamten Lebensdauer in einem CPU-Register gespeichert ist, kein Speicherort zugeordnet ist, ist der Versuch, ihre Adresse zu übernehmen, ungültig, und der Compiler ignoriert dasregister
Schlüsselwort, um die Adresse zu übernehmen.quelle
Es ist seit mindestens 15 Jahren nicht mehr relevant, da Optimierer diesbezüglich bessere Entscheidungen treffen als Sie. Selbst wenn es relevant war, machte es auf einer CPU-Architektur mit vielen Registern wie SPARC oder M68000 viel mehr Sinn als auf Intel mit seinem Mangel an Registern, von denen die meisten vom Compiler für seine eigenen Zwecke reserviert werden.
quelle
Tatsächlich teilt register dem Compiler mit, dass die Variable keinen Alias mit irgendetwas anderem im Programm hat (nicht einmal mit Zeichen).
Dies kann von modernen Compilern in einer Vielzahl von Situationen ausgenutzt werden und dem Compiler bei komplexem Code erheblich helfen - in einfachem Code können die Compiler dies selbst herausfinden.
Andernfalls hat es keinen Zweck und wird nicht für die Registerzuordnung verwendet. Normalerweise führt dies nicht zu Leistungseinbußen bei der Angabe, solange Ihr Compiler modern genug ist.
quelle
register
es überhaupt nicht möglich ist, die Adresse zu übernehmen, da es sonst nützlich sein könnte, Compiler über Fälle zu informieren, in denen ein Compiler Registeroptimierungen anwenden kann, obwohl die Adresse einer Variablen an eine externe Funktion übergeben wird (die Variable müsste) für diesen bestimmten Aufruf in den Speicher geleert werden , aber sobald die Funktion zurückkehrt, kann der Compiler sie wieder als Variable behandeln, deren Adresse nie übernommen wurde.bar
eine istregister
variabel, ein Compiler an seiner Freizeit kann ersetzenfoo(&bar);
mitint temp=bar; foo(&temp); bar=temp;
, aber unter der Adressebar
würde in den meisten anderen Kontexten verboten würde nicht wie eine allzu komplizierte Regel zu sein scheint. Wenn die Variable andernfalls in einem Register gespeichert werden könnte, würde die Ersetzung den Code verkleinern. Wenn die Variable ohnehin im RAM bleiben müsste, würde die Ersetzung den Code vergrößern. Die Frage, ob die Ersetzung dem Compiler überlassen werden soll, würde in beiden Fällen zu besserem Code führen.register
Qualifizierung für globale Variablen, unabhängig davon, ob der Compiler die Verwendung der Adresse zulässt oder nicht, würde einige nette Optimierungen ermöglichen, wenn eine Inline-Funktion, die eine globale Variable verwendet, wiederholt in einer Schleife aufgerufen wird. Ich kann mir keine andere Möglichkeit vorstellen, diese Variable zwischen Schleifeniterationen in einem Register zu halten - können Sie?Tatsächlich ist es durch den C-Standard klar definiert. Zitieren des N1570-Entwurfs, Abschnitt 6.7.1, Absatz 6 (andere Versionen haben den gleichen Wortlaut):
Der unäre
&
Operator darf nicht auf ein mit definiertes Objekt angewendetregister
undregister
nicht in einer externen Deklaration verwendet werden.Es gibt einige andere (ziemlich undurchsichtige) Regeln, die für
register
qualifizierte Objekte spezifisch sind:Das Definieren eines Array-Objekts mitregister
hat ein undefiniertes Verhalten.Korrektur: Es ist legal, ein Array-Objekt mit zu definieren
register
, aber Sie können mit einem solchen Objekt nichts Nützliches tun (für die Indizierung in ein Array muss die Adresse seines ursprünglichen Elements verwendet werden)._Alignas
Bezeichner (neu in C11) kann möglicherweise nicht auf ein solches Objekt angewendet werden.va_start
Makroregister
übergebene Parametername -qualifiziert ist, ist das Verhalten undefiniert.Es kann einige andere geben; Laden Sie einen Entwurf des Standards herunter und suchen Sie bei Interesse nach "Registrieren".
Wie der Name schon sagt, bestand die ursprüngliche Bedeutung von darin
register
, dass ein Objekt in einem CPU-Register gespeichert werden muss. Mit Verbesserungen bei der Optimierung von Compilern ist dies jedoch weniger nützlich geworden. Moderne Versionen des C-Standards beziehen sich nicht auf CPU-Register, da sie nicht mehr davon ausgehen (müssen), dass es so etwas gibt (es gibt Architekturen, die keine Register verwenden). Es ist allgemein bekannt, dass das Anwendenregister
auf eine Objektdeklaration den generierten Code mit größerer Wahrscheinlichkeit verschlechtert , da dies die Registerzuordnung des Compilers beeinträchtigt. Es kann immer noch einige Fälle geben, in denen dies nützlich ist (z. B. wenn Sie wirklich wissen, wie oft auf eine Variable zugegriffen wird und Ihr Wissen besser ist als das, was ein moderner Optimierungscompiler herausfinden kann).Der wichtigste greifbare Effekt
register
besteht darin, dass verhindert wird, dass versucht wird, die Adresse eines Objekts zu übernehmen. Dies ist als Optimierungshinweis nicht besonders nützlich, da es nur auf lokale Variablen angewendet werden kann und ein Optimierungscompiler selbst feststellen kann, dass die Adresse eines solchen Objekts nicht verwendet wird.quelle
register
qualifiziertes Array-Objekt, wenn Sie das denken.register
Array-Objekten geirrt . Siehe den aktualisierten ersten Punkt in meiner Antwort. Es ist legal, ein solches Objekt zu definieren, aber Sie können nichts damit anfangen. Wenn Sieregister
die Definition vons
in Ihrem Beispiel ergänzen , ist das Programm in C illegal (eine Einschränkungsverletzung). C ++ unterliegt nicht denselben Einschränkungenregister
, sodass das Programm in C ++ gültig wäre (die Verwendungregister
wäre jedoch sinnlos).register
Schlüsselwort könnte einen nützlichen Zweck erfüllen, wenn es legal wäre, die Adresse einer solchen Variablen zu übernehmen, jedoch nur in Fällen, in denen die Semantik nicht beeinflusst würde, wenn die Variable bei der Übernahme in eine temporäre Adresse kopiert und aus der temporären Adresse neu geladen wird am nächsten Sequenzpunkt. Dies würde es Compilern ermöglichen anzunehmen, dass die Variable über alle Zeigerzugriffe hinweg sicher in einem Register gespeichert werden kann, vorausgesetzt, sie wird an einer beliebigen Stelle gelöscht, an der ihre Adresse verwendet wird.Märchenstunde!
C als Sprache ist eine Abstraktion eines Computers. Sie können damit Dinge tun, die ein Computer tut, dh den Speicher manipulieren, rechnen, Dinge drucken usw.
Aber C ist nur eine Abstraktion. Und letztendlich extrahiert es aus Ihnen die Assemblersprache. Assembly ist die Sprache, die eine CPU liest, und wenn Sie sie verwenden, tun Sie Dinge in Bezug auf die CPU. Was macht eine CPU? Grundsätzlich liest es aus dem Speicher, rechnet und schreibt in den Speicher. Die CPU berechnet nicht nur Zahlen im Speicher. Zuerst müssen Sie eine Zahl aus dem Speicher in den Speicher innerhalb der CPU verschieben, die als Register bezeichnet wird. Sobald Sie mit dieser Nummer fertig sind, können Sie sie wieder in den normalen Systemspeicher verschieben. Warum überhaupt Systemspeicher verwenden? Die Anzahl der Register ist begrenzt. In modernen Prozessoren erhalten Sie nur etwa hundert Bytes, und ältere, beliebte Prozessoren waren noch fantastischer eingeschränkt (der 6502 verfügte über 3 8-Bit-Register für Ihre kostenlose Verwendung). Ihre durchschnittliche mathematische Operation sieht also so aus:
Vieles davon ist ... nicht Mathe. Diese Lade- und Speichervorgänge können bis zur Hälfte Ihrer Verarbeitungszeit in Anspruch nehmen. C, eine Abstraktion von Computern, befreite den Programmierer von der Sorge, Register zu verwenden und zu jonglieren, und da Anzahl und Typ zwischen Computern variieren, überträgt C die Verantwortung für die Registerzuweisung ausschließlich auf den Compiler. Mit einer Ausnahme.
Wenn Sie eine Variable deklarieren
register
Sie sagen dem Compiler: "Yo, ich beabsichtige, dass diese Variable häufig verwendet wird und / oder nur von kurzer Dauer ist. Wenn ich Sie wäre, würde ich versuchen, sie in einem Register zu führen." Wenn der C-Standard besagt, dass Compiler eigentlich nichts tun müssen, liegt das daran, dass der C-Standard nicht weiß, für welchen Computer Sie kompilieren, und es könnte wie beim 6502 oben sein, bei dem alle drei Register nur für den Betrieb benötigt werden und es gibt kein Ersatzregister, um Ihre Nummer zu behalten. Wenn jedoch angegeben wird, dass Sie die Adresse nicht übernehmen können, liegt dies daran, dass Register keine Adressen haben. Sie sind die Hände des Prozessors. Da der Compiler Ihnen keine Adresse geben muss und überhaupt keine Adresse haben kann, stehen dem Compiler jetzt mehrere Optimierungen offen. Es könnte zum Beispiel die Nummer immer in einem Register behalten. Tut es nicht' Sie müssen sich keine Gedanken darüber machen, wo es im Computerspeicher gespeichert ist (außer dass Sie es wieder zurückerhalten müssen). Es könnte es sogar in eine andere Variable stempeln, es einem anderen Prozessor geben, ihm einen sich ändernden Ort geben usw.tl; dr: Kurzlebige Variablen, die viel rechnen. Deklarieren Sie nicht zu viele auf einmal.
quelle
Sie spielen mit dem ausgeklügelten Graph-Coloring-Algorithmus des Compilers. Dies wird für die Registerzuordnung verwendet. Meistens. Es dient als Hinweis für den Compiler - das stimmt. Aber nicht in seiner Gesamtheit ignoriert, da Sie nicht die Adresse einer Registervariablen annehmen dürfen (denken Sie daran, dass der Compiler, der jetzt Ihrer Gnade ausgeliefert ist, versuchen wird, anders zu handeln). Was Ihnen in gewisser Weise sagt, dass Sie es nicht verwenden sollen.
Das Schlüsselwort wurde lange, lange zurück verwendet. Wenn es nur so wenige Register gab, die sie alle mit Ihrem Zeigefinger zählen konnten.
Aber wie gesagt, veraltet heißt nicht, dass Sie es nicht verwenden können.
quelle
Nur eine kleine Demo (ohne realen Zweck) zum Vergleich: Wenn Sie die
register
Schlüsselwörter vor jeder Variablen entfernen , dauert dieser Code auf meinem i7 (GCC) 3,41 Sekunden, wobeiregister
derselbe Code in 0,7 Sekunden abgeschlossen ist.quelle
Ich habe das Schlüsselwort register unter QNX 6.5.0 mit dem folgenden Code getestet:
Ich habe folgende Ergebnisse erhalten:
-> 807679611 Zyklen verstrichen
-> Dieses System hat 3300830000 Zyklen pro Sekunde
-> Die Zyklen in Sekunden betragen ~ 0,244600
Und jetzt ohne Register int:
Ich habe:
-> 1421694077 verstrichene Zyklen
-> Dieses System hat 3300830000 Zyklen pro Sekunde
-> Die Zyklen in Sekunden betragen ~ 0,430700
quelle
Register würde den Compiler benachrichtigen, dass der Codierer glaubte, diese Variable würde genug geschrieben / gelesen, um ihre Speicherung in einem der wenigen Register zu rechtfertigen, die für die Verwendung von Variablen verfügbar sind. Das Lesen / Schreiben aus Registern ist normalerweise schneller und erfordert möglicherweise einen kleineren Op-Code-Satz.
Heutzutage ist dies nicht sehr nützlich, da die Optimierer der meisten Compiler besser als Sie bestimmen können, ob und wie lange ein Register für diese Variable verwendet werden soll.
quelle
In den siebziger Jahren, ganz am Anfang der C-Sprache, wurde das Schlüsselwort register eingeführt, damit der Programmierer dem Compiler Hinweise geben kann, dass die Variable sehr häufig verwendet wird und dies sinnvoll sein sollte Behalten Sie den Wert in einem der internen Register des Prozessors.
Heutzutage sind Optimierer viel effizienter als Programmierer, um Variablen zu bestimmen, die mit größerer Wahrscheinlichkeit in Registern gespeichert werden, und der Optimierer berücksichtigt nicht immer den Hinweis des Programmierers.
So viele Leute empfehlen fälschlicherweise, das Schlüsselwort register nicht zu verwenden.
Mal sehen warum!
Das Schlüsselwort register hat einen damit verbundenen Nebeneffekt: Sie können nicht auf eine Registertypvariable verweisen (deren Adresse abrufen).
Menschen, die anderen raten, keine Register zu verwenden, nehmen dies fälschlicherweise als zusätzliches Argument.
Die einfache Tatsache, dass Sie die Adresse einer Registervariablen nicht übernehmen können, ermöglicht es dem Compiler (und seinem Optimierer) jedoch zu wissen, dass der Wert dieser Variablen nicht indirekt über einen Zeiger geändert werden kann.
Wenn an einem bestimmten Punkt des Befehlsstroms einer Registervariable der Wert im Register eines Prozessors zugewiesen wird und das Register nicht verwendet wurde, um den Wert einer anderen Variablen abzurufen, weiß der Compiler, dass er nicht erneut geladen werden muss der Wert der Variablen in diesem Register. Dies ermöglicht es, teuren nutzlosen Speicherzugriff zu vermeiden.
Wenn Sie Ihre eigenen Tests durchführen, erhalten Sie signifikante Leistungsverbesserungen in Ihren innersten Schleifen.
c_register_side_effect_performance_boost
quelle
Auf unterstützten C-Compilern wird versucht, den Code so zu optimieren, dass der Wert der Variablen in einem tatsächlichen Prozessorregister gespeichert wird.
quelle
Der Visual C ++ - Compiler von Microsoft ignoriert das
register
Schlüsselwort, wenn die globale Optimierung der Registerzuordnung (das Compiler-Flag / Oe) aktiviert ist.Siehe Register Keyword auf MSDN.
quelle
Das Schlüsselwort Register weist den Compiler an, die bestimmte Variable in CPU-Registern zu speichern, damit schnell darauf zugegriffen werden kann. Aus Sicht eines Programmierers wird das Schlüsselwort register für die Variablen verwendet, die in einem Programm häufig verwendet werden, damit der Compiler den Code beschleunigen kann. Obwohl es vom Compiler abhängt, ob die Variable in CPU-Registern oder im Hauptspeicher gespeichert werden soll.
quelle
Register gibt dem Compiler an, diesen Code zu optimieren, indem diese bestimmte Variable in Registern und dann im Speicher gespeichert wird. Es handelt sich um eine Anforderung an den Compiler. Der Compiler kann diese Anforderung berücksichtigen oder nicht. Sie können diese Funktion verwenden, wenn auf einige Ihrer Variablen sehr häufig zugegriffen wird. Zum Beispiel: Eine Schleife.
Eine weitere Sache ist, dass wenn Sie eine Variable als Register deklarieren, Sie ihre Adresse nicht erhalten können, da sie nicht im Speicher gespeichert ist. es erhält seine Zuordnung im CPU-Register.
quelle
gcc 9.3 asm Ausgabe ohne Verwendung von Optimierungsflags (alles in dieser Antwort bezieht sich auf die Standardkompilierung ohne Optimierungsflags):
Dies erzwingt
ebx
die Verwendung für die Berechnung, was bedeutet, dass es auf den Stapel verschoben und am Ende der Funktion wiederhergestellt werden muss, da es als Angerufene gespeichert wird.register
erzeugt mehr Codezeilen und 1 Speicherschreib- und 1 Speicherlesevorgang (obwohl dies realistisch gesehen auf 0 R / Ws optimiert werden könnte, wenn die Berechnung in durchgeführt worden wäreesi
, was mit C ++ geschiehtconst register
). Wenn Sie nicht verwenden, werdenregister
2 Schreibvorgänge und 1 Lesevorgang ausgeführt (obwohl beim Lesen eine Weiterleitung zum Laden zum Laden erfolgt). Dies liegt daran, dass der Wert direkt auf dem Stapel vorhanden sein und aktualisiert werden muss, damit der richtige Wert anhand der Adresse (Zeiger) gelesen werden kann.register
hat diese Anforderung nicht und kann nicht darauf hingewiesen werden.const
undregister
sind im Grunde das Gegenteil vonvolatile
und verwendenvolatile
überschreibt die const-Optimierungen im Datei- und Blockbereich und dieregister
Optimierungen im Blockbereich.const register
undregister
erzeugt identische Ausgaben, da const im Blockbereich nichts für C tut, sodass nur dieregister
Optimierungen gelten.Beim Klirren
register
wird ignoriert, esconst
treten jedoch weiterhin Optimierungen auf.quelle