Olen hämmentynyt register
-avainsanan käytöstä C: ssä. Yleensä sanotaan, että sen käyttö ei ole ” Tarvitaan kuten tässä pinonkulun kysymyksessä .
Onko tämä avainsana täysin tarpeeton C: ssä nykyaikaisten kääntäjien vuoksi, vai onko tilanteita, joissa se voi kyllä olla hyödyllistä? Jos kyllä, missä tilanteissa register
-avainsanan käytöstä on todella hyötyä?
Kommentit
- Luulen, että linkitetty kysymys ja vastaukset siihen ovat samat kuin voit odottaa täällä. Joten ei ole uutta tietoa, jonka voit saada täältä.
- @UwePlonus Luulin, että sama
const
avainsanasta, mutta tämä kysymys osoitti, että olin väärässä. Joten ’ Odota ja katso mitä saan. - Luulen, että
const
-avainsana on jotain erilaista rekisteriä vastaan. - Se ’ s on hyödyllinen, jos palaat vahingossa ajassa taaksepäin ja pakotetaan käyttämään yhtä varhaisen C-kääntäjistä. Sen lisäksi, että se ’ ei ole ollenkaan hyödyllinen, se ’ on ollut täysin vanhentunut vuosia.
- @ UwePlonus Tarkoitin vain, että voi olla minulle tuntemattomia skenaarioita, joissa avainsanasta voi olla hyötyä.
Vastaa
Se ei ole tarpeeton kielen suhteen, vaan vain, että käyttämällä sitä sanot kääntäjälle, haluat ”mieluummin”, että muuttuja on tallennettu rekisteriin. Ei kuitenkaan ole mitään takeita siitä, että tämä todella tapahtuu tapahtuu ajon aikana.
Kommentit
- Lisäksi ’ tapahtuu melkein aina, että kääntäjä tietää parhaiten ja sinä ’ tuhlaat hengityksesi
- @jozefg: vielä pahempaa. On vaarana, että kääntäjä kunnioittaa pyyntöäsi / vihjettäsi ja tuottaa huonompi koodi seurauksena.
Vastaus
Kuten jo mainittiin, kääntäjän optimoijat ren johdata avainsana register
vanhentunut muihin tarkoituksiin kuin aliaksen estämiseen. On kuitenkin kokonaisia kooditietokantoja, jotka käännetään optimoinnin ollessa poissa käytöstä (-O0
gcc-speak ssa). Tällaista koodia varten avainsanalla register
voi olla suuri vaikutus. Erityisesti muuttujat, jotka muuten saisivat aukon pinoon (eli kaikki toimintoparametrit ja automaattiset muuttujat) voidaan sijoittaa suoraan rekisteriin, jos ne ilmoitetaan register
avainsana.
Tässä on todellinen esimerkki: oletetaan, että tietokannan haku on tapahtunut ja että hakukoodi on täyttänyt haetun dupleksin C-rakenteeseen. Oletetaan lisäksi, että jokin tämän C-rakenteen osajoukko on kopioitava toiseen strukturiin – ehkä tämä toinen rakenne on välimuistitietue, joka edustaa tietokantaan tallennettuja metatietoja, joka muistin rajoitusten vuoksi tallentaa välimuistiin vain osan kaikista metatietueista tietokantaan tallennettuna.
Annetaan funktio, joka vie osoittimen kuhunkin rakennetyyppiin ja jonka ainoa tehtävä on kopioida joitain jäseniä alkuperäisestä rakenteesta toiseen rakenteeseen: rakennen osoittimen muuttujat elävät pinossa. Koska tehtävät tapahtuvat yhden strukturin jäsenistä muille ”,” osoitteet kutakin tehtävää varten ladataan rekisteriin kopioitavien jäsenten pääsyn suorittamiseksi. Jos rakenneosoittimet ilmoitettaisiin avainsanalla register
, strukturien osoitteet pysyisivät rekistereissä ja leikkaisivat tehokkaasti kuhunkin osoitteeseen load-address-to-register-ohjeet.
Muista jälleen, että yllä oleva kuvaus koskee optimoimattomia koodia.
Vastaus
Kerro kääntäjälle, että et ole ottanut muuttujan osoitetta ja kääntäjä voi sitten näennäisesti luoda muut optimoinnit. Sikäli kuin tiedän, nykyaikaiset kääntäjät pystyvät melko selvittämään, voidaanko muuttujaa pitää / pitää pitää rekisterissä vai ei.
Esimerkki:
int main(){ int* ptr; int a; register int b; ptr = &a; ptr = &b; //this won"t compile return 0; }
Kommentit
- Hylkää tai ota osoite?
- @detly: olet tietysti oikeassa
vastaus
16-bittisillä tietokonepäivillä tarvittiin usein useita rekistereitä 32-bittisten kertojen ja jakojen suorittamiseksi. liukulukuyksiköt liitettiin siruihin ja sitten 64-bittiset arkkitehtuurit ”ottivat haltuunsa”, sekä rekisterien leveys että niiden lukumäärä kasvoivat. Tämä johtaa lopulta CPU: n täydelliseen uudelleenarkkitehtuuriin. Katso Rekisteröi tiedostot Wikipediassa.
Lyhyesti sanottuna, vie vähän aikaa kuvaamiseen kerro mitä tapahtuu, jos käytät 64-bittistä X86- tai ARM-sirua.Jos käytät 16-bittistä sulautettua suorittinta, se saattaa todella saada sinulle jotain. Useimmat pienet upotetut sirut eivät kuitenkaan käytä mitään kriittistä aikaa – mikroaaltouuni saattaa ottaa näytteitä kosketuslevyltäsi 10000 kertaa sekunnissa – mikään ei rasita 4 MHz CPU.
Kommentit
- 4 MIPS / 10000 kyselyä / s = 400 ohjeet / kysely. Että ’ ei ole läheskään niin paljon marginaalia kuin haluat ’. Huomaa myös, että muutama 4 MHz: n prosessori oli mikrokoodattu sisäisesti, mikä tarkoittaa, että ne eivät olleet läheskään 1 MIP / MHz.
- @ JohnR.Strohm – Saattaa olla tilanteita, joissa voidaan perustella tarkalleen kuinka monta käskyä syklii sen ’ kestää, mutta usein halvempi tie on nyt hankkia nopeampi siru ja viedä tuote ulos ovesta. Annetussa esimerkissä tietysti ei ’ tarvitse jatkaa näytteenottoa 10 000: lla, jos jollakin on komento – se ei välttämättä jatka näytteenottoa neljänneksen sekunnin ajan vahingoittamatta tehty. On yhä vaikeampaa selvittää, mihin ohjelmoijan suunnittelemalla optimoinnilla on merkitystä.
- Ei ole aina mahdollista ” saada vain nopeampi siru ja saada tuote ulos ovesta ”. Harkitse reaaliaikaista kuvankäsittelyä. 640×480 pikseliä / kehys x 60 kuvaa / sekunti x N ohjeet pikseliä kohti lisää nopeasti. (Reaaliaikaisen kuvankäsittelyn opetus on, että hikoilet verta pikselien ytimien yli ja sivuutat melkein kaiken muun, koska se toimii kerran riviä kohti tai kerran korjaustiedostona tai kerran kehyksessä, toisin kuin satoja kertoja riviä kohden tai laastari tai kymmeniä tai satoja tuhansia kertoja kehystä kohti.)
- @ JohnR.Strohm – ottaessani esimerkin reaaliaikaisesta kuvankäsittelystä, oletan, että vähimmäisympäristö on 32 bittiä. Raajaan meneminen (koska en tiedä ’ en tiedä kuinka käytännöllistä tämä on), monet siruihin rakennetut grafiikkakiihdyttimet saattavat olla käyttökelpoisia myös kuvan tunnistamiseen, joten ARM-sirut (esimerkiksi ) joissa on integroidut renderointimoottorit, voi olla muita ALU: ita, joita voidaan käyttää tunnistamiseen. Siihen mennessä ’ -rekisterin ’ avainsanan käyttö optimointiin on pieni osa ongelmaa.
Vastaa
Sen selvittämiseksi, onko rekisteriavaimella merkitsevää, pieniä esimerkkikoodeja ei tehdä. Tässä on c-koodi mikä viittaa minulle, että rekisteri-avainsanalla on edelleen merkitystä. Mutta se voi olla erilainen GCC: n kanssa Linuxissa, en tiedä. TULOSKO rekisteri int k & l tallennetaan suorittimen rekisteriin vai ei? Linux-käyttäjien (erityisesti) tulisi kääntyä GCC: n ja optimoinnin avulla. Borland bcc32: n kanssa rekisterin avainsana näyttää toimivan (tässä esimerkissä), koska & -operaattori antaa virhekoodit rekisterin ilmoitetuille kokonaisluvuille. HUOMAUTUS! Näin ei ole pienessä esimerkissä Borlandin kanssa Windowsissa! Jotta näet todella, mitä kääntäjä optimoi vai ei, sen on oltava enemmän kuin pieni esimerkki. Tyhjät silmukat eivät tue! Siitä huolimatta – JOS osoite voidaan lukea & -operaattorilla, muuttujaa ei tallenneta CPU-rekisteriin. Mutta jos rekisterin ilmoitettua muuttujaa ei voida lukea (aiheuttaen virhekoodin kokoamisessa) – minun on oletettava, että rekisterihakusana laittaa muuttujan CPU-rekisteriin. Se voi olla erilainen eri alustoilla, en tiedä . (Jos se toimii, ”punkkien” määrä on paljon pienempi rekisterirekisteröinnin yhteydessä.
/* 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; }
Kommentit
- Jako, jonka yläpuolella on nolla, vaihda {tmp + = ii / jj;} arvoon {tmp + = jj / ii;} – todella pahoillani tästä
- Anna myös k: n ja i: n aloita yhdellä – ei nollalla. Hyvin pahoillani.
- Voit muokata vastaustasi kirjoittamatta korjauksia kommentteihin.