pointeur sur fonction membre / switch case

pointeur sur fonction membre / switch case - C++ - Programmation

Marsh Posté le 24-05-2007 à 19:47:46    

Bonjour les habitués!
Je me disais : plutot que de faire un switch case, je vais faire un tableau de pointeurs sur fonctions membres, et ça va être plus rapide. Ce n'est pas le cas : le calcul avec switch/case est deux fois plus rapide. Pourquoi? Le problème n'a pas l'air d'exister si tout ça se fait à l'extérieur d'une classe.
 
 

Code :
  1. #include <string>
  2. #include <iostream>
  3. #include <vector>
  4. #include <math.h>
  5. #include <ctime>
  6. #define nb 1e7
  7. class test
  8. {
  9.     public:
  10.     test()
  11.     {
  12.         res=0;
  13.         tableau[0]=&test::f0;
  14.         tableau[1]=&test::f1;
  15.         tableau[2]=&test::f2;
  16.         tableau[3]=&test::f3;
  17.         tableau[4]=&test::f4;
  18.         tableau[5]=&test::f5;
  19.        
  20.         for (int i=0;i<nb;i++)
  21.         {
  22.             liste.push_back(i%5);
  23.         }
  24.     }
  25.    
  26.    
  27.     void calc1()
  28.     {
  29.         float t1=clock();
  30.         res=0;
  31.         for (int i=0;i<liste.size();i++)
  32.         {
  33.             int index=liste[i];
  34.             (this->*tableau[index])();
  35.         }
  36.         float t2=clock();
  37.         std::cout<<"duree1 :"<<t2-t1<<" resultat : "<<res<<std::endl;
  38.     }
  39.    
  40.    
  41.     void calc2()
  42.     {
  43.         float t1=clock();
  44.         res=0;
  45.         for (int i=0;i<liste.size();i++)
  46.         {
  47.             int index=liste[i];
  48.             switch(index)
  49.             {
  50.                 case 0: f0();break;
  51.                 case 1: f1();break;
  52.                 case 2: f2();break;
  53.                 case 3: f3();break;
  54.                 case 4: f4();break;
  55.                 case 5: f5();break;
  56.             }
  57.         }
  58.         float t2=clock();
  59.         std::cout<<"duree2 : "<<t2-t1<<" resultat : "<<res<<std::endl;
  60.     }
  61.        
  62.     private:
  63.     void f0(){res+=0;}
  64.     void f1(){res+=1;}
  65.     void f2(){res+=2;}
  66.     void f3(){res+=3;}
  67.     void f4(){res+=4;}
  68.     void f5(){res+=5;}
  69.     double res;
  70.     typedef void (test::*fp)();
  71.     fp tableau[10];
  72.     std::vector<int> liste;
  73. };
  74. int main()
  75. {
  76.     test calcul;
  77.     calcul.calc1();
  78.     calcul.calc2();
  79.     return(0);
  80. }

Reply

Marsh Posté le 24-05-2007 à 19:47:46   

Reply

Marsh Posté le 24-05-2007 à 19:50:56    

dans le cas avec le switch tu fait un appel direct à une fonction définie directement dans la classe -> inline.
 
Avec un pointeur sur fonction membre placé dans un vector ça devient beaucoup moins facile pour le compilo de savoir qu'il peut faire cet inline ....


---------------
Me: Django Localization, Yogo Puzzle, Chrome Grapher, C++ Signals, Brainf*ck.
Reply

Marsh Posté le 24-05-2007 à 20:08:23    

0x90 a écrit :

dans le cas avec le switch tu fait un appel direct à une fonction définie directement dans la classe -> inline.
 
Avec un pointeur sur fonction membre placé dans un vector ça devient beaucoup moins facile pour le compilo de savoir qu'il peut faire cet inline ....


 
Et je ne peux rien faire pour l'aider?

Reply

Marsh Posté le 24-05-2007 à 20:14:17    

Garde ton switch tout simplement [:spamafote]


---------------
Me: Django Localization, Yogo Puzzle, Chrome Grapher, C++ Signals, Brainf*ck.
Reply

Marsh Posté le 24-05-2007 à 20:18:07    

0x90 a écrit :

Garde ton switch tout simplement [:spamafote]


 
Oui, c'est ce que je vais faire... J'avais pensé à ça en me disant que je pouvais éviter des pages et des pages de switch.
Tant pis  :)

Reply

Marsh Posté le 24-05-2007 à 20:23:04    

Un tableau fait autant de lignes que le switch en gros normalement ....
 
Si jamais t'es sous GCC, que la portabilité du code t'inquiète pas trop et que tu as des séries de valeurs de switch qui appellent la même fonction tu peut utiliser des range.


---------------
Me: Django Localization, Yogo Puzzle, Chrome Grapher, C++ Signals, Brainf*ck.
Reply

Marsh Posté le 24-05-2007 à 23:38:05    

c'est une contre-optimisation votre truc car le contexte est bidon.
 
bien sûr si chaque fonction fait 0 ou 1 instruction assembleur (j'adore le res+=0), t'y gagne a faire un gros switch dégueulasse avec la seule instruction inlinée, plustôt qu'un call.
mais avec une fonction qui fait pas juste que nopper, le call depuis un tableau sera insignifiant, et passé un certain nombre de cas de switch, le call sera bien plus économique.
 
change tes res+= par des vraies fonctions (avec des boucles des machins et des bidules), et mets 16,20 fonctions tu vas voir le comportement va s'inverser.

Reply

Marsh Posté le 24-05-2007 à 23:48:35    

bjone a écrit :

c'est une contre-optimisation votre truc car le contexte est bidon.
 
bien sûr si chaque fonction fait 0 ou 1 instruction assembleur (j'adore le res+=0), t'y gagne a faire un gros switch dégueulasse avec la seule instruction inlinée, plustôt qu'un call.
mais avec une fonction qui fait pas juste que nopper, le call depuis un tableau sera insignifiant, et passé un certain nombre de cas de switch, le call sera bien plus économique.
 
change tes res+= par des vraies fonctions (avec des boucles des machins et des bidules), et mets 16,20 fonctions tu vas voir le comportement va s'inverser.


 
Je suis pas trop d'accord, y'a de grandes chances que le switch ( à partir d'une certaine taille et si y'a pas de gros trou vers des valeurs importantes ) soit traduit par le compilo par un tableau, dans ce cas tu garde le temps d'accès constant quelque soit le nombre de switch ET la possibilité d'inliner. Je vois pas trop dans quelle situation alors le déréférencement de pointeur de fonction membre + le call peut alors être moins couteux, par contre j'ai vraiment l'impression que le tableau est une "optimisation" faite main de ce que le compilo ferait, en moins bien à cause de la difficulté accrue d'analyse des pointeurs pour le compilo.
 
( Sinon effectivement le contexte de test est bidon tant qu'il n'est pas exactement la chose qui sera exécutée ;) )


---------------
Me: Django Localization, Yogo Puzzle, Chrome Grapher, C++ Signals, Brainf*ck.
Reply

Marsh Posté le 25-05-2007 à 08:36:47    

moi je préfère calc1. Je vois l'intérêt de l'inline ici que parce que les méthodes sont minuscules. Sinon, si elle était de taille plus normale, voire virtuelle, ça n'aurait strictement aucun intérêt.
 
Mais ici le problème, c'est bien que t'as 6 fonctions qui font la même chose à une nuance pres. fais un void f(int inc) { res += inc; } et voilà.

Reply

Marsh Posté le 25-05-2007 à 12:38:51    

0x90 a écrit :

Je suis pas trop d'accord, y'a de grandes chances que le switch ( à partir d'une certaine taille et si y'a pas de gros trou vers des valeurs importantes ) soit traduit par le compilo par un tableau, dans ce cas tu garde le temps d'accès constant quelque soit le nombre de switch ET la possibilité d'inliner. Je vois pas trop dans quelle situation alors le déréférencement de pointeur de fonction membre + le call peut alors être moins couteux, par contre j'ai vraiment l'impression que le tableau est une "optimisation" faite main de ce que le compilo ferait, en moins bien à cause de la difficulté accrue d'analyse des pointeurs pour le compilo.
 
( Sinon effectivement le contexte de test est bidon tant qu'il n'est pas exactement la chose qui sera exécutée ;) )


 
 
on est d'accord que si tu gardes des entiers contigus, le compilo peut utiliser une table de jumps avec les fonctions inlinées.
mais à la moindre irrégularité des cas de switch, le compilo doit basculer vers des tests classiques.
bon après si son cas c'est d'avoir que des case contigus, ça peut être bien, mais à vérifier avec la sortie asm.
 
mais bon avec des vrais fonctions qui font plus de 10 cycles d'horloge, le call du tableau sur les fonctions non inlinées sera déjà moins notable.

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed