2009-03-16 10 views
2

Pour un jeu vidéo que j'implémente dans mes temps libres, j'ai essayé d'implémenter mes propres versions de sinf(), cosf() et atan2f(), en utilisant des tables de recherche. L'intention est d'avoir des implémentations plus rapides, mais avec moins de précision.Implémentation de fonctions de triggers à base de table

Ma mise en œuvre initiale est ci-dessous. Les fonctions fonctionnent et renvoient de bonnes valeurs approximatives. Le seul problème est qu'ils sont plus lents qu'appeler les fonctions standard sinf(), cosf() et atan2f().

Alors, qu'est-ce que je fais de mal?

// Geometry.h includes definitions of PI, TWO_PI, etc., as 
// well as the prototypes for the public functions 
#include "Geometry.h" 

namespace { 
    // Number of entries in the sin/cos lookup table 
    const int SinTableCount = 512; 

    // Angle covered by each table entry 
    const float SinTableDelta = TWO_PI/(float)SinTableCount; 

    // Lookup table for Sin() results 
    float SinTable[SinTableCount]; 

    // This object initializes the contents of the SinTable array exactly once 
    class SinTableInitializer { 
    public: 
     SinTableInitializer() { 
      for (int i = 0; i < SinTableCount; ++i) { 
       SinTable[i] = sinf((float)i * SinTableDelta); 
      } 
     } 
    }; 
    static SinTableInitializer sinTableInitializer; 

    // Number of entries in the atan lookup table 
    const int AtanTableCount = 512; 

    // Interval covered by each Atan table entry 
    const float AtanTableDelta = 1.0f/(float)AtanTableCount; 

    // Lookup table for Atan() results 
    float AtanTable[AtanTableCount]; 

    // This object initializes the contents of the AtanTable array exactly once 
    class AtanTableInitializer { 
    public: 
     AtanTableInitializer() { 
      for (int i = 0; i < AtanTableCount; ++i) { 
       AtanTable[i] = atanf((float)i * AtanTableDelta); 
      } 
     } 
    }; 
    static AtanTableInitializer atanTableInitializer; 

    // Lookup result in table. 
    // Preconditions: y > 0, x > 0, y < x 
    static float AtanLookup2(float y, float x) { 
     assert(y > 0.0f); 
     assert(x > 0.0f); 
     assert(y < x); 

     const float ratio = y/x; 
     const int index = (int)(ratio/AtanTableDelta); 
     return AtanTable[index];  
    } 

} 

float Sin(float angle) { 
    // If angle is negative, reflect around X-axis and negate result 
    bool mustNegateResult = false; 
    if (angle < 0.0f) { 
     mustNegateResult = true; 
     angle = -angle; 
    } 

    // Normalize angle so that it is in the interval (0.0, PI) 
    while (angle >= TWO_PI) { 
     angle -= TWO_PI; 
    } 

    const int index = (int)(angle/SinTableDelta); 
    const float result = SinTable[index]; 

    return mustNegateResult? (-result) : result; 
} 

float Cos(float angle) { 
    return Sin(angle + PI_2); 
} 

float Atan2(float y, float x) { 
    // Handle x == 0 or x == -0 
    // (See atan2(3) for specification of sign-bit handling.) 
    if (x == 0.0f) { 
     if (y > 0.0f) { 
      return PI_2; 
     } 
     else if (y < 0.0f) { 
      return -PI_2; 
     } 
     else if (signbit(x)) { 
      return signbit(y)? -PI : PI; 
     } 
     else { 
      return signbit(y)? -0.0f : 0.0f; 
     } 
    } 

    // Handle y == 0, x != 0 
    if (y == 0.0f) { 
     return (x > 0.0f)? 0.0f : PI; 
    } 

    // Handle y == x 
    if (y == x) { 
     return (x > 0.0f)? PI_4 : -(3.0f * PI_4); 
    } 

    // Handle y == -x 
    if (y == -x) { 
     return (x > 0.0f)? -PI_4 : (3.0f * PI_4); 
    } 

    // For other cases, determine quadrant and do appropriate lookup and calculation 
    bool right = (x > 0.0f); 
    bool top = (y > 0.0f); 
    if (right && top) { 
     // First quadrant 
     if (y < x) { 
      return AtanLookup2(y, x); 
     } 
     else { 
      return PI_2 - AtanLookup2(x, y); 
     } 
    } 
    else if (!right && top) { 
     // Second quadrant 
     const float posx = fabsf(x); 
     if (y < posx) { 
      return PI - AtanLookup2(y, posx); 
     } 
     else { 
      return PI_2 + AtanLookup2(posx, y); 
     } 
    } 
    else if (!right && !top) { 
     // Third quadrant 
     const float posx = fabsf(x); 
     const float posy = fabsf(y); 
     if (posy < posx) { 
      return -PI + AtanLookup2(posy, posx); 
     } 
     else { 
      return -PI_2 - AtanLookup2(posx, posy); 
     } 
    } 
    else { // right && !top 
     // Fourth quadrant 
     const float posy = fabsf(y); 
     if (posy < x) { 
      return -AtanLookup2(posy, x); 
     } 
     else { 
      return -PI_2 + AtanLookup2(x, posy); 
     } 
    } 

    return 0.0f; 
} 
+0

bien, les vrais sont optimisés à la main, et prob. aussi vite que possible! –

+0

avez-vous réellement déterminé que ceux intégrés sont un goulot d'étranglement ?? –

Répondre

9

« L'optimisation prématurée est la racine de tous les maux » - Donald Knuth

compilateurs De nos jours fournissent très efficaces pour les fonctions trigonométriques intrinsics qui obtiennent le meilleur des processeurs modernes (SSE etc.), ce qui explique pourquoi vous pouvez à peine battre les fonctions intégrées. Ne perdez pas trop de temps sur ces pièces et concentrez-vous plutôt sur les véritables goulots d'étranglement que vous pouvez repérer avec un profileur.

+1

Citation: Attribué à tort à Knuth. Hoare semble être l'initiateur, bien que Hoare décline selon l'article de Wikipedia - http://en.wikipedia.org/wiki/C._A._R._Hoare – dirkgently

+0

@dirkgently: thx pour l'info! – fbonnet

+0

Knuth a été le premier à le mettre sous presse (dans son séminal "Programmation structurée avec GOTOs").Citant Wikipédia comme une source, c'est comme dire: "un gars au hasard sur les interwebs dit ...." –

0

Je suis inquiet par ce lieu:

// Normalize angle so that it is in the interval (0.0, PI) 
while (angle >= TWO_PI) { 
    angle -= TWO_PI; 
} 

Mais vous pouvez: Ajouter du temps mètres à toutes les fonctions, écrire des tests spéciaux de performance, exécuter des tests de performance, impression rapport de test du temps. Je pense que vous connaîtrez la réponse après ces tests.

Vous pouvez également utiliser certains outils de profilage tels que AQTime.

0

Les fonctions intégrées sont déjà très bien optimisées, donc il va être vraiment difficile de les battre. Personnellement, je chercherais ailleurs des endroits pour gagner en performance.

Cela dit, une optimisation que je peux voir dans votre code:

// Normalize angle so that it is in the interval (0.0, PI) 
while (angle >= TWO_PI) { 
    angle -= TWO_PI; 
} 

pourrait être remplacé par:

angle = fmod(angle, TWO_PI); 
+0

no. parce que l'angle a une valeur flottante. – bayda

+0

peut-être utiliser fmod() – basszero

3

Rappelez-vous que vous avez un co-processeur ... vous auriez vu une augmenter en vitesse si c'était en 1993 ... mais aujourd'hui vous aurez du mal à vaincre intrinsèques indigènes.

Essayez de voir le désastre à sinf.

Questions connexes