2011-01-30 13 views
5

Ho una domanda sull'opportunità di utilizzare "case" o "if" in una funzione che viene chiamata abbastanza. Ecco quanto segue ora, in "if"; il codice è autoesplicativo:Ottimizzazione del codice; switch versus if

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; 
} 

La mia domanda è: un interruttore funzionerà meglio? So che se uso 'ifs', posso posizionare le opzioni più probabili nella parte superiore.

+0

vedere questo: http://stackoverflow.com/questions/97987/switch-vs-if-else – Nawaz

+1

Dovresti concentrarsi sul rendere il codice corretto prima di preoccuparsi di micro-ottimizzazione. E una volta che è corretto, è necessario misurare prima di ottimizzare. E poi quando pensi di aver bisogno di ottimizzare dovresti pensare a lungo e duramente sulla manutenibilità futura del codice ottimizzato. In breve, non ha senso avere codice ottimizzato che non è corretto. –

risposta

10

Non è possibile utilizzare le istruzioni switch per le stringhe perché sono puntatori e non vengono valutate in fase di compilazione. Sei bloccato con l'utilizzo di un gruppo di istruzioni if.

Tuttavia, per motivi di prestazioni, credo che gli interruttori funzionino meglio quando ci sono più condizioni da controllare, ma la differenza sarà così piccola che non importa.

non ho mai provato prima, però, ma ho letto su questo tipo di ottimizzazione interruttore:

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

Infatti. Ma a parte questo: la domanda "che si comporta meglio" sarebbe discutibile anche se fosse possibile. – delnan

+0

Un altro buon argomento su SO: http://stackoverflow.com/questions/97987/switch-vs-if-else – Nawaz

+3

Questi switch nidificati non dovrebbero avere alcun impatto positivo sulle prestazioni. Se si verifica uno dei casi frequenti, nessuna condizione verrà comunque verificata per nessuno degli altri casi. Lo stesso vale per il solito se-elseif. – Thorarin

5

È potrebbe uso gperf per generare perfetti hash dei "verbi" che si desidera vedere. Quindi è possibile utilizzare una dichiarazione switch.

Oppure, si potrebbe fare qualcosa di simile:

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

(Si ottiene l'idea).

Per quanto ancora un'altra opzione (se tutti i comandi sono 4 caratteri), trasformarli in un unico numero a 32 bit e quindi passare su quella:

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

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

Per essere onesti, però, a meno la lista di opzioni è particolarmente grande, o questa funzione viene chiamata un numero oscuro di volte, non ne varrà la pena.

Ricordare: misurare prima; ottimizzare in seguito (solo se necessario).

+0

generando hash, quindi l'utilizzo di switch non è eccessivo? specialmente quando l'OP si concentra su prestazioni così minuscole? – Nawaz

+0

Potrebbe valerne la pena; potrebbe non farlo. Misura prima, decidi se vale la pena ottimizzare. –

+0

Upvoted a causa dell'ottimizzazione "mashed-to-32bits" :) – riviera

2

Si potrebbe usare molto bene "enum" per quelle stringhe. quindi utilizzare le istruzioni caso switch.

+1

+1 per errore: :-) In realtà, questo è quello che sta facendo - la conversione di una stringa di identificazione in un numero id. Se l'ID viene dai dati (ad esempio XML), difficilmente può essere un numero, ma mai un enum. –

2

Un'altra alternativa che ho incontrato di recente, che potrebbe o non potrebbe andare bene vostro gradimento è:

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

Rende il codice più chiaro, probabilmente non più veloce. –

+0

Perché non utilizzare semplicemente un array associativo STL, quindi? – riviera

+0

Se si fanno delle coppie un membro 'static' della funzione, esso non sarà potenzialmente ri-bult ogni volta che si inserisce la funzione. –

2

C'erano due domande, per quanto ho capito. Ottimizzazione e if/switch.

Prima di tutto, l'ottimizzazione del codice è un processo costoso. Ottimizza solo quelle parti di codice, che sono chiaramente scolpiti. Ne dubito in questo caso. Sembra che tu stia inviando un ID testuale per prendere una decisione su cosa fare con un messaggio. Da dove viene il messaggio? IPC, XML, File? Che cosa hai intenzione di fare con questo messaggio? Quanto è performante il codice di elaborazione del contenuto del messaggio? Ci dovrebbero essere posti nel codice, che richiedono più risorse rispetto al confronto tra stringhe.

Avete provato alcuni analizzatori di prestazioni come Purify, gperf, cachegrind?

Per quanto riguarda if/switch: switch funziona solo con tipi interi.(Char, short, int, long, enum)

2

Supponendo che conta davvero:

Perché strcmp è lento, ma un intero confronto è veloce: se tutti i comandi sono 4 caratteri - che avviene per adattarsi un intero a 32 bit: puoi castare ogni stringa su un numero a 32 bit, e quindi cambiare in base a quello.

In caso contrario, ci sono due modi di base per confrontare rapidamente una stringa con un sacco di stringhe candidati:

  • Conservare i candidati in una tabella hash.

o

  • Ordina i candidati in ordine alfabetico ordinati in un array. È quindi possibile eseguire una ricerca binaria sull'array utilizzando il risultato di strcmp per trovare una corrispondenza o escludere la metà dei candidati rimanenti.

Come nota a lato - compilatori, come MSVC e GCC, hanno implementato un'ottimizzazione su switch che verifica le condizioni dell'interruttore utilizzando una ricerca binaria. Quindi un'istruzione switch con, ad esempio, 256 elementi, sarà ottimizzata fino a 8 operazioni di confronto.

4

È possibile inserire tutti i valori in una 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); 
}