Quando la parola chiave del registro è effettivamente utile in C?

11

Sono confuso sull'uso della parola chiave register in C. Si dice generalmente che il suo uso non è necessario come in questa domanda su stackoverflow .

Questa parola chiave è totalmente ridondante in C a causa dei moderni compilatori o ci sono situazioni in cui può essere ancora utile? In caso affermativo, quali sono alcune situazioni in cui l'utilizzo della parola chiave register è effettivamente utile?

    
posta Aseem Bansal 29.07.2013 - 13:04
fonte

5 risposte

11

Non è ridondante in termini di linguaggio, è solo che usandolo, stai dicendo al compilatore, "preferiresti" avere una variabile memorizzata nel registro. Tuttavia, è assolutamente garantito che ciò avvenga effettivamente durante il runtime.

    
risposta data 29.07.2013 - 13:36
fonte
8

Come già accennato, gli ottimizzatori del compilatore essenzialmente rendono obsoleta la parola chiave register per scopi diversi dalla prevenzione dell'aliasing. Tuttavia, ci sono interi codebase che sono stati compilati con l'ottimizzazione disattivata ( -O0 in gcc-speak ). Per tale codice, la parola chiave register può avere un grande effetto. Nello specifico, le variabili che altrimenti otterrebbero uno slot nello stack (ad esempio tutti i parametri di funzione e le variabili automatiche) possono essere inserite direttamente in un registro se dichiarate con la parola chiave register .

Ecco un esempio del mondo reale: supponiamo che si sia verificato qualche recupero nel database e che il codice di recupero abbia riempito la tupla recuperata in una struttura C. Inoltre, supponiamo che alcuni sottoinsiemi di questa struttura C debbano essere copiati in un'altra struttura, forse questa seconda struttura è un record di cache che rappresenta i metadati memorizzati nel database che, a causa di vincoli di memoria, memorizza solo un sottoinsieme di ogni record di metadati come memorizzato nel database.

Data una funzione che accetta un puntatore a ogni tipo di struct e il cui unico compito è copiare alcuni membri dalla struct iniziale alla seconda struct: le variabili del puntatore struct vivranno nello stack. Poiché gli incarichi si verificano dai membri di una struttura all'altra, gli indirizzi della struttura verranno caricati in un registro per ciascun compito al fine di consentire l'accesso ai membri della struttura che vengono copiati. Se i puntatori della struttura dovevano essere dichiarati con la parola chiave register , gli indirizzi delle strutture rimarrebbero nei registri, eliminando efficacemente le istruzioni di caricamento-indirizzo-registro per ciascun compito.

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

    
risposta data 30.07.2014 - 06:07
fonte
5

In pratica dici al compilatore che non prenderai l'indirizzo della variabile e che il compilatore potrà quindi fare ulteriori ottimizzazioni. Per quanto ne so, i compilatori moderni sono in grado di determinare se una variabile può / deve essere tenuta in un registro o meno.

Esempio:

int main(){
        int* ptr;
        int a;
        register int b;
        ptr = &a;
        ptr = &b; //this won't compile
        return 0;
} 
    
risposta data 29.07.2013 - 14:17
fonte
0

Nei giorni di computer a 16 bit, uno spesso aveva bisogno di più registri per eseguire moltiplicazioni e divisioni a 32 bit. Dato che le unità in virgola mobile sono state incorporate nei chip e quindi le architetture a 64 bit hanno "assunto", sia la larghezza dei registri che il loro numero sono stati espansi. Questo alla fine porta a una completa riprogettazione della CPU. Vedi Registrati file su Wikipedia.

In breve, ti ci vorrà un po 'di tempo per capire cosa sta succedendo se sei su un chip X86 o ARM a 64-bit. Se sei su una CPU embedded a 16 bit, questo potrebbe davvero farti avere qualcosa. Tuttavia, la maggior parte dei chip integrati di piccole dimensioni non esegue alcun fattore critico di tempo: il tuo forno a microonde potrebbe campionare il tuo touchpad 10.000 volte al secondo - niente che metta a dura prova una CPU da 4Mhz.

    
risposta data 29.07.2013 - 17:39
fonte
-3

Per stabilire se la parola chiave del registro ha qualche significanza, i codici di esempio minuscoli non lo faranno. Ecco un codice c che mi suggerisce, la parola chiave del registro ha ancora un significato. Ma potrebbe essere diverso con GCC su Linux, non lo so. REGISTRERA 'int k & Posso essere memorizzati in un registro della CPU o no? Gli utenti Linux (in particolare) dovrebbero compilare GCC e l'ottimizzazione. Con Borland bcc32 la parola chiave register sembra funzionare (in questo esempio), poiché il & -operator fornisce i codici di errore per gli interi dichiarati dal registro. NOTA! Questo NON è il caso con un piccolo esempio con Borland su Windows! Per vedere veramente cosa ottimizza o meno il compilatore, deve essere un esempio più che minuscolo. I loop vuoti non lo faranno! Tuttavia - SE un indirizzo può essere letto con il & -operator, la variabile non è memorizzata in un registro CPU. Ma se una variabile dichiarata dal registro non può essere letta (causando il codice di errore nella compilazione) - Devo presumere che la parola chiave del registro inserisca effettivamente 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 del 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;

} 
    
risposta data 14.02.2018 - 04:43
fonte

Leggi altre domande sui tag