Când este util cuvântul cheie register în C?

Sunt confuz cu privire la utilizarea cuvântului cheie register în C. În general, se spune că utilizarea sa nu este ” a fost nevoie de ca în această întrebare de pe stackoverflow .

Este acest cuvânt cheie complet redundant în C datorită compilatoarelor moderne sau există situații în care poate fi în continuare util? Dacă da, care sunt unele situații în care utilizarea cuvântului cheie register este de fapt utilă?

Comentarii

  • Cred că întrebarea legată și răspunsurile la aceasta sunt aceleași pe care le puteți aștepta aici. Deci, nu vor exista informații noi pe care le puteți obține aici.
  • @UwePlonus Am crezut că același lucru despre const cuvânt cheie, dar această întrebare a dovedit că am greșit. Așa că am ‘ Voi aștepta să văd ce primesc.
  • Cred că cuvântul cheie const este ceva diferit față de registru.
  • Este ‘ este util dacă vă întoarceți în mod accidental în timp și sunteți forțați să utilizați unul dintre compilatoarele C timpurii. În afară de asta, ‘ nu este deloc util, ‘ este învechit de ani de zile.
  • @ UwePlonus am vrut doar să spun că pot exista scenarii necunoscute pentru mine în care un cuvânt cheie ar putea fi util.

Răspuns

Nu este redundant din punct de vedere al limbajului, ci doar că, folosindu-l, îi spuneți compilatorului, ați prefera să aveți o variabilă stocată în registru. Există totuși o garanție absolut nulă că acest lucru va de fapt se întâmplă în timpul runtimeului.

Comentarii

  • Mai mult decât atât, ‘ este aproape întotdeauna cazul în care compilatorul știe cel mai bine și ‘ îți pierzi respirația
  • @jozefg: chiar mai rău. Risti că compilatorul îți onorează cererea / sugestia și produce rău .

Răspuns

După cum sa menționat deja, optimizatorii compilatorului sunt în esență ren der register cuvânt cheie învechit în alte scopuri decât prevenirea aliasării. Cu toate acestea, există baze de cod întregi care sunt compilate cu optimizarea dezactivată (-O0 în gcc-speak ). Pentru un astfel de cod, cuvântul cheie register poate avea un efect excelent. Mai exact, variabilele care altfel ar obține un slot pe stivă (adică toți parametrii funcției și variabilele automate) pot să fie plasate direct într-un registru dacă sunt declarate cu register cuvânt cheie.

Aici este un exemplu din lumea reală: presupunem că a avut loc o recuperare a bazei de date și că codul de recuperare a umplut tuplul recuperat într-o structură C. În plus, să presupunem că unele subseturi ale acestei structuri C trebuie copiat într-o altă structură – poate că a doua structură este o înregistrare cache care reprezintă metadatele stocate în baza de date care, din cauza constrângerilor de memorie, cache doar un subset al fiecărei înregistrări de metadate stocate în baza de date.

Având în vedere o funcție care ia un pointer către fiecare tip de struct și a cărei sarcină este de a copia unii membri din structul inițial în al doilea struct: variabilele de pointer struct vor trăi în stivă. Pe măsură ce atribuțiile apar din membrii unui struct la celelalte „s, adresele struct” vor, pentru fiecare sarcină, să fie încărcat într-un registru pentru a efectua accesul membrilor structurilor care sunt copiate. Dacă indicatorii struct ar fi declarați cu cuvântul cheie register, adresele structurilor ar rămâne în registre, tăind efectiv instrucțiunile de încărcare-adresă-în-registru pentru fiecare atribuire.

Rețineți că descrierea de mai sus se aplică codului neoptimizat .

Răspuns

Practic îi spuneți compilatorului că nu ați luat adresa variabilei, iar compilatorul poate face aparent optimizări suplimentare. Din câte știu, compilatoarele moderne sunt destul de capabile să determine dacă o variabilă poate / ar trebui păstrată sau nu într-un registru.

Exemplu:

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

Comentarii

  • Deferențați sau luați adresa?
  • @detly: sunteți bineînțeles corect

Răspuns

În zilele de computer pe 16 biți, de multe ori era nevoie de mai multe registre pentru a executa înmulțirea și împărțirea pe 32 de biți. unitățile cu virgulă mobilă au fost încorporate în cipuri și apoi „au preluat” arhitecturile pe 64 de biți, atât lățimea registrelor, cât și numărul acestora extins. Aceasta duce în cele din urmă la o re-arhitectură completă a procesorului. Vezi Înregistrează fișiere pe Wikipedia.

