2009-03-16 5 views
2

Für ein Videospiel, das ich in meiner Freizeit implementiere, habe ich versucht, meine eigenen Versionen von sinf(), cosf() und atan2f() mithilfe von Nachschlagetabellen zu implementieren. Die Absicht ist, Implementierungen zu haben, die schneller, jedoch mit geringerer Genauigkeit sind.Implementieren von auf Tabellen basierenden Trigonfunktionen

Meine ursprüngliche Implementierung ist unten. Die Funktionen funktionieren und geben gute ungefähre Werte zurück. Das einzige Problem ist, dass sie langsamer sind als Aufruf der Standardfunktionen sinf(), cosf() und atan2f().

Also, was mache ich falsch?

// 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

Nun, vermutlich sind die eingebauten handoptimiert und prob. so schnell wie möglich! –

+0

hast du tatsächlich festgestellt, dass die eingebauten ein Engpass sind ?? –

Antwort

9

„Vorzeitige Optimierung ist die Wurzel aller Übel“ - Donald Knuth

Heute Compiler bieten eine sehr effizientes intrinsics für trigonometrische Funktionen, die das Beste aus dem modernen Prozessoren (SSE etc.) erhalten, was erklärt, warum man sich kaum schlagen die eingebauten Funktionen. Verlieren Sie nicht zu viel Zeit auf diesen Teilen und konzentrieren Sie sich stattdessen auf die echten Engpässe, die Sie mit einem Profiler erkennen können.

+1

Quote: Falsch zu Knuth zugeschrieben. Hoare scheint der Urheber zu sein, obwohl Hoare dies laut dem Wikipedia-Artikel ablehnt - http://en.wikipedia.org/wiki/C._A._R._Hoare – dirkgently

+0

@dirkently: thx for the info! – fbonnet

+0

Knuth war der erste, der es in Druck brachte (in seinem bahnbrechenden "Structured Programming with GOTOs"). Zitat von Wikipedia als Quelle ist wie zu sagen, "irgendein zufälliger Typ auf den Interwebs sagt ...." –

0

Ich mache mir Sorgen, von diesem Ort:

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

Aber man kann: zeit Meter auf alle Funktionen hinzufügen, schreiben spezielle Leistungstests, führen Performance-Tests, Druckreport Zeittest. Ich denke du wirst nach diesen Tests antworten.

Sie können auch einige Profiling-Tools wie AQTime verwenden.

0

Die eingebauten Funktionen sind bereits sehr gut optimiert, so dass es WIRKLICH schwer wird, sie zu schlagen. Persönlich würde ich woanders nach Orten suchen, um Leistung zu bekommen.

Das hieß, eine Optimierung ich im Code sehen:

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

könne ersetzt werden durch:

angle = fmod(angle, TWO_PI); 
+0

nicht. weil Winkel Float-Wert hat. – bayda

+0

vielleicht verwenden fmod() – basszero

3

Denken Sie daran, Sie haben einen Co-Prozessor ... Sie habe gesehen ein Erhöhung der Geschwindigkeit, wenn es 1993 wäre ... aber heute wirst du kämpfen, um native intrinsics zu schlagen.

Versuchen Sie, disassolly zu sinf.

Verwandte Themen