När är registret nyckelordet faktiskt användbart i C?

Jag är förvirrad över användningen av register nyckelord i C. Det brukar sägas att dess användning inte är ” t behövs som i den här frågan om stackoverflow .

Är detta sökord helt överflödigt i C på grund av moderna kompilatorer eller finns det situationer där det kan fortfarande vara användbart? Om ja, i vilka situationer kan användningen av register nyckelord egentligen vara till hjälp?

Kommentarer

  • Jag tror att den länkade frågan och svaren på den är desamma som du kan förvänta dig här. Så det kommer ingen ny information som du kan få här.
  • @UwePlonus Jag trodde att samma om const sökord men denna fråga visade att jag hade fel. Så jag ’ Jag väntar och ser vad jag får.
  • Jag tror att const nyckelordet är något annorlunda mot registret.
  • Det ’ är användbart om du av misstag går tillbaka i tiden och tvingas använda en av de tidiga C-kompilatorerna. Förutom att det ’ inte är användbart alls, är det ’ helt föråldrat i flera år.
  • @ UwePlonus Jag menade bara att det kan finnas scenarier okända för mig där ett nyckelord kan vara användbart.

Svar

Det är inte överflödigt i termer av språk, det är bara att genom att använda det säger du till kompilatorn att du ”föredrar” att ha en variabel lagrad i registret. Det finns dock absolut ingen garanti för att detta faktiskt kommer att hända under körning.

Kommentarer

  • Mer än så är det ’ nästan alltid så att kompilatorn vet bäst och du ’ slösar bort andan
  • @jozefg: ännu värre. Du riskerar att kompilatorn respekterar din begäran / ledtråd och producerar värre kod som ett resultat.

Svar

Som redan nämnts är kompilatoroptimiserare i huvudsak ren där nyckelordet register föråldrat för andra ändamål än att förhindra aliasing. Det finns dock hela kodbaser som kompileras med optimering avstängd (-O0 i gcc-speak ). För sådan kod kan nyckelordet register ha stor effekt. Specifikt kan variabler som annars skulle få en plats på stacken (dvs. alla funktionsparametrar och automatiska variabler) placeras direkt i ett register om deklareras med register nyckelord.

Här ”är ett verkligt exempel: antag att någon databashämtning har inträffat och att hämtningskoden har stoppat den hämtade tupeln i en C-struktur. Antag vidare att någon delmängd av denna C-struktur måste kopieras till en annan struktur – kanske den här andra strukturen är en cachepost som representerar metadata lagrade i databasen som på grund av minnesbegränsningar bara cachar en delmängd av varje metadatapost som lagrad i databasen.

Givet en funktion som tar en pekare till varje strukturtyp och vars enda uppgift det är att kopiera några medlemmar från den ursprungliga strukturen till den andra strukturen: strukturpekervariablerna kommer att finnas kvar på stacken. Eftersom tilldelningar sker från en strukturs medlemmar till de andra ”s, kommer strukturadresserna för varje uppdrag, laddas in i ett register för att utföra åtkomst till strukturmedlemmarna som kopieras. Om strukturpekarna skulle deklareras med nyckelordet register, skulle striktens adresser förbli i registren, vilket effektivt skulle skära ut instruktioner för belastningsadress-till-register för varje uppgift.

Kom ihåg igen att beskrivningen ovan gäller ooptimerad kod.

Svar

Du berättar i grund och botten för kompilatorn att du inte kommer att ta adressen till variabeln och kompilatorn kan då uppenbarligen göra ytterligare optimeringar. Såvitt jag vet är moderna kompilatorer ganska kapabla att avgöra om en variabel kan / ska hållas i ett register eller inte.

Exempel:

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

Kommentarer

  • Dereference eller ta adressen till?
  • @detly: du har naturligtvis rätt

Svar

Under 16-bitars datordagar behövde man ofta flera register för att utföra 32-bitars multiplikationer och delningar. floating point-enheter införlivades i marker och sedan ”tog över” 64-bitars arkitekturer, både bredden på registren och antalet dem utökades. Detta leder så småningom till en fullständig omarkitektur av CPU. Se Registrera filer på Wikipedia.

