Análise do código C
-
27-09-2019 - |
Pergunta
Aqui está a função que estou escrevendo em uma máquina Linux de 64 bits.
void myfunc(unsigned char* arr) //array of 8 bytes is passed by reference
{
unsigned long a = 0; //8 bytes
unsigned char* LL = (unsigned char*) &a;
LL[0] = arr[6];
LL[1] = arr[3];
LL[2] = arr[1];
LL[3] = arr[7];
LL[4] = arr[5];
LL[5] = arr[4];
LL[6] = arr[0];
LL[7] = arr[2];
}
Agora minhas perguntas são:
- A variável 'a' será armazenada em um registro para que não seja acessado repetidamente a partir de Ram ou Chache?
- Trabalhando na arquitetura de 64 bits, devo assumir que a matriz 'ARR' será armazenada em um registro, pois os parâmetros de funções são armazenados em um registro em arco de 64 bits?
- Quão eficiente é o elenco do tipo ponteiro? Meu palpite é que deve ser ineficiente?
Qualquer ajuda seria apreciada.
Cumprimentos
Solução
a
Não pode ser armazenado em um registro, pois você recebeu seu endereço. (Valdo aponta corretamente que um compilador realmente inteligente poderia Otimize a matriz acessos em operações de bit e saiaa
Em um registro, mas nunca vi um compilador fazer isso, e não tenho certeza de que acabaria sendo mais rápido).arr
(o próprio ponteiro) é armazenado em um registro (%edi
, em AMD64). o Conteúdo da matriz estão na memória.- Fundição do tipo de ponteiro por si próprio geralmente gera nenhum código. No entanto, fazer coisas tolas com elencos de tipo pode levar a um código muito ineficiente, ou mesmo para codificar cujo comportamento é indefinido.
Parece que você está tentando permitir os bytes em uma matriz e depois empurrá -los em um número, e o código da máquina que seu exemplo gera não é muito ruim para isso. A sugestão de David para usar as operações de mudança e máscara é boa (isso também evitará problemas se o seu código precisar correr em uma máquina grande e-endiana), e também existem instruções de permuta do vetor SSE, mas ouvi dizer que são gentis de uma dor de usar.
Aliás, você deve fazer com que o tipo de retorno da sua função de exemplo seja unsigned long
e colocar return a;
no final; Então você pode usar gcc -O2 -S
E veja exatamente o que você obtém da compilação. Sem a mudança para retornar a
, O GCC otimizará alegremente todo o corpo da função, pois não tem efeitos colaterais visíveis externamente.
Outras dicas
Você pode fazer melhor usar as instruções explícitas de turno e mascarar para conseguir isso, em vez de usar a indexação da matriz.
As operações da matriz dificultarão o uso do compilador, porque normalmente não há instruções que façam coisas como "carregar 8 bits do terceiro byte do registro A". (Um compilador de otimização poderia Descubra que é possível fazer isso com turnos/máscaras, mas não tenho certeza de quão provável é).
A pergunta sobre se a variável
a
Será armazenado no registro é uma questão de otimização. Já que não hávolatile
Modificador IMHO Um compilador inteligente fará isso.É uma questão da convenção de chamada. Se por convenção um único parâmetro de ponteiro for transferido em um registro - então será
arr
.A fundição do tipo de ponteiro não é uma operação que a CPU interpreta. Não há código gerado para isso. Apenas as informações para o compilador sobre o que você quer dizer.
(Na verdade, às vezes, a fundição produz código extra, mas isso está relacionado à herança múltipla e ao polimorfismo)
Depende do seu nível de otimização. Você pode examinar a montagem para responder às suas perguntas. Com o GCC, use o sinalizador "-s".
gcc -S -O0 -o /tmp/xx-O0.s /tmp/xx.c
gcc -S -O3 -o /tmp/xx-O3.s /tmp/xx.c
A montagem gerada é completamente diferente. (Certifique -se de fazer o return a;
mudança sugerida por Zack.)
Veja também esta mensagem Para obter dicas sobre como gerar uma listagem de C/montagem mista (que rapidamente se torna inútil com otimização).