Jeg er forvirret over brugen af register
søgeord i C. Det fortælles generelt, at brugen ikke er ” t behov som i dette spørgsmål om stackoverflow .
Er dette nøgleord helt overflødigt i C på grund af moderne compilers, eller er der situationer, hvor det kan stadig være nyttigt? Hvis ja, hvad er nogle situationer, hvor brug af register
nøgleord faktisk er nyttigt?
Kommentarer
- Jeg synes, det linkede spørgsmål og svarene på det er de samme som du kan forvente her. Så der vil ikke være nogen nye oplysninger, du kan få her.
- @UwePlonus Jeg troede, at samme om
const
søgeord, men dette spørgsmål viste, at jeg tog fejl. Så jeg ‘ Vent og se, hvad jeg får. - Jeg tror, at
const
nøgleordet er noget andet i forhold til registret. - Det ‘ er nyttigt, hvis du ved et uheld går tilbage i tiden og bliver tvunget til at bruge en af de tidlige C-kompilatorer. Bortset fra at det ‘ overhovedet ikke er nyttigt, har det ‘ været helt forældet i årevis.
- @ UwePlonus Jeg mente bare, at der kan være ukendte scenarier, hvor et nøgleord kan være nyttigt.
Svar
Det er ikke overflødigt med hensyn til sprog, det er bare, at ved at bruge det, når du fortæller kompilatoren, ville du “foretrække” at have en variabel gemt i registret. Der er dog absolut nul garanti for, at dette faktisk ske i løbet af løbetiden.
Kommentarer
- Mere end det er det ‘ næsten altid sådan, at kompilatoren ved bedst, og du ‘ spilder vejret
- @jozefg: endnu værre. Du risikerer, at kompilatoren respekterer din anmodning / tip og producerer dårligere kode som et resultat.
Svar
Som allerede nævnt er kompilatoroptimerer i det væsentlige ren der register
søgeord forældet til andre formål end at forhindre aliasing. Der er dog hele kodebaser, som er kompileret med optimering slået fra (-O0
i gcc-speak ). For en sådan kode kan nøgleordet register
have stor effekt. Specifikt, variabler, der ellers ville få en plads på stakken (dvs. alle funktionsparametre og automatiske variabler) kan placeres direkte i et register, hvis deklareres med register
nøgleord.
Her er et eksempel fra den virkelige verden: antag, at der er fundet nogen databasegendannelse, og at hentningskoden har fyldt den hentede tuple i en C-struktur. Antag endvidere, at nogle delmængder af denne C-struktur skal kopieres til en anden struktur – måske er denne anden struktur en cache-post, der repræsenterer de metadata, der er gemt i databasen, som på grund af hukommelsesbegrænsninger kun cacher et undersæt af hver metadata-post som lagret i databasen.
Givet en funktion, der tager en markør til hver strukturtype, og hvis eneste opgave det er at kopiere nogle medlemmer fra den oprindelige struktur til den anden struct: struct-pointervariablerne lever på stakken. Da tildelinger forekommer fra en strukturs medlemmer til de andre “s, vil strukturens adresser for hver opgave indlæses i et register for at udføre adgangen til de strukturer, der kopieres. Hvis strukturpegerne skulle erklæres med nøgleordet register
, ville structternes adresser forblive i registre, hvilket effektivt skar vejledningen adresse-i-register instruktionerne for hver opgave.
Husk igen, at beskrivelsen ovenfor gælder for ikke-optimeret kode.
Svar
Du fortæller grundlæggende kompilatoren, at du ikke vinder adressen på variablen, og kompilatoren kan så tilsyneladende lave yderligere optimeringer. Så vidt jeg ved, er moderne compilere ret i stand til at afgøre, om en variabel kan / skal opbevares i et register eller ej.
Eksempel:
int main(){ int* ptr; int a; register int b; ptr = &a; ptr = &b; //this won"t compile return 0; }
Kommentarer
- Forstyrrelse eller tage adressen på?
- @detly: du har selvfølgelig ret
Svar
I 16-bit computerdage havde man ofte brug for flere registre for at udføre 32-bit multiplikationer og opdelinger. flydende punkt enheder blev inkorporeret i chips, og derefter tog 64-bit arkitekturer “over”, både bredden på registre og antallet af dem blev udvidet. Dette fører til sidst til en komplet omarkitektur af CPUen. Se Registrer filer på Wikipedia.
Kort sagt, det tager dig lidt tid at finde ud af re ud, hvad der faktisk foregår, hvis du er på en 64-bit X86- eller ARM-chip.Hvis du bruger en 16-bit integreret CPU, kan det faktisk give dig noget. De fleste små indlejrede chips kører dog ikke noget tidskritisk – din mikrobølgeovn prøver muligvis på din touchpad 10.000 gange i sekundet – intet der belaster en 4Mhz CPU.
Kommentarer
- 4 MIPS / 10.000 afstemninger / sek = 400 instruktioner / afstemning. At ‘ ikke er nær så meget margen som du ‘ gerne vil have. Bemærk også, at en hel del 4 MHz-processorer var mikrokodet internt, hvilket betyder, at de ikke var tæt på 1 MIP / MHz.
- @ JohnR.Strohm – Der kan være situationer, hvor man kunne retfærdiggøre at finde ud af nøjagtigt hvor mange instruktioner cykler det ‘ tager, men ofte er den billigere vej ud nu bare at få en hurtigere chip og få produktet ud af døren. I det givne eksempel behøver man selvfølgelig ikke ‘ at prøve med 10.000, hvis man har en kommando – det genoptager muligvis ikke sampling i et kvart sekund uden skade Færdig. Det bliver stadig sværere at finde ud af, hvor programmørstyret optimering betyder noget.
- Det er ikke altid muligt at ” bare få en hurtigere chip og få den produkt ud af døren “. Overvej billedbehandling i realtid. 640×480 pixels / ramme x 60 billeder / sekund x N-instruktioner pr. Pixel tilføjes hurtigt. (Læringen fra billedbehandling i realtid er, at du sveder blod over dine pixelkerner, og at du næsten ignorerer alt andet, fordi det kører en gang pr. Linje eller en gang pr. Patch eller en gang pr. Frame i modsætning til hundreder af gange pr. Linje eller patch eller titusinder eller hundreder af tusinder af gange pr. ramme.)
- @ JohnR.Strohm – tager jeg billedbehandlingseksemplet i realtid, antager jeg, at minimumsmiljøet er 32 bit. At gå ud på en lem (fordi jeg ikke ‘ ikke ved, hvor praktisk dette er at gøre), mange grafiske acceleratorer, der er indbygget i chips, kan også bruges til billedgenkendelse, så ARM-chips (for eksempel ), der har integrerede gengivelsesmotorer, kan have yderligere ALUer, der kan bruges til genkendelse. På det tidspunkt er brugen af ‘ register ‘ søgeord til optimering en lille del af problemet.
Svar
For at fastslå, om registerets nøgleord har nogen betydning, vinder små eksemplarskoder ikke. Her er en c-kode hvilket antyder for mig, at nøgleordet til registeret stadig har en betydning. Men det kan være anderledes med GCC på Linux, det ved jeg ikke. SKAL register int k & l gemmes i et CPU-register eller ej? Linux-brugere (især) skal kompilere med GCC og optimering. Med Borland bcc32 ser registerregistreringsnøgleordet ud til at fungere (i dette eksempel), da & -operatøren giver fejlkoder for registrerede erklærede heltal. BEMÆRK! Dette er IKKE tilfældet med et lille eksempel med Borland på Windows! For virkelig at se, hvad kompilatoren optimerer eller ej, skal det være et mere end et lille eksempel. Tomme sløjfer vinder ikke! Ikke desto mindre – HVIS en adresse KAN læses med & -operatøren, er variablen ikke gemt i et CPU-register. Men hvis en registerdeklareret variabel ikke kan læses (forårsager fejlkode ved kompilering) – må jeg antage, at registerets nøgleord faktisk placerer variablen i et CPU-register. Det kan variere på forskellige platforme, ved jeg ikke . (Hvis det fungerer, vil antallet af “kryds” 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=ⅆ ep=&e; eep=ⅇ fp=&f; ffp=&ff; gp=&g; ggp=≫ hp=&h; hhp=&hh; ip=&i; iip=ⅈ 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
- Der vil være en division med nul ovenfor, skal du ændre {tmp + = ii / jj;} til {tmp + = jj / ii;} – virkelig beklager dette
- Lad også k og i begynde med 1 – ikke nul. Meget ked af det.
- Du kan redigere dit svar i stedet for at skrive rettelser i kommentarer.