Kort sagt, det tar lite tid att räkna ut ta reda på vad som faktiskt händer om du använder ett 64-bitars X86- eller ARM-chip.Om du använder en 16-bitars inbäddad processor kan det faktiskt skaffa dig något. De flesta små inbäddade marker kör dock inte någonting tidskritiskt – din mikrovågsugn kan sampla din pekplatta 10 000 gånger i sekunden – inget som spänner en 4Mhz CPU.

Kommentarer

  • 4 MIPS / 10 000 omröstningar / sek = 400 instruktioner / enkät. Att ’ inte är så mycket marginal som du ’ vill ha. Observera också att en hel del 4 MHz-processorer mikrokodades internt, vilket betyder att de inte var nära 1 MIP / MHz.
  • @ JohnR.Strohm – Det kan finnas situationer där man kan motivera att räkna ut exakt hur många instruktioner cyklar det ’ tar, men ofta är den billigare vägen ut nu bara att få ett snabbare chip och få ut produkten genom dörren. I det givna exemplet behöver man naturligtvis inte ’ fortsätta att sampla vid 10 000 om man har ett kommando – det kanske inte fortsätter samplingen i en kvarts sekund utan skada Gjort. Det blir allt svårare att ta reda på var programmeringsriktad optimering kommer att ha betydelse.
  • Det är inte alltid möjligt att ” bara få ett snabbare chip och få produkt ut genom dörren ”. Överväg bildbehandling i realtid. 640×480 pixlar / ram x 60 bilder / sekund x N-instruktioner per pixel lägger snabbt till. (Läran från bildbehandling i realtid är att du svettar blod över dina pixelkärnor och att du nästan ignorerar allt annat, eftersom det körs en gång per rad eller en gång per lapp eller en gång per bild, i motsats till hundratals gånger per rad eller lapp eller tiotals eller hundratusentals gånger per bildruta.)
  • @ JohnR.Strohm – tar jag realtidsbildbehandlingsexemplet, jag antar att minimimiljön är 32 bitar. Att gå ut på en lem (eftersom jag inte ’ inte vet hur praktiskt det här är att göra) kan många grafiska acceleratorer inbyggda i chips också vara användbara för bildigenkänning, så ARM-chips (till exempel ) som har integrerade renderingsmotorer kan ha ytterligare ALU som kan användas för igenkänning. Vid den tiden är användningen av ’ registret ’ nyckelord för optimering en liten del av problemet.

Svar

För att fastställa om registernyckelordet har någon betydelse, kommer små exempelkoder inte att göra. Här är en c-kod vilket tyder på att nyckelordet i registret fortfarande har en betydelse. Men det kan vara annorlunda med GCC på Linux, jag vet inte. SKAL registret int k & l lagras i ett CPU-register eller inte? Linux-användare (särskilt) bör kompilera med GCC och optimering. Med Borland bcc32 verkar registernyckelordet fungera (i det här exemplet), eftersom & -operatören ger felkoder för registerdeklarerade heltal. NOTERA! Detta är INTE fallet med ett litet exempel med Borland på Windows! För att verkligen se vad kompilatorn optimerar eller inte måste det vara ett mer än ett litet exempel. Tomma slingor kommer inte att göra! Ändå – OM en adress KAN läsas med & -operatören, är variabeln inte lagrad i ett CPU-register. Men om en registerdeklarerad variabel inte kan läsas (orsakar felkod vid sammanställning) – måste jag anta att registernyckelordet faktiskt sätter variabeln i ett CPU-register. Det kan skilja sig på olika plattformar, jag vet inte . (Om det fungerar kommer antalet ”fästingar” att vara mycket lägre med registerdeklarationen.

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

Kommentarer

  • Det kommer att finnas en uppdelning med noll ovan, ändra {tmp + = ii / jj;} till {tmp + = jj / ii;} – verkligen ledsen för detta
  • Låt också k och i börja med 1 – inte noll. Mycket ledsen.
  • Du kan redigera ditt svar istället för att skriva korrigeringar i kommentarer.

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *