[C] switch-case vs if-else

Forum dedicato alla programmazione.

Moderatore: Staff

Regole del forum
1) Citare in modo preciso il linguaggio di programmazione usato.
2) Se possibile portare un esempio del risultato atteso.
3) Leggere attentamente le risposte ricevute.
4) Scrivere i messaggi con il colore di default, evitare altri colori.
5) Scrivere in Italiano o in Inglese, se possibile grammaticalmente corretto, evitate stili di scrittura poco chiari, quindi nessuna abbreviazione tipo telegramma o scrittura stile SMS o CHAT.
6) Appena registrati è consigliato presentarsi nel forum dedicato.

La non osservanza delle regole porta a provvedimenti di vari tipo da parte dello staff, in particolare la non osservanza della regola 5 porta alla cancellazione del post e alla segnalazione dell'utente. In caso di recidività l'utente rischia il ban temporaneo.
Rispondi
Mario Vanoni
Iper Master
Iper Master
Messaggi: 3174
Iscritto il: lun 3 set 2007, 21:20
Nome Cognome: Mario Vanoni
Slackware: 12.2
Kernel: 3.0.4 statico
Desktop: fluxbox/seamonkey
Località: Cuasso al Monte (VA)

[C] switch-case vs if-else

Messaggio da Mario Vanoni »

In inglese

http://www.safercode.com/blog/2008/10/2 ... speed.html

Ho sempre preferito switch-case ad if-else,
ma mai usato uno switch nello switch
come descritto nell'articolo, interessante.

Avatar utente
aschenaz
Staff
Staff
Messaggi: 4623
Iscritto il: mer 28 lug 2004, 0:00
Nome Cognome: Nino
Slackware: current
Kernel: 5.4.x
Desktop: KDE
Località: Reggio Calabria
Contatta:

Re: [C] switch-case vs if-else

Messaggio da aschenaz »

Non ho capito lo scopo di quell'esempio:

Codice: Seleziona tutto

switch(a)
{
  case 1: /*do something*/
  case 2: /*do something*/
  case 3: /*do something*/
  default:
    switch(a)
    {
      case 4: /*do something*/
      case 5: /*do something*/
      case 6: /*do something*/
      default: /*do something*/
    }
}
Non sarebbe stato equivalente scrivere:

Codice: Seleziona tutto

switch(a)
{
  case 1: /*do something*/
  case 2: /*do something*/
  case 3: /*do something*/
  case 4: /*do something*/
  case 5: /*do something*/
  case 6: /*do something*/
  default: /*do something*/
}
:?:

Avatar utente
nuitari
Linux 3.x
Linux 3.x
Messaggi: 777
Iscritto il: dom 14 ott 2007, 12:51
Slackware: 12.0
Località: San Colombano al Lambro
Contatta:

Re: [C] switch-case vs if-else

Messaggio da nuitari »

Personalmente li ho sempre usati interscambiabilmente in base alla semantica del codice. Diventa più una questione di lettura che altro.

Lo switch nidificato nell'esempio serve per sfruttare le caratteristiche dei compilatori C/C++ nella generazione del *codice macchina* allo scopo di ottimizzarlo per la velocità.
La logica è che indipendentemente da tutto il blocco default ha la priorità minore e che nel caso lo switch sia di piccole dimensioni viene usata una (performante) jump table. Per cui, suddividere un blocco switch isolando le occorrenze più probabili in uno switch di piccole dimensioni permette di forzare l'utlizzo di una jump table almeno per queste ultime.

Si tratta comunque di ottimizzazioni estreme, in cui la velocità è un obbiettivo mission critical (e richiedono una notevole conoscenza del comportamento del compilatore, con disassemblaggi al seguito).
Onestamente se si ha bisogno di performance così elevate, è quasi conveniente scrivere in assembler il codice relativo. Interessante comunque ^^

