mikeash.com: just this guy, you know?

next up previous contents
Next: Les accès mémoire Up: Optimisation de fluide Previous: Optimisation de fluide   Contents

Conversions int-float

Sur une grande partie des processeurs modernes, la conversion entre les données flottantes et les données entières est très lente. Par exemple, sur un processeur d'Intel or AMD de type x86, une conversion d'un float à un int nécessite de vider le pipeline du processeur, ce qui perturbe fortement les opérations du processeur. [16] Sur un PowerPC G4 de Motorola à 800MHz, une conversion d'un int de 16 bits prend plus de 43 nanosecondes, ce qui fait 35 cycles d'exécution [17]. Pour comparaison, la plupart des instructions dans l'unité flottant de ce même processeur s'exécutent sur un cycle dans le meilleur des cas, ou sur 5 cycles pour le pire cas. [18]

Souvent dans le code de la simulation il y a des variables de type int qui ne changent pas, ou qui changent d'une façon évidente, mais qui sont souvent utilisées dans les calculs avec des valeurs de type float. Ceci demande une conversion à chaque fois que la variable est utilisée, et cette conversion est redondante.

Par exemple, la variable N dans la fonction advect() est souvent utilisée dans les expressions avec des valeurs flottantes. En ajoutant une variable Nfloat et en faisant la conversion une seule fois avant la boucle, on économise beaucoup de conversions. Au lieu de convertir la valeur de N plusieurs fois pendant chaque exécution de la boucle, la valeur est convertie une fois au début.

Dans la même fonction, la variable i est dans le même cas, mais sa valeur change souvent. Mais ce changement est simple, alors il est possible de créer une variable ifloat. Au début de la boucle, ifloat commence avec la même valeur que i, et à chaque étape elle est incrémentée en même temps que i. Alors avec ce changement, il n'y a plus de conversion de la variable i.

Dans le même ordre d'idées, on préfère les multiplications que les divisions. Encore sur le PowerPC G4, une multiplication est dans la catégorie des instructions qui prennent entre 1 et 5 cycles, mais la division peut prendre plus que 14 cycles.

La fonction lin_solve() fait une division par la variable c à chaque itération de sa boucle. Comme c ne change jamais pendant cette boucle, on peut alors calculer sa réciproque, et multiplier par cette réciproque dans la boucle. Alors, on met au début de la fonction :

float cRecip = 1.0 / c;

Et puis dans la boucle, on remplace ... / c par ... * cRecip.


next up previous contents
Next: Les accès mémoire Up: Optimisation de fluide Previous: Optimisation de fluide   Contents
Michael Ash 2005-09-21

Code syntax highlighting thanks to Pygments.
Hosted at DigitalOcean.