Je travaillais sur un projet personnel récemment lorsque je suis tombé sur un problème étrange.
Dans une boucle très serrée, j'ai un entier avec une valeur comprise entre 0 et 15. J'ai besoin d'obtenir -1 pour les valeurs 0, 1, 8 et 9 et 1 pour les valeurs 4, 5, 12 et 13.
Je me suis tourné vers godbolt pour vérifier quelques options et j'ai été surpris de constater que le compilateur ne pouvait pas optimiser une instruction switch de la même manière qu'une chaîne if.
Le lien est ici: https://godbolt.org/z/WYVBFl
Le code est:
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;
}
}
J'aurais pensé que b et c produiraient les mêmes résultats, et j'espérais pouvoir lire les bit-hacks pour trouver une implémentation efficace moi-même puisque ma solution (l'instruction switch - sous une autre forme) était assez lente.
Curieusement, b
compilé en bit-hacks alors qu'il c
était à peu près non optimisé ou réduit à un cas différent en a
fonction du matériel cible.
Quelqu'un peut-il expliquer pourquoi il y a cet écart? Quelle est la manière «correcte» d'optimiser cette requête?
ÉDITER:
Clarification
Je veux que la solution de commutation soit la plus rapide ou une solution similaire "propre". Cependant, lorsqu'elle est compilée avec des optimisations sur ma machine, la solution if est beaucoup plus rapide.
J'ai écrit un programme rapide pour démontrer et TIO a les mêmes résultats que je trouve localement: Essayez-le en ligne!
Avec static inline
la table de recherche accélère un peu: essayez-le en ligne!
if
bat toujours switch
(la recherche étrange devient encore plus rapide) [TIO à suivre]
-O3
, et il a compiléc
en quelque chose de probablement pire quea
oub
(c
avait deux sauts conditionnels plus quelques manipulations de bits, contre un seul saut conditionnel et une manipulation de bits plus simple pourb
), mais toujours mieux que des tests élément par élément. Je ne sais pas vraiment ce que vous demandez ici; le simple fait est qu'un compilateur d'optimisation peut transformer n'importe lequel de ces éléments en l' un des autres s'il le souhaite, et il n'y a pas de règles strictes pour ce qu'il fera ou ne fera pas.