Quando a palavra-chave register é realmente útil em C?

Estou confuso sobre o uso da palavra-chave register em C. Geralmente é dito que seu uso isn ” t necessário como nesta pergunta sobre stackoverflow .

Esta palavra-chave é totalmente redundante em C devido aos compiladores modernos ou há situações em que ainda pode ser útil? Se sim, quais são algumas situações nas quais o uso da palavra-chave register é realmente útil?

Comentários

  • Acho que a pergunta vinculada e as respostas são as mesmas que você pode esperar aqui. Portanto, não haverá nenhuma informação nova que você possa obter aqui.
  • @UwePlonus Eu pensei que o mesmo sobre const palavra-chave, mas esta pergunta provou que eu estava errado. Então, ‘ vou esperar para ver o que recebo.
  • Acho que a const palavra-chave é algo diferente do registro.
  • É ‘ é útil se você acidentalmente voltar no tempo e for forçado a usar um dos primeiros compiladores C. Fora isso, ‘ não é útil, ‘ está totalmente obsoleto há anos.
  • @ UwePlonus Só quis dizer que pode haver cenários desconhecidos para mim em que uma palavra-chave pode ser útil.

Resposta

Não é redundante em termos de linguagem, é apenas que, ao usá-lo, você está dizendo ao compilador que “prefere” ter uma variável armazenada no registrador. No entanto, há absolutamente nenhuma garantia de que isso realmente acontecer durante o tempo de execução.

Comentários

  • Mais do que isso, ‘ é quase sempre o caso de o compilador sabe melhor e você ‘ está perdendo seu fôlego
  • @jozefg: ainda pior. Você corre o risco de que o compilador honre sua solicitação / sugestão e produza pior .

Resposta

Como já mencionado, otimizadores de compilador essencialmente ren der a register palavra-chave obsoleta para outros fins que não a prevenção de apelidos. No entanto, existem bases de código inteiras que são compiladas com a otimização desativada (-O0 em gcc-speak ). Para esse código, a palavra-chave register pode ter um grande efeito. Especificamente, as variáveis que, de outra forma, teriam um slot na pilha (ou seja, todos os parâmetros de função e variáveis automáticas) podem ser colocadas diretamente em um registro se declaradas com register palavra-chave.

Aqui está um exemplo do mundo real: suponha que alguma recuperação de banco de dados tenha ocorrido e que o código de recuperação tenha colocado a tupla recuperada em uma estrutura C. Além disso, suponha que algum subconjunto dessa estrutura C precisa ser copiado para outro struct – talvez este segundo struct seja um registro de cache que representa os metadados armazenados no banco de dados que, devido às restrições de memória, armazena em cache apenas um subconjunto de cada registro de metadados armazenado no banco de dados.

Dada uma função que leva um ponteiro para cada tipo de estrutura e cujo único trabalho é copiar alguns membros da estrutura inicial para a segunda estrutura: as variáveis de ponteiro de estrutura viverão na pilha. Conforme as atribuições ocorrem a partir de membros de uma estrutura para os outros “s, os endereços da estrutura irão, para cada atribuição, ser carregado em um registrador para realizar o acesso dos membros da estrutura que estão sendo copiados. Se os ponteiros da estrutura fossem declarados com a palavra-chave register, os endereços das estruturas permaneceriam nos registradores, eliminando efetivamente as instruções de carregamento do endereço no registro para cada atribuição.

Novamente, lembre-se de que a descrição acima se aplica ao código não otimizado .

Resposta

Basicamente, você diz ao compilador que não pegará o endereço da variável e o compilador poderá fazer ostensivamente otimizações adicionais. Pelo que eu sei, os compiladores modernos são bastante capazes de determinar se uma variável pode / deve ser mantida em um registro ou não.

Exemplo:

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

Comentários

  • Cancele a referência ou pegue o endereço de?
  • @detly: você está certo, é claro

Resposta

Nos dias de computador de 16 bits, muitas vezes eram necessários vários registros para executar multiplicações e divisões de 32 bits. unidades de ponto flutuante foram incorporadas aos chips e, em seguida, as arquiteturas de 64 bits “assumiram o controle”, tanto a largura dos registros quanto o número deles expandidos. Isso eventualmente leva a uma reformulação completa da CPU. Consulte Registre arquivos na Wikipedia.

