Wann ist das Schlüsselwort register in C tatsächlich nützlich?

Ich bin verwirrt über die Verwendung des Schlüsselworts register in C. Es wird allgemein gesagt, dass seine Verwendung nicht „ist.“ t benötigt wie in dieser Frage zum Stackoverflow .

Ist dieses Schlüsselwort in C aufgrund moderner Compiler vollständig redundant oder gibt es Situationen, in denen es auftritt? kann immer noch nützlich sein? Wenn ja, in welchen Situationen ist die Verwendung des Schlüsselworts register tatsächlich hilfreich?

Kommentare

  • Ich denke, die verknüpfte Frage und die Antworten darauf sind die gleichen, die Sie hier erwarten können. Es gibt also keine neuen Informationen, die Sie hier erhalten können.
  • @UwePlonus Ich dachte die Das gleiche gilt für das Schlüsselwort const, aber diese Frage hat bewiesen, dass ich falsch lag. Also habe ich ‚ Ich werde abwarten und sehen, was ich bekomme.
  • Ich denke, das Schlüsselwort const ist etwas anderes als das Register.
  • Es ‚ ist nützlich, wenn Sie versehentlich in die Vergangenheit reisen und gezwungen sind, einen der frühen C-Compiler zu verwenden. Abgesehen davon, dass ‚ überhaupt nicht nützlich ist, ist ‚ seit Jahren völlig veraltet.
  • @ UwePlonus Ich meinte nur, dass es mir unbekannte Szenarien geben könnte, in denen ein Schlüsselwort nützlich sein könnte.

Antwort

Es ist sprachlich nicht redundant, es ist nur so, dass Sie, wenn Sie es verwenden, dem Compiler mitteilen, dass Sie es „vorziehen“ würden, eine Variable im Register zu speichern. Es gibt jedoch absolut keine Garantie dafür, dass dies tatsächlich der Fall ist passieren zur Laufzeit.

Kommentare

  • Darüber hinaus ist ‚ fast immer der Fall Der Compiler weiß es am besten und Sie ‚ verschwenden Ihren Atem
  • @jozefg: noch schlimmer. Sie laufen Gefahr, dass der Compiler Ihre Anfrage / Ihren Hinweis berücksichtigt und schlechterer Code als Ergebnis.

Antwort

Wie bereits erwähnt, sind Compiler-Optimierer im Wesentlichen ren Das Schlüsselwort register ist für andere Zwecke als das Verhindern von Aliasing veraltet. Es gibt jedoch ganze Codebasen, die bei deaktivierter Optimierung kompiliert werden (-O0 in gcc-speak ). Für einen solchen Code kann das Schlüsselwort register eine große Wirkung haben. Insbesondere können Variablen, die andernfalls einen Steckplatz auf dem Stapel erhalten würden (dh alle Funktionsparameter und automatischen Variablen) , direkt in ein Register gestellt werden, wenn sie mit register Schlüsselwort.

Hier „ein reales Beispiel: Nehmen Sie an, dass ein Datenbankabruf stattgefunden hat und dass der Abrufcode das abgerufene Tupel in eine C-Struktur gestopft hat. Nehmen Sie ferner an, dass eine Teilmenge dieser C-Struktur vorhanden ist muss in eine andere Struktur kopiert werden – möglicherweise handelt es sich bei dieser zweiten Struktur um einen Cache-Datensatz, der die in der Datenbank gespeicherten Metadaten darstellt, die aufgrund von Speicherbeschränkungen nur eine Teilmenge jedes in der Datenbank gespeicherten Metadatensatzes zwischenspeichern.

Bei einer Funktion, die einen Zeiger auf jeden Strukturtyp verwendet und deren einzige Aufgabe es ist, einige Elemente von der ursprünglichen Struktur in die zweite Struktur zu kopieren: Die Strukturzeigervariablen befinden sich auf dem Stapel. Da Zuweisungen von den Mitgliedern einer Struktur erfolgen an die anderen „s“ werden die Adressen der Struktur für jede Zuweisung in ein Register geladen werden, um den Zugriff auf die zu kopierenden Mitglieder der Struktur durchzuführen. Wenn die Strukturzeiger mit dem Schlüsselwort register deklariert würden, würden die Adressen der Strukturen in den Registern verbleiben, wodurch die Anweisungen zum Laden der Adresse in das Register für jede Zuweisung effektiv herausgeschnitten würden.

Denken Sie auch hier daran, dass die obige Beschreibung für nicht optimierten Code gilt.

Antwort

Sie teilen dem Compiler im Grunde mit, dass Sie die Adresse der Variablen nicht übernehmen und der Compiler dann angeblich machen kann Weitere Optimierungen. Soweit ich weiß, können moderne Compiler ziemlich gut bestimmen, ob eine Variable in einem Register gespeichert werden kann / sollte oder nicht.

Beispiel:

int main(){ int* ptr; int a; register int b; ptr = &a; ptr = &b; //this won"t compile return 0; } 

Kommentare

  • Dereferenzieren oder nehmen Sie die Adresse von?
  • @detly: Sie sind natürlich korrekt

Antwort

In den 16-Bit-Computertagen wurden häufig mehrere Register benötigt, um 32-Bit-Multiplikationen und -Divisionen auszuführen Gleitkommaeinheiten wurden in Chips integriert und dann „übernommen“ 64-Bit-Architekturen, wobei sowohl die Breite der Register als auch die Anzahl ihrer Register erweitert wurden. Dies führt schließlich zu einer vollständigen Neuarchitektur der CPU. Siehe Dateien registrieren auf Wikipedia.

Kurz gesagt, es würde ein wenig Zeit in Anspruch nehmen, um herauszufinden Finden Sie heraus, was tatsächlich los ist, wenn Sie sich auf einem 64-Bit-X86- oder ARM-Chip befinden.Wenn Sie sich auf einer 16-Bit-Embedded-CPU befinden, erhalten Sie möglicherweise tatsächlich etwas. Die meisten kleinen Embedded-Chips führen jedoch keine zeitkritischen Schritte aus – Ihr Mikrowellenherd tastet möglicherweise 10.000 Mal pro Sekunde Ihr Touchpad ab – nichts, was a belastet 4-MHz-CPU.

Kommentare

  • 4 MIPS / 10.000 Umfragen / Sek. = 400 Anweisungen / Umfrage. Das ‚ ist nicht annähernd so viel Spielraum, wie Sie ‚ gerne hätten. Beachten Sie auch, dass einige 4-MHz-Prozessoren intern mikrocodiert wurden, was bedeutet, dass sie nicht annähernd 1 MIP / MHz waren.
  • @ JohnR.Strohm – Es kann Situationen geben, in denen es gerechtfertigt sein könnte, genau herauszufinden, wie viele Anweisungen es gibt Zyklen ‚ wird dauern, aber oft ist der billigere Ausweg jetzt, einfach einen schnelleren Chip zu bekommen und das Produkt aus der Tür zu holen. In dem angegebenen Beispiel muss man natürlich nicht ‚ weiter mit 10.000 abtasten, wenn man einen Befehl hat – es kann sein, dass die Abtastung für eine Viertelsekunde ohne Schaden nicht fortgesetzt wird getan. Es wird immer schwieriger herauszufinden, wo programmierergesteuerte Optimierung eine Rolle spielen wird.
  • Es ist nicht immer möglich, “ einfach einen schnelleren Chip zu bekommen und den zu bekommen Produkt aus der Tür „. Betrachten Sie die Echtzeit-Bildverarbeitung. 640 x 480 Pixel / Bild x 60 Bilder / Sekunde x N Anweisungen pro Pixel werden schnell hinzugefügt. (Die Lehre aus der Echtzeit-Bildverarbeitung ist, dass Sie Blut über Ihre Pixelkerne schwitzen und fast alles andere ignorieren, da es einmal pro Zeile oder einmal pro Patch oder einmal pro Frame ausgeführt wird und nicht hunderte Male pro Zeile oder Patch oder Zehntausende oder Hunderttausende Male pro Frame.)
  • @ JohnR.Strohm – Am Beispiel der Echtzeit-Bildverarbeitung würde ich annehmen, dass die Mindestumgebung 32 Bit beträgt. Viele Grafikbeschleuniger, die in Chips eingebaut sind, können auch für die Bilderkennung verwendet werden, z. B. ARM-Chips (z. B. weil ich nicht weiß, wie praktisch dies ist), da ich ‚ nicht weiß ) mit integrierten Rendering-Engines können zusätzliche ALUs zur Erkennung verwendet werden. Zu diesem Zeitpunkt ist die Verwendung des Schlüsselworts ‚ register ‚ zur Optimierung ein winziger Teil des Problems.

Antwort

