Når er registernøkkelordet faktisk nyttig i C?

Jeg er forvirret om bruken av register nøkkelord i C. Det blir generelt fortalt at bruken ikke er » t nødvendig som i dette spørsmålet om stackoverflow .

Er dette søkeordet helt overflødig i C på grunn av moderne kompilatorer, eller er det situasjoner der det kan fortsatt være nyttig? Hvis ja, hva er noen situasjoner der bruk av register nøkkelord faktisk er nyttig?

Kommentarer

  • Jeg tror det koblede spørsmålet og svarene på det er de samme som du kan forvente deg her. Så det vil ikke være noen ny informasjon du kan få her.
  • @UwePlonus Jeg trodde samme om const søkeord, men dette spørsmålet beviste at jeg tok feil. Så jeg ‘ Vent og se hva jeg får.
  • Jeg tror const nøkkelordet er noe annet mot register.
  • Det ‘ er nyttig hvis du ved et uhell går tilbake i tid og blir tvunget til å bruke en av de første C-kompilatorene. Annet enn at det ‘ ikke er nyttig i det hele tatt, er det ‘ helt foreldet i mange år.
  • @ UwePlonus Jeg mente bare at det kan være ukjente scenarier for meg der et nøkkelord kan være nyttig.

Svar

Det er ikke overflødig når det gjelder språk, det er bare det at når du bruker det, forteller du kompilatoren at du foretrekker å ha en variabel lagret i registeret. Det er imidlertid absolutt ingen garanti for at dette faktisk skje i løpetid.

Kommentarer

  • Mer enn det, det er ‘ nesten alltid slik at kompilatoren vet best, og du ‘ kaster bort pusten din
  • @jozefg: enda verre. Du risikerer at kompilatoren respekterer forespørselen / hintet og produserer verre kode som et resultat.

Svar

Som allerede nevnt, kompilatoroptimerer i det vesentlige ren der register nøkkelordet er foreldet for andre formål enn å forhindre aliasing. Imidlertid er det hele kodebaser som er kompilert med optimalisering slått av (-O0 i gcc-speak ). For slik kode kan nøkkelordet register ha stor effekt. Nærmere bestemt kan variabler som ellers vil få en plass på stakken (dvs. alle funksjonsparametere og automatiske variabler) plasseres direkte i et register hvis deklareres med register nøkkelord.

Her «er et eksempel fra den virkelige verden: antar at noe databasegjenfinning har skjedd og at hentekoden har fylt den hentede tuplen i en C-struktur. Anta videre at noen delsett av denne C-strukturen må kopieres til en annen struktur — kanskje denne andre strukturen er en cache-post som representerer metadataene som er lagret i databasen, som på grunn av minnebegrensninger bare cacher et delsett av hver metadatapost som er lagret i databasen.

Gitt en funksjon som tar en peker til hver strukturtype og hvis eneste jobb det er å kopiere noen medlemmer fra den opprinnelige strukturen til den andre strukturen: strukturpekervariablene vil leve på bunken. Når oppgaver skjer fra en strukturs medlemmer til de andre «s, vil strukturadressene for hver oppgave, lastes inn i et register for å utføre tilgangen til strukturens medlemmer som blir kopiert. Hvis strukturpekerne skulle deklareres med nøkkelordet register, ville strukturens adresser forbli i registerene, og effektivt kutte ut instruksjonene for belastningsadresse-i-register for hver oppgave.

Igjen, husk at beskrivelsen ovenfor gjelder ikke-optimalisert kode.

Svar

Du forteller i utgangspunktet kompilatoren at du ikke vil ta adressen til variabelen, og kompilatoren kan da tilsynelatende lage ytterligere optimaliseringer. Så vidt jeg vet er moderne kompilatorer ganske i stand til å avgjøre om en variabel kan / bør oppbevares i et register eller ikke.

Eksempel:

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

Kommentarer

  • Forstyrrelse eller ta adressen til?
  • @detly: du har selvfølgelig rett

Svar

I løpet av 16-biters datadager trengte man ofte flere registre for å utføre 32-bits multiplikasjoner og delinger. flytende punktenheter ble innlemmet i sjetonger og deretter tok 64-biters arkitektur «over», både bredden på registerene og antallet av dem utvidet. Dette fører til slutt til en fullstendig omarkitektur av CPU. Se Registrer filer på Wikipedia.