Em suma, levaria um pouco de tempo para você entender Veja o que realmente está acontecendo se você estiver em um chip X86 ou ARM de 64 bits.Se você estiver em uma CPU embutida de 16 bits, isso pode lhe render algo. No entanto, a maioria dos pequenos chips embutidos não executam nada crítico – seu forno de micro-ondas pode amostrar seu touchpad 10.000 vezes por segundo – nada que force um CPU de 4 MHz.

Comentários

  • 4 MIPS / 10.000 pesquisas / s = 400 instruções / pesquisa. Isso ‘ não tem tanta margem quanto você ‘ gostaria de ter. Observe também que alguns processadores de 4 MHz foram microcodificados internamente, o que significa que eles estavam longe de 1 MIP / MHz.
  • @ JohnR.Strohm – Pode haver situações em que alguém poderia justificar descobrir exatamente quantas instruções faz um ciclo ‘ vai demorar, mas muitas vezes a maneira mais barata de sair agora é apenas obter um chip mais rápido e lançar o produto. No exemplo dado, é claro, não é necessário ‘ continuar a amostrar em 10.000 se tiver um comando – pode não retomar a amostragem por um quarto de segundo sem nenhum dano feito. Está se tornando cada vez mais difícil descobrir onde a otimização dirigida pelo programador vai importar.
  • Nem sempre é possível ” apenas obter um chip mais rápido e obter o produto para fora da porta “. Considere o processamento de imagem em tempo real. 640×480 pixels / quadro x 60 quadros / segundo x N instruções por pixel adiciona rapidamente. (A lição do processamento de imagem em tempo real é que você transpira sangue sobre seus núcleos de pixel e quase ignora todo o resto, porque é executado uma vez por linha ou uma vez por patch ou uma vez por quadro, em oposição a centenas de vezes por linha ou patch ou dezenas ou centenas de milhares de vezes por quadro.)
  • @ JohnR.Strohm – tomando o exemplo de processamento de imagem em tempo real, eu presumiria que o ambiente mínimo é de 32 bits. Saindo em um limbo (porque eu não ‘ não sei como isso é prático) muitos aceleradores gráficos embutidos em chips também podem ser usados para reconhecimento de imagem, então chips ARM (por exemplo ) que possuem mecanismos de renderização integrados podem ter ALUs adicionais utilizáveis para reconhecimento. Nessa época, o uso da palavra-chave ‘ registrar ‘ para otimização é uma pequena parte do problema.

Resposta

A fim de estabelecer se a palavra-chave de registro tem algum significado, pequenos códigos de exemplo não servirão. Aqui está um código-c o que me sugere que a palavra-chave register ainda tem um significado. Mas pode ser diferente com o GCC no Linux, não sei. IRÁ registrar int k & l ser armazenado em um registro de CPU ou não? Usuários Linux (especialmente) devem compilar com GCC e otimização. Com o Borland bcc32, a palavra-chave de registro parece funcionar (neste exemplo), pois o operador & fornece códigos de erro para inteiros declarados de registro. NOTA! Este NÃO é o caso de um pequeno exemplo com Borland no Windows! Para realmente ver o que o compilador otimiza ou não, tem que ser mais do que um pequeno exemplo. Loops vazios não funcionam! No entanto – SE um endereço PODE ser lido com o operador &, a variável não é armazenada em um registro da CPU. Mas se uma variável declarada de registro não pode ser lida (causando código de erro na compilação) – eu tenho que presumir que a palavra-chave de registro realmente colocou a variável em um registro de CPU. Pode ser diferente em várias plataformas, eu não sei . (Se funcionar, o número de “tiques” será muito menor com a declaração do 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; } 

Comentários

  • Haverá uma divisão com zero acima, altere {tmp + = ii / jj;} para {tmp + = jj / ii;} – sinto muito por isso
  • Também deixe k e i comece com 1 – não zero. Sinto muito.
  • Você pode editar sua resposta em vez de escrever correções nos comentários.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *