Quando la parola chiave register è effettivamente utile in C?

Sono confuso sulluso della parola chiave register in C. Generalmente si dice che il suo utilizzo non è ” t necessaria come in questa domanda su stackoverflow .

Questa parola chiave è totalmente ridondante in C a causa dei compilatori moderni o ci sono situazioni in cui può ancora essere utile? Se sì, quali sono alcune situazioni in cui luso della parola chiave register è effettivamente utile?

Commenti

  • Penso che la domanda collegata e le risposte ad essa siano le stesse che puoi aspettarti qui. Quindi non ci saranno nuove informazioni che puoi ottenere qui.
  • @UwePlonus Ho pensato che il lo stesso della const parola chiave ma questa domanda ha dimostrato che mi sbagliavo. Quindi ‘ Aspetterò e vedrò cosa ottengo.
  • Penso che la parola chiave const sia qualcosa di diverso rispetto a register.
  • It ‘ è utile se si torna indietro nel tempo accidentalmente e si è costretti a utilizzare uno dei primi compilatori C. A parte questo ‘ non è affatto utile, ‘ è completamente obsoleto da anni.
  • @ UwePlonus Volevo solo dire che potrebbero esserci scenari a me sconosciuti in cui una parola chiave potrebbe essere utile.

Risposta

Non è ridondante in termini di linguaggio, è solo che usandolo, stai dicendo al compilatore, che “preferiresti” avere una variabile memorizzata nel registro. Tuttavia non cè assolutamente nessuna garanzia che questo verrà effettivamente accade durante il runtime.

Commenti

  • Inoltre, ‘ è quasi sempre il caso che il compilatore lo sa meglio e tu ‘ stai sprecando il fiato
  • @jozefg: anche peggio. Corri il rischio che il compilatore rispetti la tua richiesta / suggerimento e produca peggio codice di conseguenza.

Answer

Come già accennato, essenzialmente gli ottimizzatori del compilatore ren der la parola chiave register obsoleta per scopi diversi dallimpedire laliasing. Tuttavia, ci sono intere basi di codice compilate con lottimizzazione disattivata (-O0 in gcc-speak ). Per tale codice, la parola chiave register può avere un grande effetto. In particolare, le variabili che altrimenti avrebbero uno slot nello stack (cioè tutti i parametri di funzione e le variabili automatiche) possono essere inserite direttamente in un registro se dichiarate con register parola chiave.

Qui “un esempio del mondo reale: supponi che si sia verificato un recupero del database e che il codice di recupero abbia inserito la tupla recuperata in una struttura C. Inoltre, supponi che qualche sottoinsieme di questa struttura C deve essere copiato in unaltra struttura: forse questa seconda struttura è un record della cache che rappresenta i metadati archiviati nel database che, a causa dei vincoli di memoria, memorizza nella cache solo un sottoinsieme di ciascun record di metadati come archiviato nel database.

Data una funzione che accetta un puntatore a ogni tipo di struttura e il cui unico compito è copiare alcuni membri dalla struttura iniziale alla seconda struttura: le variabili del puntatore a struttura vivranno sullo stack. Poiché gli assegnamenti avvengono dai membri di una struttura agli altri, gli indirizzi della struttura, per ogni assegnazione, essere caricato in un registro per eseguire laccesso dei membri della struttura che vengono copiati. Se i puntatori della struttura dovessero essere dichiarati con la parola chiave register, gli indirizzi delle strutture rimarrebbero nei registri, eliminando di fatto le istruzioni di caricamento dellindirizzo nel registro per ogni assegnazione.

Ancora una volta, ricorda che la descrizione sopra si applica al codice non ottimizzato .

Risposta

Fondamentalmente dici al compilatore che non prendi lindirizzo della variabile e il compilatore può quindi apparentemente fare ulteriori ottimizzazioni. Per quanto ne so, i compilatori moderni sono abbastanza in grado di determinare se una variabile può / deve essere tenuta in un registro oppure no.

Esempio:

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

Commenti

  • Dereferenziare o prendere lindirizzo di?
  • @detly: hai ovviamente ragione

Risposta

Ai tempi dei computer a 16 bit, spesso erano necessari più registri per eseguire moltiplicazioni e divisioni a 32 bit. unità a virgola mobile sono state incorporate nei chip e quindi le architetture a 64 bit “hanno preso il sopravvento”, sia la larghezza dei registri che il numero di essi sono stati ampliati. Questo alla fine porta a una completa ri-architettura della CPU. Vedere Registra file su Wikipedia.

In breve, ci vorrebbe un po di tempo per figu Scopri cosa sta succedendo se utilizzi un chip X86 o ARM a 64 bit.Se utilizzi una CPU incorporata a 16 bit, questo potrebbe effettivamente darti qualcosa. Tuttavia, la maggior parte dei chip incorporati di piccole dimensioni non esegue nulla di critico in termini di tempo: il tuo forno a microonde potrebbe campionare il tuo touchpad 10.000 volte al secondo, niente che affatica CPU da 4 Mhz.

Commenti

  • 4 MIPS / 10.000 polls / sec = 400 istruzioni / poll. Questo ‘ non è tanto margine quanto ‘ vorresti avere. Si noti inoltre che alcuni processori da 4 MHz erano microcodificati internamente, il che significa che non erano neanche lontanamente vicini a 1 MIP / MHz.
  • @ JohnR.Strohm – Potrebbero esserci situazioni in cui si potrebbe giustificare il calcolo di quante istruzioni esattamente cicli che ‘ prenderà, ma spesso il modo più economico ora è quello di ottenere un chip più veloce e portare il prodotto fuori dalla porta. Nellesempio dato, ovviamente, non è necessario ‘ continuare a campionare a 10.000 se si ha un comando: potrebbe non riprendere il campionamento per un quarto di secondo senza danni fatto. Sta diventando sempre più difficile capire dove sarà importante lottimizzazione diretta dal programmatore.
  • Non è sempre possibile ” ottenere un chip più veloce e ottenere il prodotto fuori dalla porta “. Considera lelaborazione delle immagini in tempo reale. 640×480 pixel / fotogramma x 60 fotogrammi / secondo x N istruzioni per pixel si aggiungono rapidamente. (La lezione dallelaborazione delle immagini in tempo reale è che si suda sangue sui kernel pixel e si ignora quasi tutto il resto, perché viene eseguito una volta per riga o una volta per patch o una volta per frame, invece di centinaia di volte per riga o patch o decine o centinaia di migliaia di volte per frame.)
  • @ JohnR.Strohm – prendendo lesempio di elaborazione delle immagini in tempo reale, presumo che lambiente minimo sia di 32 bit. Uscire su un arto (perché non ‘ per sapere quanto sia pratico farlo) molti acceleratori grafici incorporati nei chip potrebbero anche essere utilizzabili per il riconoscimento delle immagini, quindi i chip ARM (ad esempio ) che hanno motori di rendering integrati possono avere ALU aggiuntive utilizzabili per il riconoscimento. A quel punto, luso della parola chiave ‘ register ‘ per lottimizzazione è una piccola parte del problema.

Risposta

Per stabilire se la parola chiave register ha un significato, minuscoli codici di esempio non lo faranno. Ecco un c-code il che mi suggerisce che la parola chiave register ha ancora un significato, ma potrebbe essere diversa con GCC su Linux, non lo so. VERRÀ registrato int k & Sarò memorizzato in un registro della CPU o no? Gli utenti Linux (soprattutto) dovrebbero compilare con GCC e ottimizzazione. Con Borland bcc32, la parola chiave register sembra funzionare (in questo esempio), poiché loperatore & fornisce codici di errore per gli interi dichiarati dal registro. NOTA! Questo NON è il caso di un piccolo esempio con Borland su Windows! Per vedere davvero cosa ottimizza o meno il compilatore, deve essere un esempio più che minuscolo. Ciononostante, SE un indirizzo PU essere letto con loperatore &, la variabile non è memorizzata in un registro della CPU. Ma se una variabile dichiarata dal registro non può essere letta (causando un codice di errore alla compilazione), devo presumere che la parola chiave register effettivamente metta la variabile in un registro della CPU. Potrebbe differire su varie piattaforme, non lo so . (Se funziona, il numero di “tick” sarà molto più basso con la dichiarazione di registro.

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

Commenti

  • Ci sarà una divisione con lo zero sopra, per favore cambia {tmp + = ii / jj;} in {tmp + = jj / ii;} – veramente dispiaciuto per questo
  • Lascia anche k e i inizia con 1 – non zero. Mi dispiace molto.
  • Puoi modificare la tua risposta invece di scrivere correzioni nei commenti.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *