CONNEXION
  • RetourJeux
    • Sorties
    • Hit Parade
    • Les + populaires
    • Les + attendus
    • Soluces
    • Tous les Jeux
    • Gaming
  • RetourActu Gaming
    • News
    • Astuces
    • Tests
    • Previews
    • Toute l'actu gaming
  • RetourBons plans
    • Bons plans
    • Bons plans Smartphone
    • Bons plans Hardware
    • Bons plans Image et Son
    • Bons plans Amazon
    • Bons plans Cdiscount
    • Bons plans Decathlon
    • Bons plans Fnac
    • Tous les Bons plans
  • RetourJVTech
    • Actus High-Tech
    • Intelligence Artificielle
    • Smartphones
    • Mobilité urbaine
    • Hardware
    • Image et son
    • Tutoriels
    • Tests produits High-Tech
    • Guides d'achat High-Tech
    • JVTech
  • RetourCulture
    • Actus Culture
    • Culture
  • RetourVidéos
    • A la une
    • Gaming Live
    • Vidéos Tests
    • Vidéos Previews
    • Gameplay
    • Trailers
    • Chroniques
    • Replay Web TV
    • Toutes les vidéos
  • RetourForums
    • Hardware PC
    • PS5
    • Switch 2
    • Xbox Series
    • Switch
    • Pokemon pocket
    • FC 25 Ultimate Team
    • League of Legends
    • Tous les Forums
  • PC
  • PS5
  • Xbox Series
  • Switch 2
  • PS4
  • One
  • Switch
  • iOS
  • Android
  • MMO
  • RPG
  • FPS
En ce moment Genshin Impact Valhalla Breath of the wild Animal Crossing GTA 5 Red dead 2
Liste des sujets

(C) Optimisation du switch (alternative)

lag-it
lag-it
Niveau 10
02 mai 2005 à 20:00:40

Une question que je me pose depuis un petit bout de temps concernant une possible amélioration des structures switch :
Il me semble que le switch en C/C++/etc... fonctionne comme les if/else imbriqué : le programme parcours linéairement la structure en partant de la première étiquette " case" jusqu´à la dernière ( ce qui parait assez légitime étant donné la nécessité d´employer " break" pour sortir du switch)
Néanmoins, le problème est que les accès aux différentes étiquettes ne se font donc pas à temps constant : si les premiers cas sont rencontrés assez vite, sur un switch portant sur des 100ain de cas au sein d´une boucle s´exécutant des 100 aines de fois par seconde, c´est assez problématique.

Aussi, ne serait-il pas judicieux d´employer lorsque c´est possible un tableau de pointeurs de fonctions ?

Ainsi :

switch(truc)
{
case 1:
/ / action1
break;
case 2:
/ / action2
break;

/ ...

}

Pourrait être remplacé par :

void action1( void ) ;
void action2( void ) ;
/ / etc...

void ( *actionTable[]) = { action1, action2, . .. };

Et le " switch" se résumerait à :

actionTable[n-1];

Bien sûr je me place dans une optique où seules les performances importent : la surcharge en terme de données etc, je m´en fiche.

Mais étant donné que cette méthode permet un accès à temps constant à une partie de code correspondant à un indice donné ne présente-elle pas de nombreux avantages ?

lag-it
lag-it
Niveau 10
02 mai 2005 à 20:02:14

Quand je dis if/else imbriqué, ca sous entends : parcours linéaire, car je sais bien que dans un if/else imbriqué, on ne s´intéresse pas au else si le if est réalisé, contrairement au switch qui parcours les étiquettes à partir de celle qui rempli la condition jusqu´à la fin :)

lag-it
lag-it
Niveau 10
02 mai 2005 à 20:03:20

Raah pis c´est actionTable[n-1](); :o))

lord_kalipsy
lord_kalipsy
Niveau 10
02 mai 2005 à 20:21:17

Intéressant ton truc, j´ai hate de voir ce que vont répondre ceux qui si connaisse ^^

Ptival
Ptival
Niveau 10
02 mai 2005 à 22:17:52

Un truc que j´me demandais à propos du switch, qui ferait la différence d´un ifelse :

if(1)
{
int pouet;
}
else
{
int pouet;
}

Compile

switch(paf)
{
case 1:
int pouet;
break;
case 2:
int pouet;
break;
}

Ne compile pas : Redeclaration of int pouet

Donc alors que les deux parties d´un ifelse ont une portée différente, on dirait que les différents cases d´un switch aient la même portée...Mais bon si ça voulait compiler ça devrait pas poser de problème vu que les cases sont différenciés...
Si qqn peut éclairer ma lanterne...

L´histoire du pointeur de fonction est aussi intéressant, lag-it c bien pensé :)

lag-it
lag-it
Niveau 10
02 mai 2005 à 22:23:59

" Donc alors que les deux parties d´un ifelse ont une portée différente, on dirait que les différents cases d´un switch aient la même portée"

héhé, regarde comment tu as écris tes blocs :
Dans ta structure if/else, on a clairement deux blocs distincts :

if(1)
/ / BLOC1
{
int pouet;
}
/ / FIN BLOC1
else
/ / BLOC2
{
int pouet;
}
/ / FIN BLOC2

alors que dans le corps de ton switch :

{
case 1:
int pouet;
break;
case 2:
int pouet;
break;
}

sont dans le même bloc :)
Pour le compilateur, c´est comme si tu écrivait :

{
int test;
int test;
}

Ce dernier n´est pas " suffisament intelligent" pour s´appercevoir qu´il y a des break ( et de toutes facons une telle pratique transgresse la norme qui stipule qu´on ne peut avoir 2 identificateurs de même non au sein d´un même bloc.

LGV
LGV
Niveau 28
02 mai 2005 à 22:31:45

pour les déclarations dans les switches, mieux vaut leur mettre des blocs de portée propres {} ça evite que le compilo ne rale.
Sinon, le pointeur sur fonction dans ce cas peut sembler interessant... mais c´est une lourdeur et une contrainte supplementaire ( pointeurs de memes types) ; quant au break, il ne fait que générer un jmp supplémentaires pour ne effectuer les instructions suivantes.
Mais oui, tu peux gagner qq instructions en passant par des pointeurs et en fournissant une table de sauts statique, plutot que laisser le compilo tester les cas et brancher où il faut

La preuve par l´exemple :

int a;
int b = rand()%2;
switch ( b)
{
case 0:
a = 0;
break;
case 1:
a = 1;
case 2:
a = 2;
}
std::cout < < a;

le rand, c´est pour que le compilo n´optimise pas un jmp statique, le cout c´est pour que le code ne soit considéré comme " inactif" et générer effectivement des instructions.
En compilant en release, on obtient ceci :

; 23 : int a;
; 24 : int b = rand()%2;

call _rand
and eax, -2147483647 ; 80000001H
jns SHORT $LN71@main
dec eax
or eax, -2 ; fffffffeH
inc eax
$LN71@main:

; 25 : switch ( b)

sub eax, 0
je SHORT $LN15@main
sub eax, 1
je SHORT $LN14@main
sub eax, 1
jne SHORT $LN70@main
$LN14@main:

; 29 : break;
; 30 : case 1:
; 31 : a = 1;
; 32 : case 2:
; 33 : a = 2;

mov eax, 2
jmp SHORT $LN16@main
$LN15@main:

; 26 : {
; 27 : case 0:
; 28 : a = 0;

xor eax, eax
jmp SHORT $LN16@main
$LN70@main:
mov eax, DWORD PTR _a$[esp+4]
$LN16@main:
push ebx
push esi
push edi

; 34 : }

je vous passe le cout final qui n´est qu´un call.

Qu´est-ce qu´on voit ? des je et des jne pour brancher au bon cas. Donc oui, si tu as BCP de case dans ton switch, tu risques d´y perdre.
D´un autre coté, un déréférencement de pointeur risque de générer un far jump, qui coute aussi...
Perso, je recommande la conduite suivante : des switch avec peu de cas, et des petits bouts de code dans les cases : c´est un bon compromis souplesse/efficacité.

Ce point est d´ailleurs discuté dans un Gems d´ailleurs il me semble, mais je ne sais plus lequel :-?

lag-it
lag-it
Niveau 10
02 mai 2005 à 22:39:05

Merci pour cette réponse très complète LGV :ok:
( juste une question subsiste cependant, qu´est ce exactement qu´un " far jump" ? Je lirai la réponse demain car je vais me coucher :o)) )

dnob700
dnob700
Niveau 10
03 mai 2005 à 00:46:48

le far jump c´est juste un jump quand l´instruction que tu pointe est a plus de 256 octets du jump ( enfin je crois que c´est ça).

kufa
kufa
Niveau 9
03 mai 2005 à 02:44:29

Bcp de compilo vont tout de meme utiliser des tables, et regroupper les valeurs des cases dans un nombre restreint de cas. Grosso modo apres quelques tests que j avais fait pour un post dans #prog, j avais remarque que gcc ( ok c est pas le meilleur compilo :) en i86 et arm utilisait ces tableaux a partir d environ 7-8 cases. Perso, je considere les switch ( en dehors de cote esthetique) sympa lorsqu il y a bcp de cas. Lorsqu il y en a peu, je le deconseille surtout sur les platformes ARM, pour lequelles je prefererai avoir un jmp plutot qu un *grand nombre* d instructions conditionnelles et de lectures memoire inutiles. ( je m explique la plupart des instructions peuvent etre du style Bcc/Jcc, par exemple addne, movne, etc, ce qui est utilise par defaut dans un switch :/)

LGV
LGV
Niveau 28
03 mai 2005 à 10:15:56

comme quoi on n´arretera jamais assez de repeter qu´un bon code est un code qui prend en compte le compilo :P
Au passage, de memoire il me semble que ICC propose cette optim, qq soit le nombre d´entrees, mais que ce n´est pas fait par defaut. A confirmer avec des tests recents.

lag-it
lag-it
Niveau 10
03 mai 2005 à 10:25:01

Merci bien pour les réponses très complètes :ok:

masterbrahma
masterbrahma
Niveau 6
03 mai 2005 à 15:05:37

vous pouver m´xpliquer svp comment on magne les switch sur rpg maker

fil_razorback
fil_razorback
Niveau 10
03 mai 2005 à 15:12:29

:rire:

kufa
kufa
Niveau 9
03 mai 2005 à 19:19:39

lgv: de tete, je confirme. Et je confirme ( a 100%) que icc est BIEN meilleur :D

/ kufa.se

Sous forums
  • Aide à l'achat Mac
  • Macintosh
  • Création de sites web
  • Création de Jeux
  • Linux
  • Programmation
  • Internet
  • Steam Deck
  • Hardware
La vidéo du moment