Kort sagt, det vil ta deg litt tid å finne ut re ut hva som faktisk skjer hvis du er på en 64-biters X86- eller ARM-brikke.Hvis du bruker en 16-biters innebygd CPU, kan dette faktisk gi deg noe. De fleste små innebygde sjetonger kjører imidlertid ikke noe tidskritisk – mikrobølgeovnen din tar kanskje prøveplaten på 10.000 ganger i sekundet – ingenting som belaster en 4Mhz CPU.

Kommentarer

  • 4 MIPS / 10.000 avstemninger / sek = 400 instruksjoner / avstemning. At ‘ ikke er så mye margin som du ‘ vil ha. Vær også oppmerksom på at ganske mange 4 MHz-prosessorer ble mikrokodet internt, noe som betyr at de ikke var i nærheten av 1 MIP / MHz.
  • @ JohnR.Strohm – Det kan være situasjoner der man kan rettferdiggjøre å finne ut nøyaktig hvor mange instruksjoner sykler det ‘ som kommer til å ta, men ofte er den billigere veien ut nå å bare få en raskere chip og få produktet ut av døren. I eksemplet som er gitt, trenger man selvfølgelig ikke ‘ å prøve med 10 000 hvis man har en kommando – det kan hende det ikke fortsetter sampling i et kvart sekund uten skade ferdig. Det blir stadig vanskeligere å finne ut hvor optimalisering av programmeringsstyring kommer til å spille noen rolle.
  • Det er ikke alltid mulig å » bare få en raskere chip og få produkt ut døren «. Vurder bildebehandling i sanntid. 640×480 piksler / ramme x 60 bilder / sekund x N instruksjoner per piksel legger raskt til. (Leksjonen fra sanntids bildebehandling er at du svetter blod over pikselkjernene dine, og du omtrent ignorerer alt annet, fordi det kjører en gang per linje eller en gang per patch eller en gang per ramme, i motsetning til hundrevis av ganger per linje eller lapp eller titusener eller hundretusener av ganger per ramme.)
  • @ JohnR.Strohm – tar jeg sanntidsbildebehandlingseksemplet, vil jeg anta at minimumsmiljøet er 32 bits. Å gå ut på et lem (fordi jeg ikke ‘ ikke vet hvor praktisk dette er å gjøre) mange grafikkakseleratorer innebygd i sjetonger kan også være brukbare for bildegjenkjenning, så ARM-brikker (for eksempel ) som har integrerte renderingsmotorer, kan ha flere ALU-er som kan brukes til gjenkjenning. På den tiden er bruken av ‘ register ‘ søkeordet for optimalisering en liten del av problemet.

Svar

For å fastslå om registeret nøkkelordet har noen betydning, vil små eksempelkoder ikke gjøre. Her er en c-kode som antyder for meg, har registernøkkelordet fortsatt en betydning. Men det kan være annerledes med GCC på Linux, jeg vet ikke. SKAL register int k & l lagres i et CPU-register eller ikke? Linux-brukere (spesielt) bør kompilere med GCC og optimalisering. Med Borland bcc32 ser det ut til at ordet med nøkkelordet fungerer (i dette eksemplet), da & -operatøren gir feilkoder for registrerte heltall. MERK! Dette er IKKE tilfelle med et lite eksempel med Borland på Windows! For å virkelig se hva kompilatoren optimaliserer eller ikke, må det være et mer enn et lite eksempel. Tomme sløyfer vil ikke gjøre! Likevel – HVIS en adresse KAN leses med & -operatøren, er variabelen ikke lagret i et CPU-register. Men hvis en registerdeklarert variabel ikke kan leses (forårsaker feilkode ved kompilering) – må jeg anta at registernøkkelordet faktisk plasserer variabelen i et CPU-register. Det kan variere på forskjellige plattformer, jeg vet ikke . (Hvis det fungerer, vil antall «flått» være langt lavere med registererklæringen.

/* 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 vil være en inndeling med null over, vær så snill å endre {tmp + = ii / jj;} til {tmp + = jj / ii;} – beklager virkelig dette
  • La også k og i begynn med 1 – ikke null. Veldig lei meg.
  • Du kan redigere svaret i stedet for å skrive rettelser i kommentarene.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *