Estaba trabajando en un proyecto personal recientemente cuando me topé con un problema extraño.
En un ciclo muy cerrado, tengo un número entero con un valor entre 0 y 15. Necesito obtener -1 para los valores 0, 1, 8 y 9 y 1 para los valores 4, 5, 12 y 13.
Me volví a Godbolt para verificar algunas opciones y me sorprendió que pareciera que el compilador no podía optimizar una declaración de cambio de la misma manera que una cadena if.
El enlace está aquí: https://godbolt.org/z/WYVBFl
El codigo es:
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;
}
}
Pensé que byc produciría los mismos resultados, y esperaba poder leer los trucos de bits para obtener una implementación eficiente yo mismo, ya que mi solución (la declaración de cambio, en otra forma) fue bastante lenta.
Curiosamente, se b
compiló en bit-hacks mientras no c
estaba optimizado o se redujo a un caso diferente a
dependiendo del hardware de destino.
¿Alguien puede explicar por qué existe esta discrepancia? ¿Cuál es la forma 'correcta' de optimizar esta consulta?
EDITAR:
Aclaración
Yo quiero la solución cambie a ser el más rápido, o una solución similar "limpia". Sin embargo, cuando se compila con optimizaciones en mi máquina, la solución if es significativamente más rápida.
Escribí un programa rápido para demostrar y TIO tiene los mismos resultados que encuentro localmente: ¡ Pruébelo en línea!
Con static inline
la tabla de búsqueda se acelera un poco: ¡ Pruébelo en línea!
if
aún late switch
(la búsqueda extraña se vuelve aún más rápida) [TIO a seguir]
-O3
, y compilóc
algo probablemente peora
ob
(c
tenía dos saltos condicionales más algunas manipulaciones de bits, frente a un solo salto condicional y una manipulación de bits más simpleb
), pero aún así mejor que las pruebas ingenuas ítem por ítem. No estoy seguro de lo que realmente estás pidiendo aquí; El simple hecho es que un compilador optimizador puede convertir cualquiera de estos en cualquiera de los otros si así lo desea, y no hay reglas estrictas para lo que hará o no hará.