Uso de switch en programación

Orange switches artwork. Valentin Ruhry.
¿Para qué sirve la sentencia switch en C/C++ o Java? ¿Por qué los profesores insisten en que lo usemos, porque es más bonito? ¡Pues sí! No olvides que un código claro favorece una lectura más comprensible.
Pero no somos nosotros los únicos que nos beneficiaremos: nuestro programa puede ser más eficiente. Recuerda que cuantas más y mejores pistas demos al compilador, mejor código podrá generar.
Es cierto que un switch tiene el mismo diagrama de flujo que un if-else, pero es más estricto respecto a la condición. En primer lugar sólo admite expresiones constantes, y en segundo estamos usando etiquetas, con lo que se lo estamos poniendo muy fácil al compilador para que genere una tabla de functores.

Precaución: no utilices etiquetas y sentencias goto en C/C++.

Por ejemplo, supongamos este sencillo código en C:
if      (x == 1) a();
else if (x == 2) b();
else if (x == 3) c();
else if (x == 5) d();
else             error();
Es muy fácil traducirlo a una sentencia switch:
switch (x) {
case 1: a(); break;
case 2: b(); break;
case 3: c(); break;
case 5: d(); break;
default: error();
}
Pero, ¿qué hará el compilador? ¿Lo mismo que si hubiéramos escrito lo primero? No, si puede evitarlo. Los valores constantes son muy próximos entre sí, y evaluar todas las condiciones una a una sería muy costoso. La idea es equivalente al siguiente código, que emplea un vector de punteros a función (nótese que debemos tener en cuenta los casos 0 y 4):
void (*funciones[])() = { error, a, b, c, error, d };

if (x > 5)
    error();
else
    funciones[x]();
De esta forma con una sola condición (el default) y una suma saltaremos directamente a la función correcta.

Consejo: Tanto si utilizas if-else como switch, escribe siempre primero aquellas condiciones que tegan mayor probabilidad de acertar. En el primer caso, o si switch no se puede optimizar, evitaremos hacer demasiadas comparaciones. Si switch se puede optimizar, el compilador ordenará todas las condiciones.

Experimento

Hemos creado una sentencia if-else con 1.000 condiciones, su orden switch y su vector de functores equivalentes, en un archivo de sólo 10.025 líneas de código. No lo hemos escrito a mano, nos hemos servido de un programita en Python para generarlo. Lo hemos compilado con GNU CC, hemos ejecutado 108 pruebas, y estos son los resultados:

Procesador: Intel Core i7 @ 2,2 GHz
Compilador: g++ 4.8.1 / -Ofast
S.O.: Ubuntu 13.10 x86_64

El código fuente está disponible en este enlace:

Avanzado

Cabe mencionar que Microsoft Visual C++ es capaz de optimizar algunas estructuras if-else como la de arriba, de igual forma que si se hubiera escrito con switch, aunque no es capaz de compilar un programa tan complejo como el anterior, hay que bajar de 1.000 a 500 condiciones. Aquí os dejo una versión simplificada de la salida en ensamblador del compilador GCC con el código switch de arriba (véase el comentario en las instrucciones de salto):
    movl x, %eax
    cmpl $5, %eax
    ja DEF     ; if (x > 5) goto DEF
    
    movl BASE(,%eax,4), %eax 
    jmp *%eax  ; goto (BASE + x)

BASE:
    .long DEF
    .long L1
    .long L2
    .long L3
    .long DEF
    .long L5<
L1:
    call a
    jmp END
.L2:
    call b
    jmp END
.L3:
    call c
    jmp END
.L5:
    call d
    jmp END
DEF:
    call error

Comentarios

Entradas populares de este blog

Algoritmo de relleno

Problema de las N reinas

Cifrado de Vernam