2011-01-30 10 views
5

Tengo una pregunta sobre si se debe usar 'caso' o 'si' en una función que se llama bastante. Aquí está el siguiente como está ahora, en 'ifs'; el código es autoexplicativo:Optimización de código; switch versus if's

int identifyMsg(char* textbuff) { 
if (!strcmp(textbuff,"text")) { 
    return 1; 
} 
if (!strcmp(textbuff,"name")) { 
    return 2; 
} 
if (!strcmp(textbuff,"list")) { 
    return 3; 
} 
if (!strcmp(textbuff,"remv")) { 
    return 4; 
} 
if (!strcmp(textbuff,"ipad")) { 
    return 5; 
} 
if (!strcmp(textbuff,"iprm")) { 
    return 6; 
} 
return 0; 
} 

Mi pregunta es: ¿Un cambio funcionaría mejor? Sé que si utilizo 'ifs', puedo colocar las opciones más probables en la parte superior.

+0

ver esto: http://stackoverflow.com/questions/97987/switch-vs-if-else – Nawaz

+1

Usted debe concentrarse en hacer que su código correcto antes de preocuparse por micro-optimización. Y una vez que es correcto, debes medir antes de optimizar. Y luego, cuando piense que debe optimizar, debe pensar detenidamente sobre la futura capacidad de mantenimiento del código optimizado. En resumen, no tiene sentido tener un código optimizado que sea incorrecto. –

Respuesta

10

No se pueden usar las declaraciones switch para cadenas porque son punteros y no se evalúan en tiempo de compilación. Tiene que utilizar varias instrucciones if.

Sin embargo, en aras del rendimiento, creo que los conmutadores funcionan mejor cuando hay más condiciones para verificar pero la diferencia será tan pequeña que no importaría.

nunca he probado esto antes, sin embargo, pero he leído sobre este tipo de optimización del interruptor:

switch (value) { 
    case frequent_value1: 
    case frequent_value2: 
    case frequent_value3: 
    break; 

default: 
    switch (value) { 
    case infrequent_value1: 
    case infrequent_value2: 
    case infrequent_value3: 
     break; 
    } 
} 
+0

De hecho. Pero aparte de eso: la pregunta "que funciona mejor" sería discutible incluso si fuera posible. – delnan

+0

Otro buen tema en SO: http://stackoverflow.com/questions/97987/switch-vs-if-else – Nawaz

+3

Esos conmutadores anidados no deberían tener ningún impacto positivo en el rendimiento. Si ocurre uno de los casos frecuentes, no se verificará ninguna condición para ninguno de los otros casos de todos modos. Lo mismo ocurre con if-elseif regular. – Thorarin

5

Usted podía uso gperf para generar valores hash perfectas de los "verbos" que desea ver. Entonces podría usar una declaración switch.

O, usted podría hacer algo como esto:

switch (textbuff[0]) 
{ 
    case 'i': 
    { 
     switch (textbuff[1]) 
     { 
      case 'p': 
      { 
       switch (textbuff[2]) 
       { 
        case 'a': /* something. */ break; 
        case 'r': /* something else. */ break; 
       } 
      } 
     } 
    } 

(Usted consigue la idea).

Como otra opción (si todos sus comandos son 4 caracteres), convertirlos en un único número de 32 bits y luego cambiar en eso:

int32_t mashed = 
    textbuff[0] << 24 | 
    textbuff[1] << 16 | 
    textbuff[2] << 8 | 
    textbuff[3]; 

switch (mashed) { /* ... */ } 

Para ser honesto, sin embargo, a menos que la lista de opciones es particularmente grande, o esta función se llama un número obsceno de veces, no valdrá la pena.

Recuerde: medir primero; optimice más tarde (solo si es necesario).

+0

generar hash, luego usar el interruptor no es demasiado? especialmente cuando el OP se enfoca en un rendimiento tan minúsculo? – Nawaz

+0

Puede valer la pena; puede que no. Mida primero, decida si vale la pena optimizar. –

+0

Upvoted it debido a la optimización de purgado a 32 bits :) – riviera

2

Puede usar "enum" para esas cadenas. luego use declaraciones de mayúsculas y minúsculas.

+1

+1 por error: :-) En realidad, eso es lo que está haciendo: convertir una cadena de identificación en una identificación numérica. Si la identificación proviene de datos (por ejemplo, XML), difícilmente puede ser un número, pero nunca una enumeración. –

2

Otra alternativa que he encontrado recientemente, lo que podría o no podría adaptarse a su gusto es:

int identifyMsg(const char* textbuff) { 
    static const struct { const char* str; int id; } pairs[] = { 
     { "text", 1 }, 
     { "name", 2 }, 
     { "list", 3 }, 
     { "remv", 4 }, 
     { "ipad", 5 }, 
     { "iprm", 6 }, 
    }; 
    for (int i = 0; i < sizeof(pairs)/sizeof(pairs[0]); ++i) { 
     if (!strcmp(textbuff, pairs[i].str)) 
      return pairs[i].id; 
    } 
    return 0; 
} 
+0

Hace que el código sea más claro, probablemente no más rápido. –

+0

¿Por qué no simplemente usar una matriz asociativa STL, entonces? – riviera

+0

Si hace un par de un miembro 'static 'de la función, no se volverá a generar potencialmente cada vez que se ingrese la función. –

2

Había dos preguntas, por lo que yo entendí. Optimización y si/cambiar.

En primer lugar, la optimización de código es un proceso costoso. Optimice solo aquellas partes de código, que son cuellos de botella por evidente. Lo dudo en este caso. Parece que está enviando una identificación textual para tomar una decisión sobre qué hacer con un mensaje. ¿De dónde viene el mensaje? IPC, XML, Archivo? ¿Qué vas a hacer con este mensaje? ¿Qué tan eficiente es el código de procesamiento del contenido del mensaje? Debería haber lugares en el código, que requieren más recursos que la comparación de cadenas.

¿Has probado algunos analizadores de rendimiento como Purify, gperf, cachegrind?

En relación con if/switch: el interruptor funciona solo con tipos de enteros.(Char, short, int, long, enum)

2

Suponiendo que realmente importa:

Debido a strcmp es lento, pero una comparación de enteros es rápido: si todos sus comandos son 4 caracteres - que pasa a encajar un entero de 32 bits: puede convertir cada cadena en un número de 32 bits y luego cambiar en función de eso.

De lo contrario, hay dos formas básicas para comparar rápidamente una cadena contra una gran cantidad de cadenas de candidatos:

  • tienda de los candidatos en una tabla hash.

o

  • Ordenar los candidatos ordenados alfabéticamente en una matriz. A continuación, puede realizar una búsqueda binaria en la matriz utilizando el resultado de strcmp para buscar una coincidencia o excluir la mitad de los candidatos restantes.

Como nota al margen - compiladores, como MSVC y GCC, han puesto en marcha una optimización en los interruptores que pone a prueba las condiciones del interruptor utilizando una búsqueda binaria. Por lo tanto, una instrucción de conmutación con, digamos, 256 elementos, se optimizará hasta, como máximo, 8 operaciones de comparación.

4

Puede poner todos los valores en un std :: map.

class MessageMap 
{ 
    std::map<std::string,int> data; 
    public: 
     MessageMap() 
     { 
      data["text"] = 1; 
      data["name"] = 2; 
      data["list"] = 3; 
      data["remv"] = 4; 
      data["ipad"] = 5; 
      data["iprm"] = 6; 
     } 
     int getMessageId(std::string const& index) cosnt 
     { 
      std::map<std::string,int>::const_iterator f; 
      if ((f = data.find(index)) != data.end()) 
      { 
       return f->second; 
      } 
      return 0; 
     } 
}; 
int identifyMsg(char* textbuff) 
{ 
    static MessageMap mssageMap; 
    return messageMap.getMessageId(textbuff); 
} 
Cuestiones relacionadas