Optimisation de tableau de fonctions

Optimisation de tableau de fonctions - C++ - Programmation

Marsh Posté le 26-04-2014 à 17:27:33    

Hullo :hello:

 

Je suis en train de bosser sur une machine virtuelle (pour un langage interprété), je veux faire en sorte qu'elle soit le plus flexible possible (ajouter des nouvelles instructions sans avoir à retoucher le code source natif, idem pour gérer de nouveaux types de variables).

 

Mon implémentation actuelle se base sur un tableau de fonctions, où chaque case correspond à un op code et pointe vers la fonction à exécuter (celle-ci étant une lambda).
La déclaration du tableau est comme ça :

 
Code :
  1. using OPFunction_t =  void(*)(VirtualMachine*, const Instruction& );
  2. class VirtualMachine
  3. {
  4.     private:
  5.         std::vector<OPFunction_t> OPFunctions;
  6. }
 

(nb À la base, OPFunction_t était défini comme std::function<void( const Instruction& ) > et capturait le this de la machine virtuelle dans la fonction lambda, mais ça ralentissait le processus, et j'ai fini par déterminer que les pointeurs sur fonction type C étaient plus rapide que std::function.)

 

Les fonctions sont ensuite géré dans une fonction d'initialisation :

 
Code :
  1. void VirtualMachine::Initialize()
  2. {
  3. OPFunctions[ OP_LoadK ] = [] (VirtualMachine* self, const Instruction& I )
  4. {
  5.     uint32_t Extra = self->ByteCode[ ++self->InstructionAddress ].Data;
  6.     self->Stack[ self->GetLocalIndex( Extra ) ] = self->GetConstant( I.GetU_AArg(), I.GetU_SArg() );
  7. };
  8. }
 

L'exécution est ensuite assez simple, ça donne un truc comme ça :

 
Code :
  1. void VirtualMachine::Execute()
  2. {
  3.     for(;;)
  4.     {
  5.         const Instruction& I = ByteCode[ InstructionAddress ];
  6.         OPFunctions[ I.GetOPCode() ]( I );
  7.         ++InstructionAddress;
  8.         if ( InstructionAddress >= ByteCode.size() )
  9.             break;
  10.     }
  11. }
 

Bref, j'ai fait un test de fonction écrite avec  cette Vm assez simple, voilà le pseudo-code (fonction bidon super optimisable) :

 
Code :
  1. function foo( int A, int B, int It )
  2. {
  3.     int C,i;
  4.     C := A;
  5.     for (i=0, It, i := i+1)
  6.     {
  7.       C := C + B;
  8.     }
  9.     return C;
  10. }
 

J'ai fait des tests comparatif entre le C++, le Lua et ma VM.
De tête, ça donnait ~2ms en C++, ~15ms en Lua et ~25ms avec ma VM.

 

J'ai cherché un peu partout les bottlenecks, et il se trouve que ça venait du tableau de pointeurs sur fonction.
J'ai donc retapé mon code en changeant le tableau de fonctions en un switch, et là miracle, ~12ms sur ma VM.

 

Le soucis, c'est que je perd entièrement cet aspect modulaire, je suis obligé de retaper le code source de la VM pour intégrer des nouveaux types d'instructions, et ça me semble être un bordel interminable pour gérer plein de types différent (int, float, string, vec2, vec3, mat4, etc,etc).

 

Je voudrais savoir s'il existait un moyen d'inliner ce tableau de fonction, pour qu'il soit en gros comme un switch avec le code en dur (quitte à bosser avec des templates de partout et à ralentir les temps de compilations), quelqu'un aurait une piste ?

 

Merci ! [:mad_overclocker]


Message édité par Terminapor le 26-04-2014 à 17:28:38

---------------
Perhaps you don't deserve to breathe
Reply

Marsh Posté le 26-04-2014 à 17:27:33   

Reply

Marsh Posté le 26-04-2014 à 21:43:00    

Bon ben finalement j'ai trouvé ça :  
 
http://christopherschwaab.wordpres [...] ents-in-c/
 
Ca va me donner une bonne occasion d'apprendre la metaprog avec boost :D
 
Si quelqu'un connaît une astuce un peu plus "joli" je suis toujours preneur. :jap:


---------------
Perhaps you don't deserve to breathe
Reply

Sujets relatifs:

Leave a Replay

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