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:

  1. A variável 'a' será armazenada em um registro para que não seja acessado repetidamente a partir de Ram ou Chache?
  2. 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?
  3. Quão eficiente é o elenco do tipo ponteiro? Meu palpite é que deve ser ineficiente?

Qualquer ajuda seria apreciada.

Cumprimentos

Foi útil?

Solução

  1. 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 saia a Em um registro, mas nunca vi um compilador fazer isso, e não tenho certeza de que acabaria sendo mais rápido).
  2. arr (o próprio ponteiro) é armazenado em um registro (%edi, em AMD64). o Conteúdo da matriz estão na memória.
  3. 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 é).

  1. 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.

  2. É 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.

  3. 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).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top