Avatar utente
ZeD
Linux 1.x
Linux 1.x
Messaggi: 112
Iscritto il: ven 28 ott 2005, 12:30
Slackware: 12.1
Località: Terra

Re: [C] switch-case vs if-else

Messaggio da ZeD »

Mario Vanoni ha scritto:In inglese

http://www.safercode.com/blog/2008/10/2 ... speed.html

Ho sempre preferito switch-case ad if-else,
ma mai usato uno switch nello switch
come descritto nell'articolo, interessante.
Mah, a me pare una mezza fesseria.. cioé
se capita d'avere, boh, + di 512 confronti allora la cosa magari inizia ad avere un senso (per i vari longjump)... tuttavia, qualcuno ha *effettivamente* visto che cambia? :)
prova1.c

Codice: Seleziona tutto

int main() {
    unsigned i = 0;
    switch (i) {
        case 1:
        case 2:
        case 3:
        default:
            switch (i) {
                case 4:
                case 5:
                case 6:
                default:
                    i += 10; /* operazione a caso */
            }
    }
}
prova2.c

Codice: Seleziona tutto

int main() {
    unsigned i = 0;
    switch (i) {
        case 1:
        case 2:
        case 3:
/*        default:
            switch (i) {*/
                case 4:
                case 5:
                case 6:
                default:
                    i += 10; /* operazione a caso */
            /*}*/
    }
}
diff prova{1,2}.c

Codice: Seleziona tutto

--- prova1.c    2009-02-04 09:23:22.000000000 +0100
+++ prova2.c    2009-02-04 09:23:52.000000000 +0100
@@ -4,13 +4,13 @@
         case 1:
         case 2:
         case 3:
-        default:
-            switch (i) {
+/*        default:
+            switch (i) {*/
                 case 4:
                 case 5:
                 case 6:
                 default:
                     i += 10; /* operazione a caso */
-            }
+            /*}*/
     }
 }
e vediamo l'assembly generato:

Codice: Seleziona tutto

$ for i in `seq 4`; do
    gcc -O$i -S prova1.c -o prova1_$i.s
    gcc -O$i -S prova2.c -o prova2_$i.s
    diff prova1_$i.s prova2_$i.s
done

--- prova1_1.s  2009-02-03 14:56:48.000000000 +0100
+++ prova2_1.s  2009-02-03 14:56:48.000000000 +0100
@@ -1,4 +1,4 @@
-       .file   "prova1.c"
+       .file   "prova2.c"
        .text
 .globl main
        .type   main, @function
--- prova1_2.s  2009-02-03 14:56:48.000000000 +0100
+++ prova2_2.s  2009-02-03 14:56:48.000000000 +0100
@@ -1,4 +1,4 @@
-       .file   "prova1.c"
+       .file   "prova2.c"
        .text
        .p2align 4,,15
 .globl main
--- prova1_3.s  2009-02-03 14:56:48.000000000 +0100
+++ prova2_3.s  2009-02-03 14:56:48.000000000 +0100
@@ -1,4 +1,4 @@
-       .file   "prova1.c"
+       .file   "prova2.c"
        .text
        .p2align 4,,15
 .globl main
--- prova1_4.s  2009-02-03 14:56:49.000000000 +0100
+++ prova2_4.s  2009-02-03 14:56:49.000000000 +0100
@@ -1,4 +1,4 @@
-       .file   "prova1.c"
+       .file   "prova2.c"
        .text
        .p2align 4,,15
 .globl main
$
in pratica non ci sono differenze :)
Ultima modifica di ZeD il mer 4 feb 2009, 9:26, modificato 1 volta in totale.

Avatar utente
conraid
Staff
Staff
Messaggi: 13631
Iscritto il: gio 14 lug 2005, 0:00
Nome Cognome: Corrado Franco
Slackware: current64
Desktop: kde
Località: Livorno
Contatta:

Re: [C] switch-case vs if-else

Messaggio da conraid »

ZeD ha scritto: prova1.c

Codice: Seleziona tutto

