Eu estava trabalhando recentemente em um projeto pessoal quando me deparei com uma questão estranha.
Em um loop muito fechado, tenho um número inteiro com um valor entre 0 e 15. Preciso obter -1 para os valores 0, 1, 8 e 9 e 1 e para os valores 4, 5, 12 e 13.
Eu me virei para o godbolt para verificar algumas opções e fiquei surpreso ao parecer que o compilador não poderia otimizar uma declaração de switch da mesma maneira que uma cadeia if.
O link está aqui: https://godbolt.org/z/WYVBFl
O código é:
const int lookup[16] = {-1, -1, 0, 0, 1, 1, 0, 0, -1, -1, 0, 0, 1, 1, 0, 0};
int a(int num) {
return lookup[num & 0xF];
}
int b(int num) {
num &= 0xF;
if (num == 0 || num == 1 || num == 8 || num == 9)
return -1;
if (num == 4 || num == 5 || num == 12 || num == 13)
return 1;
return 0;
}
int c(int num) {
num &= 0xF;
switch (num) {
case 0: case 1: case 8: case 9:
return -1;
case 4: case 5: case 12: case 13:
return 1;
default:
return 0;
}
}
Eu pensaria que bec produziriam os mesmos resultados, e esperava poder ler os bit-hacks para obter uma implementação eficiente, já que minha solução (a instrução switch - de outra forma) era bastante lenta.
Estranhamente, b
compilado para hacks de bits, enquanto c
praticamente não era otimizado ou reduzido a um caso diferente de a
depender do hardware de destino.
Alguém pode explicar por que existe essa discrepância? Qual é a maneira 'correta' de otimizar esta consulta?
EDITAR:
Esclarecimento
Eu quero a solução chave para ser o mais rápido, ou uma solução semelhante "limpa". No entanto, quando compilado com otimizações na minha máquina, a solução if é significativamente mais rápida.
Eu escrevi um programa rápido para demonstrar e o TIO tem os mesmos resultados que encontro localmente: Experimente online!
Com static inline
a tabela de pesquisa acelera um pouco: Experimente online!
if
ainda bate switch
(estranhamente pesquisa torna-se ainda mais rápido) [TIO seguir]
-O3
, e ele compilouc
para algo provavelmente pior quea
oub
(c
teve dois saltos condicionais mais algumas manipulações de bits, versus apenas um salto condicional e manipulação de bits mais simplesb
), mas ainda assim item melhor do que ingênuo por testes de itens. Não tenho certeza do que você realmente está pedindo aqui; o simples fato é que um compilador otimizador pode transformar qualquer um desses itens em qualquer um dos outros, se assim o desejar, e não há regras rígidas para o que ele fará ou não fará.