Um festzustellen, ob das Schlüsselwort register eine Bedeutung hat, werden winzige Beispielcodes nicht verwendet. Hier ist ein C-Code was mir nahe legt, dass das Schlüsselwort register immer noch eine Bedeutung hat. Aber es könnte bei GCC unter Linux anders sein, ich weiß es nicht. Wird das Register int k & in einem CPU-Register gespeichert oder nicht? Linux-Benutzer sollten (insbesondere) mit GCC und Optimierung kompilieren. Bei Borland bcc32 scheint das Schlüsselwort register zu funktionieren (in diesem Beispiel), da der Operator & Fehlercodes für deklarierte Ganzzahlen des Registers angibt. HINWEIS! Dies ist bei einem winzigen Beispiel mit Borland unter Windows NICHT der Fall! Um wirklich zu sehen, was der Compiler optimiert oder nicht, muss es ein mehr als winziges Beispiel sein. Leere Schleifen funktionieren nicht! Trotzdem – WENN eine Adresse mit dem Operator & gelesen werden kann, wird die Variable nicht in einem CPU-Register gespeichert. Aber wenn eine vom Register deklarierte Variable nicht gelesen werden kann (was beim Kompilieren zu Fehlercode führt) – Ich muss davon ausgehen, dass das Schlüsselwort register die Variable tatsächlich in ein CPU-Register einfügt. Es kann auf verschiedenen Plattformen unterschiedlich sein, ich weiß es nicht . (Wenn es funktioniert, ist die Anzahl der „Ticks“ mit der Registerdeklaration weitaus geringer.

/* reg_or_not.c */ #include <stdio.h> #include <time.h> #include <stdlib> //not requiered for Linux #define LAPSb 50 #define LAPS 50000 #define MAXb 50 #define MAX 50000 int main (void) { /* 20 ints and 2 register ints */ register int k,l; int a,aa,b,bb,c,cc,d,dd,e,ee,f,ff,g,gg,h,hh,i,ii,j,jj; /* measure some ticks also */ clock_t start_1,start_2; clock_t finish_1,finish_2; long tmp; //just for the workload /* pointer declarations of all ints */ int *ap, *aap, *bp, *bbp, *cp, *ccp, *dp, *ddp, *ep, *eep; int *fp, *ffp, *gp, *ggp, *hp, *hhp, *ip, *iip, *jp, *jjp; int *kp,*lp; /* end of declarations */ /* read memory addresses, if possible - which can"t be done in a CPU-register */ ap=&a; aap=&aa; bp=&b; bbp=&bb; cp=&c; ccp=&cc; dp=&d; ddp=&dd; ep=&e; eep=&ee; fp=&f; ffp=&ff; gp=&g; ggp=&gg; hp=&h; hhp=&hh; ip=&i; iip=&ii; jp=&j; jjp=&jj; //kp=&k; //won"t compile if k is stored in a CPU register //lp=&l; //same - but try both ways ! /* what address , isn"t the issue in this case - but if stored in memory some "crazy" number will be shown, whilst CPU-registers can"t be read */ printf("Address a aa: %u %u\n",a,aa); printf("Address b bb: %u %u\n",b,bb); printf("Address c cc: %u %u\n",c,cc); printf("Address d dd: %u %u\n",d,dd); printf("Address e ee: %u %u\n",e,ee); printf("Address f ff: %u %u\n",f,ff); printf("Address g gg: %u %u\n",g,gg); printf("Address h hh: %u %u\n",h,hh); printf("Address i ii: %u %u\n",i,ii); printf("Address j jj: %u %u\n\n",j,jj); //printf("Address k: %u \n",k); //no reason to try "k" actually is in a CPU-register //printf("Address l: %u \n",l); start_2=clock(); //just for fun /* to ensure workload */ for (a=1;a<LAPSb;a++) {for (aa=0;aa<MAXb;aa++);{tmp+=aa/a;}} for (b=1;b<LAPSb;b++) {for (bb=0;bb<MAXb;bb++);{tmp+=aa/a;}} for (a=1;c<LAPSb;c++) {for (cc=0;cc<MAXb;cc++);{tmp+=bb/b;}} for (d=1;d<LAPSb;d++) {for (dd=0;dd<MAXb;dd++);{tmp+=cc/c;}} for (e=1;e<LAPSb;e++) {for (ee=0;ee<MAXb;ee++);{tmp+=dd/d;}} for (f=1;f<LAPSb;f++) {for (ff=0;ff<MAXb;ff++);{tmp+=ee/e;}} for (g=1;g<LAPSb;g++) {for (gg=0;gg<MAXb;gg++);{tmp+=ff/f;}} for (h=1;h<LAPSb;h++) {for (hh=0;hh<MAXb;hh++);{tmp+=hh/h;}} for (jj=1;jj<LAPSb;jj++) {for (ii=0;ii<MAXb;ii++);{tmp+=ii/jj;}} start_1=clock(); //see following printf for (i=0;i<LAPS;i++) {for (j=0;j<MAX;j++);{tmp+=j/i;}} /* same double loop - in supposed memory */ finish_1=clock(); //see following printf printf ("Memory: %ld ticks\n\n", finish_1 - start_1); //ticks for memory start_1=clock(); //see following printf for (k=0;k<LAPS;k++) {for (l=0;l<MAX;l++);{tmp+=l/k;}} /* same double loop - in supposed register*/ finish_1=clock(); //see following printf printf ("Register: %ld ticks\n\n", finish_1 - start_1); //ticks for CPU register (?) any difference ? finish_2=clock(); printf ("Total: %ld ticks\n\n", finish_2 - start_2); //really for fun only system("PAUSE"); //only requiered for Windows, so the CMD-window doesn"t vanish return 0; } 

Kommentare

  • Es wird eine Division mit Null oben geben, bitte ändern Sie {tmp + = ii / jj;} in {tmp + = jj / ii;} – entschuldigen Sie dies wirklich
  • Lassen Sie auch k und i Beginnen Sie mit 1 – nicht mit Null. Sehr leid.
  • Sie können Ihre Antwort bearbeiten, anstatt Korrekturen in Kommentare zu schreiben.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.