Pe scurt, ți-ar lua un pic de timp aflați ce se întâmplă de fapt dacă sunteți pe un chip 64-bit X86 sau ARM.Dacă utilizați un procesor încorporat pe 16 biți, acest lucru vă poate oferi ceva. Cu toate acestea, majoritatea cipurilor mici încorporate nu rulează nimic critic – cuptorul dvs. cu microunde ar putea să vă probeze touchpadul de 10.000 de ori pe secundă – nimic care să CPU de 4Mhz.

Comentarii

  • 4 MIPS / 10.000 sondaje / sec = 400 instrucțiuni / sondaj. ‘ nu este aproape la fel de mare ca pe care ‘ ți-ar plăcea să o ai. Rețineți, de asemenea, că destul de multe procesoare de 4 MHz au fost microcodificate intern, ceea ce înseamnă că nu erau nici pe departe 1 MIP / MHz.
  • @ JohnR.Strohm – Ar putea exista situații în care s-ar putea justifica aflarea exactă a câte instrucțiuni ciclurile pe care ‘ le va lua, dar de multe ori cea mai ieftină ieșire acum este să obțineți doar un cip mai rapid și să scoateți produsul pe ușă. În exemplul dat, bineînțeles, nu trebuie ‘ să continui eșantionarea la 10.000 dacă cineva are o comandă – s-ar putea să nu reia eșantionarea timp de un sfert de secundă fără niciun rău Terminat. Devine din ce în ce mai dificil să ne dăm seama unde va conta optimizarea direcționată de programator.
  • Nu este întotdeauna posibil să ” pur și simplu să obțineți un cip mai rapid și să obțineți produs afară din ușă „. Luați în considerare procesarea imaginilor în timp real. 640×480 pixeli / cadru x 60 cadre / secundă x N instrucțiuni pe pixel se adaugă rapid. (Lecția din procesarea imaginilor în timp real este că transpirați sânge peste nucleele dvs. de pixeli și aproape că ignorați orice altceva, deoarece rulează o dată pe linie sau o dată pe patch sau o dată pe cadru, spre deosebire de sute de ori pe linie sau patch sau zeci sau sute de mii de ori pe cadru.)
  • @ JohnR.Strohm – luând exemplul de procesare a imaginilor în timp real, aș presupune că mediul minim este de 32 de biți. Ieșirea pe un membru (deoarece nu ‘ nu știu cât de practic este acest lucru) multe acceleratoare grafice încorporate în cipuri ar putea fi, de asemenea, utilizabile pentru recunoașterea imaginii, deci cipurile ARM (de exemplu ) care au motoare de redare integrate pot avea ALU-uri suplimentare utilizabile pentru recunoaștere. În acel moment, utilizarea cuvântului cheie ‘ registry ‘ pentru optimizare este o mică parte a problemei.

Răspuns

Pentru a stabili dacă cuvântul cheie registru are vreo semnificație, nu există exemple minuscule de coduri câștigate. Iată un cod c ceea ce îmi sugerează, cuvântul cheie registru are încă o semnificație. Dar ar putea fi diferit cu GCC pe Linux, nu știu. ÎNREGISTRARE int k & va fi stocată într-un registru CPU sau nu? Utilizatorii Linux (în special) ar trebui să compileze cu GCC și optimizare. Cu Borland bcc32, cuvântul cheie register pare să funcționeze (în acest exemplu), deoarece operatorul & oferă coduri de eroare pentru registrele întregi declarate. NOTĂ! Acesta NU este cazul cu un mic exemplu cu Borland pe Windows! Pentru a vedea cu adevărat ce optimizează sau nu compilatorul, trebuie să fie un exemplu mai mult decât mic. Buclele goale nu se vor face! Cu toate acestea – DACĂ o adresă POATE fi citită cu & -operator, variabila nu este stocată într-un registru CPU. Dar dacă un registru declarat variabilă nu poate fi citit (cauzând codul de eroare la compilare) – trebuie să presupun că cuvântul cheie registru pune într-adevăr variabila într-un registru CPU. Poate diferi pe diferite platforme, nu știu . (Dacă funcționează, numărul de „căpușe” va fi mult mai mic odată cu declarația de registru.

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

Comentarii

  • Va exista o diviziune cu zero mai sus, vă rugăm să schimbați {tmp + = ii / jj;} în {tmp + = jj / ii;} – îmi pare rău pentru asta
  • De asemenea, permiteți k și i începeți cu 1 – nu zero. Îmi pare rău.
  • Puteți modifica răspunsul în loc să scrieți corecții în comentarii.

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *