2009-03-16 18 views
2

は、私は、ルックアップテーブルを使用して、SINFの私の独自のバージョンを実装する()、cosf()、およびatan2f()を試してみました。その目的は、精度は低いものの、より高速な実装を実現することです。実装テーブルルックアップベースの三角関数、私は私の暇な時に実装していますビデオゲームのために

私の最初の実装は以下の通りです。関数は機能し、良い近似値を返します。唯一の問題は、標準のsinf()、cosf()、およびatan2f()関数を呼び出すよりも、が遅いであることです。

だから、私は間違って何をやっていますか?

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

だけでなく、おそらく作り付けのものが手に最適化、およびPROBあります。可能な限り速く! –

+0

あなたは実際に組み込みのものがボトルネックだと決めましたか? –

答えて

9

「時期尚早の最適化は諸悪の根源である」 - ドナルド・クヌース

昨今のコンパイラを使用すると、ほとんど打つことができない理由を説明している、最新のプロセッサ(SSEなど)からベストを引き出す三角関数のために非常に効率的な組み込み関数を提供組み込み関数これらの部分で時間を浪費せず、代わりにプロファイラで検出できる実際のボトルネックに集中してください。

+1

引用:間違ってクヌスに起因します。情報のためのTHXを:http://en.wikipedia.org/wiki/C._A._R._Hoare – dirkgently

+0

@dirkgently - ホーアは、ホーアは、Wikipediaの記事によると、それを放棄しても、発信者のようです! – fbonnet

+0

クヌスは、それを印刷物に入れた最初の人物でした(彼の精神的な「GOTOを使った構造化プログラミング」)。 Wikipediaをソースとして引用すると、「interwebsのいくつかのランダムな人は....」と言います。 –

0

は、私がこの場所で心配している:

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

しかし、あなたがすることができます: は、すべての機能に時間メートルを追加し、特別なパフォーマンステスト、実行性能テスト、時間のテストの印刷レポートを書きます。私はあなたがこのテストの後に答えを知っていると思います。

また、あなたは、AQtimeはなど、いくつかのプロファイリングツールを使用することができます。

0

組み込み関数は既に非常に最適化されているので、それらを倒すことは本当に難しいでしょう。個人的には、パフォーマンスを得る場所が他にもあります。

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

を置き換えることができ:

angle = fmod(angle, TWO_PI); 
+0

番号。 angleには浮動小数点値があるためです。 – bayda

+0

おそらくfmod()を使用します。 – basszero

3

あなたはコプロセッサを持っている忘れないでください...あなたが見ているだろう1つの最適化、私はあなたのコードで見ることができる、と述べた

1993年ならスピードアップしましたが、今日ではネイティブのイントリンシックスを打ち負かすのに苦労します。

はSINFにdisasseblyを見てみてください。

関連する問題