Wanneer is het trefwoord register eigenlijk nuttig in C?

Ik ben in de war over het gebruik van het register trefwoord in C. Over het algemeen wordt gezegd dat het gebruik ervan niet is ” t nodig zoals in deze vraag over stackoverflow .

Is dit trefwoord volledig overbodig in C vanwege moderne compilers of zijn er situaties waarin het kan nog steeds nuttig zijn? Zo ja, in welke situaties is het gebruik van het trefwoord register eigenlijk nuttig?

Opmerkingen

  • Ik denk dat de gekoppelde vraag en de antwoorden erop hetzelfde zijn als u hier kunt verwachten. Er zal dus geen nieuwe informatie zijn die u hier kunt krijgen.
  • @UwePlonus Ik dacht dat de hetzelfde over const trefwoord maar deze vraag bewees dat ik ongelijk had. Dus ik ‘ Ik zal afwachten wat ik krijg.
  • Ik denk dat het const -zoekwoord iets anders is dan registreren.
  • Het ‘ s handig als je per ongeluk terug in de tijd gaat en gedwongen wordt om een van de vroege C-compilers te gebruiken. Behalve dat het ‘ helemaal niet nuttig is, is het ‘ al jaren volledig achterhaald.
  • @ UwePlonus Ik bedoelde alleen dat er voor mij onbekende scenarios kunnen zijn waarin een trefwoord nuttig zou kunnen zijn.

Antwoord

Het is niet overbodig in termen van taal, het is alleen dat je door het te gebruiken de compiler vertelt dat je er “de voorkeur aan geeft” om een variabele in het register op te slaan. Er is echter absoluut geen enkele garantie dat dit gebeuren tijdens runtime.

Reacties

  • Bovendien is het ‘ bijna altijd het geval dat de compiler weet het het beste en je ‘ verspilt je adem opnieuw
  • @jozefg: nog erger. Je loopt het risico dat de compiler je verzoek / hint respecteert en slechtere code als resultaat.

Antwoord

Zoals eerder vermeld, compiler optimizers in wezen ren Der het register sleutelwoord verouderd voor andere doeleinden dan het voorkomen van aliasing. Er zijn echter volledige codebases die zijn gecompileerd met optimalisatie uitgeschakeld (-O0 in gcc-speak ). Voor dergelijke code kan het trefwoord register een groot effect hebben. Specifiek, variabelen die anders een slot op de stapel zouden krijgen (dwz alle functieparameters en automatische variabelen) kunnen direct in een register worden geplaatst als ze worden gedeclareerd met de register trefwoord.

Hier is een voorbeeld uit de echte wereld: neem aan dat er iets is opgehaald uit de database en dat de ophaalcode het opgehaalde tupel in een C-structuur heeft gestopt. Neem verder aan dat een deelverzameling van deze C-structuur moet naar een andere structuur worden gekopieerd – misschien is deze tweede structuur een cacherecord dat de metagegevens vertegenwoordigt die zijn opgeslagen in de database en die vanwege geheugenbeperkingen slechts een subset van elk metagegevensrecord zoals opgeslagen in de database in de cache opslaat. > Gegeven een functie die een pointer meeneemt naar elk struct-type en wiens enige taak het is om enkele leden van de initiële struct naar de tweede struct te kopiëren: de struct pointer-variabelen zullen op de stapel staan. Aangezien toewijzingen plaatsvinden van de leden van één struct naar de andere s, zullen de adressen van de structuur, voor elke opdracht, worden geladen in een register om toegang te krijgen tot de leden van de structuur die worden gekopieerd. Als de struct pointers zouden worden gedeclareerd met het register sleutelwoord, zouden de adressen van de structs in de registers blijven, waardoor de laad-adres-in-register instructies voor elke toewijzing effectief zouden worden verwijderd.

Onthoud nogmaals dat de bovenstaande beschrijving van toepassing is op niet-geoptimaliseerde code.

Answer

Je vertelt de compiler in feite dat je het adres van de variabele niet zult nemen en de compiler kan dan schijnbaar verdere optimalisaties. Voor zover ik weet, zijn moderne compilers vrij goed in staat om te bepalen of een variabele kan / moet worden bewaard in een register of niet.

Voorbeeld:

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

Reacties

  • Dereferentie of neem het adres van?
  • @detly: je hebt natuurlijk gelijk

Antwoord

In de 16-bits computertijd had men vaak meerdere registers nodig om 32-bits vermenigvuldigingen en delen uit te voeren. drijvende-komma-eenheden werden in chips opgenomen en vervolgens namen 64-bits architecturen het “over”, zowel de breedte van de registers als het aantal ervan werd uitgebreid. Dit leidt uiteindelijk tot een volledige herarchitectuur van de CPU. Zie Registreer bestanden op Wikipedia.

Kortom, het zou wat tijd kosten om Kijk wat er werkelijk aan de hand is als je een 64-bit X86- of ARM-chip gebruikt.Als je een 16-bits ingebouwde CPU gebruikt, kan dit je echt iets opleveren. De meeste kleine ingebedde chips zijn echter niet tijdkritisch – je microgolfoven test je touchpad misschien wel 10.000 keer per seconde – niets dat een 4Mhz CPU.

Reacties

  • 4 MIPS / 10.000 polls / sec = 400 instructies / poll. Dat ‘ heeft lang niet zoveel marge als je ‘ zou willen hebben. Merk ook op dat nogal wat 4 MHz-processors intern microgecodeerd waren, wat betekent dat ze bij lange na niet in de buurt van 1 MIP / MHz waren.
  • @ JohnR.Strohm – Er kunnen situaties zijn waarin men zou kunnen rechtvaardigen om uit te zoeken hoeveel instructies cycli het ‘ s gaat duren, maar vaak is de goedkopere uitweg nu om gewoon een snellere chip te krijgen en het product de deur uit te krijgen. In het gegeven voorbeeld hoeft men natuurlijk niet ‘ te blijven bemonsteren bij 10.000 als men een commando heeft – het kan zijn dat het bemonsteren gedurende een kwart seconde niet zonder schade wordt hervat gedaan. Het wordt steeds moeilijker om erachter te komen waar door programmeur gestuurde optimalisatie er toe doet.
  • Het is niet altijd mogelijk om ” gewoon een snellere chip te krijgen en de product de deur uit “. Overweeg real-time beeldverwerking. 640×480 pixels / frame x 60 frames / seconde x N instructies per pixel worden snel toegevoegd. (De les van real-time beeldverwerking is dat je bloed zweet over je pixelkorrels en bijna al het andere negeert, omdat het eenmaal per regel of eenmaal per patch of eenmaal per frame wordt uitgevoerd, in tegenstelling tot honderden keren per regel of patch of tientallen of honderdduizenden keren per frame.)
  • @ JohnR.Strohm – als ik het real-time beeldverwerkingsvoorbeeld neem, zou ik aannemen dat de minimale omgeving 32 bits is. Uitgaan op een tak (omdat ik niet ‘ weet hoe praktisch dit is om te doen) veel grafische versnellers die in chips zijn ingebouwd, kunnen ook bruikbaar zijn voor beeldherkenning, dus ARM-chips (bijvoorbeeld ) die geïntegreerde rendering-engines hebben, kunnen aanvullende ALUs hebben die kunnen worden herkend. Tegen die tijd is het gebruik van het ‘ register ‘ trefwoord voor optimalisatie een klein deel van het probleem.

Antwoord

Om vast te stellen of het registerzoekwoord enige betekenis heeft, doen kleine voorbeeldcodes dit niet. Hier is een c-code wat mij suggereert dat het register sleutelwoord nog steeds een betekenis heeft. Maar het zou anders kunnen zijn met GCC op Linux, ik weet het niet. ZAL register int k & l worden opgeslagen in een CPU-register of niet? Linux-gebruikers (vooral) zouden moeten compileren met GCC en optimalisatie. Met Borland bcc32 lijkt het register sleutelwoord te functioneren (in dit voorbeeld), aangezien de & -operator foutcodes geeft voor register gedeclareerde gehele getallen. NOTITIE! Dit is NIET het geval met een klein voorbeeld met Borland op Windows! Om echt te zien wat de compiler optimaliseert of niet, moet het meer dan een klein voorbeeld zijn. Lege lussen doen dat niet! Desalniettemin – ALS een adres kan worden gelezen met de & -operator, wordt de variabele niet opgeslagen in een CPU-register. Maar als een in het register gedeclareerde variabele niet kan worden gelezen (veroorzaakt foutcode bij compilatie), moet ik aannemen dat het register-trefwoord de variabele daadwerkelijk in een CPU-register plaatst. Het kan op verschillende platforms verschillen, ik weet het niet . (Als het werkt, zal het aantal “vinkjes” veel lager zijn met de registeraangifte.

/* 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; } 

Reacties

  • Er zal een deling zijn met nul hierboven, verander alstublieft {tmp + = ii / jj;} in {tmp + = jj / ii;} – sorry hiervoor
  • Laat ook k en i begin met 1 – niet nul. Het spijt me zeer.
  • U kunt uw antwoord bewerken in plaats van correcties in opmerkingen te schrijven.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *