Envoyé par
bafman
Bien sûre, sur des appli "classiques", c'est moins important, mais quand on fait de la 3D temps réel sur des gros moteurs, les perfs sont un objectif permannent.
Ahhhhh, enfin quelqu'un qui me comprends je commençais a me sentir seul ....
Page 1 : Utiliser la STL
Page 2 : ici.
Bon, quelques astuces d'implementation en vrac :
J'en donnerai d'autres plus tard.
N'hesitez pas a commenter dessus si vous pensez pouvoir les optimiser plus.
Et n'hesitez pas a donner vos astuces ...
-> Precalculer les fonctions lourdes.
Très connu mais toujours efficace.
Où : une boucle qui fait appel a une fonction lourde mais previsible ( genre sinus / cosinus).
Quand : seulement si vous y passez souvez et que vous n'avez pas besoin d'une très grande precision.
Comment :
Creer un tableau de 10 x 360 par exemple, faire une boucle qui le remplit avec des valeurs resultats de la fonction.
A l'utilisation, on a juste un utiliser le tableau avec comme index l'angle en dixième de degrès.
Variante : rand().
Rand() peut etre précalculé de cette façon.A eviter si vous voulez un vrai rand, mais si vous avez besoin d'un rand entre 0 et 255, par exemple, c'est parfait.
A cumuler avec la technique suivante pour une vitesse assez incroyable.
-> Index de tableaux en unsigned char / unsigned short.
Ca peut paraitre stupide puisque les registres sont en 32 bits, mais en fait, c'est plus rapide, vu que ça evite d'avoir a verifier que l'index est dans les limites prévues.
Où : si vous avez besoin de boucler sur quelque chose qui a exactement 256 ou 65536 valeurs possibles.
Quand : dès que l'on peut, c'est a dire presque jamais.
Comment :
Exemple de cette technique cumulée avec la précedante :
1 2 3 4 5 6 7 8 9 10 11
| const int MAX_SIZE = 65536;
unsigned int *g_precalcRands = NULL;
unsigned short g_index;
void Init()
{
g_precalcRands = new unsigned int[ MAX_SIZE ];
for( unsigned int i = 0 ; i < MAX_SIZE ; i++ )
{
g_precalcRands = rand( ) & 0x1FF;
}
} |
Maintenant que c'est rempli, vous avez des rands entre 0 et 511 de façon très, très rapide.
1 2 3 4 5
|
inline unsigned int myrand()
{
return g_precalcRands[++g_index];
} |
g_index etant un unsigned short, il est forcement entre 0 et MAX_SIZE - 1, ce qui correspond a tous les index valides pour le tableau, donc pas besoin de verification.
Et il reviendra tout seul a zero.
Touche finale, même pas besoin de l'initialiser.Si votre compilateur est gentil, il ne le mettra pas a zero, vous evitant d'avoir a appeler g_index = rand() & 0xFFFF; pour l'initialiser a une valeur au pif ...
Certes, c'est pas beau, et ça passe que sur une architecture 32 bits, mais qu'est-ce-que c'est rapide >.<
-> Preallocation de la mémoire.
Où : Là où vous avez un nombre précis d'objets que vous passez votre temps a construire / detruire.
Quand : au moment d'en construire un / detruire un.
Comment : Placement new, et explicit destructor.
Un peu compliqué mais potentiellement très puissant.
Premiere etape : placement new.
Exemple :
1 2
| char * l_buffer = new char[256];
std::string * l_string = new ( l_buffer ) std::string(); |
L'exemple est pourri, mais ça donne le principe. La mémoire est alloué en bloc par le buffer, pas lors du new sur l'objet.Celui çi ne fait qu'appeler le constructeur.
Deuxième etape : placement delete, sauf qu'il n'existe pas, dommage.
Appeler le destructeur comme ça ne libere pas la mémoire, donc c'est a reserver exclusivement a cet usage.
-> Calculs de distance
Technique extremement spécialisée ( je vois pas d'applications en dehors de la 3D / jeux / simulations, mais bon, vous etes inventifs).
Explication directe :
soit 3 points dans l'espace A,B,C.
La distance AB se calcule comme sqrt((a.x-b.x)² + (a.y-b.y)² + (a.z-b.z)²)
Sauf que la racine carrée n'est pas nécessaire quand on fait des comparaisons entre 2 distances.
Ca tombe bien, la racine carrée est une opération couteuse.
Cas pratique : en pathfinding A *, pour trouver les liens les plus courts....
-> Maps de paire d'objets, recherche d'une paire précise.
C'est basé sur le principe d'une hashmap, mais en plus rapide.
Probleme : vous avez une multimap qui stoque des paires d'objet, tous les 2 en pointeurs.Et vous devez recuperer une paire précise la dedans.
Technique : map< __int64 , std::pair< A * , B *>>
Remplacez __int64 par son equivalent local si vous ne bossez pas sous windows ( long long int sous gcc)
la clef de la map se crée comme suit :
__int64 l_clef = reinterpret_cast<__int64>(l_a) + (reinterpret_cast<__int64>(l_b)<<32);
Et voila, si vous avez les 2 pointeurs, vous avez la clef, donc vous pouvez chercher sur la map.
Je n'ai pas encore eu l'occasion de tester ça, mais ça me parait prometteur.
Calculs en virgule fixe.
Très, très chaud.
Principe de base : faire vos calculs sur des ints plutot que sur des floats, c'est beaucoup plus rapide ( même si l'ecart diminue avec les proc actuels).
Le problème principal c'est de rester dans les limites des ints malgré les calculs compliqués, et d'eviter d'enchainer les calculs car cela entraine une perte de precision monstrueuse.
Le meilleur exemple que je connais c'est une des demos du moteur Ogre3D, elle s'appelle "Dynamic Textures", et elle tourne au moins 5 fois plus vite avec les ints qu'avec les même calculs en floats...
Partager