int main() {
    unsigned i = 0;
    switch (i) {
        case 1:
        case 2:
        case 3:
        default:
            switch (i) {
                case 4:
                case 5:
                case 6:
                default:
                    i += 10; /* operazione a caso */
            }
    }
}
prova2.c

Codice: Seleziona tutto

int main() {
    unsigned i = 0;
    switch (i) {
        case 1:
        case 2:
        case 3:
        default:
            switch (i) {
                case 4:
                case 5:
                case 6:
                default:
                    i += 10; /* operazione a caso */
            }
    }
}
in pratica non ci sono differenze :)
Perché avrebbero dovuto esserci se non ci sono nemmeno nel codice sorgente?

Avatar utente
Vito
Staff
Staff
Messaggi: 4182
Iscritto il: mar 5 dic 2006, 17:28
Nome Cognome: Vito
Desktop: MacOS
Località: Monaco (DE)
Contatta:

Re: [C] switch-case vs if-else

Messaggio da Vito »

Non sono due strutture perfettamente equivalenti?


Non si aumenta la complessità dell'algoritmo inoltre nidificando dei case?

EDIT: Per quanto riguarda la scelta di if-else / sitch-case a seconda di quale algoritmo devo scrivere utilizzo una delle due strutture.
La nidificazione di if-else è indispensabile quando bisogna fare dei confronti particolari (esempio prelevare da una matrice gli elementi tra le due diagonali principali).
Uno switch-case invece è utilissimo quando si devono lavorare sulla variazione del valore di una variabile..anche perchè è molto più sintetico ed espressivo come costrutto!
"Stat rosa pristina nomina, nomina nuda tenemus." [ Umberto Eco - Il nome della rosa]

"Faber est suae quisque fortunae ." [ Appio Claudio Cieco]

Mario Vanoni
Iper Master
Iper Master
Messaggi: 3174
Iscritto il: lun 3 set 2007, 21:20
Nome Cognome: Mario Vanoni
Slackware: 12.2
Kernel: 3.0.4 statico
Desktop: fluxbox/seamonkey
Località: Cuasso al Monte (VA)

Re: [C] switch-case vs if-else

Messaggio da Mario Vanoni »

nuitari ha scritto:Personalmente li ho sempre usati interscambiabilmente in base alla semantica del codice. Diventa più una questione di lettura che altro.

Lo switch nidificato nell'esempio serve per sfruttare le caratteristiche dei compilatori C/C++ nella generazione del *codice macchina* allo scopo di ottimizzarlo per la velocità.
La logica è che indipendentemente da tutto il blocco default ha la priorità minore e che nel caso lo switch sia di piccole dimensioni viene usata una (performante) jump table. Per cui, suddividere un blocco switch isolando le occorrenze più probabili in uno switch di piccole dimensioni permette di forzare l'utlizzo di una jump table almeno per queste ultime.

Si tratta comunque di ottimizzazioni estreme, in cui la velocità è un obbiettivo mission critical (e richiedono una notevole conoscenza del comportamento del compilatore, con disassemblaggi al seguito).
Onestamente se si ha bisogno di performance così elevate, è quasi conveniente scrivere in assembler il codice relativo. Interessante comunque ^^
Hai ragione, e` la forzatura della jump-table.

Ma sarebbe interessante,
su una macchina Intel Core 2 o i7,
vedere le differenze tra gcc e icc.

Avatar utente
ZeD
Linux 1.x
Linux 1.x
Messaggi: 112
Iscritto il: ven 28 ott 2005, 12:30
Slackware: 12.1
Località: Terra

Re: [C] switch-case vs if-else

Messaggio da ZeD »

conraid ha scritto:Perché avrebbero dovuto esserci se non ci sono nemmeno nel codice sorgente?
Perché non so fare copia-incolla -.-
I 2 file sono effettivamente diversi: nel primo ci sono 2 switch annidati (0-3 + 4-6), nel secondo è uno unico (0-6)

Rispondi