1

私はかなり複雑な関数を持っており、緯度と経度がラジアンと角度である形式(大きさ、緯度、経度)の3空間で2つのベクトルを表すいくつかのdouble値を取ります。この関数の目的は、最初のベクトルを2番目のベクトルの周りで指定された角度だけ回転させ、結果のベクトルを返すことです。コードが論理的に正しく、機能することをすでに確認しました。

この関数の期待される目的はグラフィックスであるため、倍精度は必要ありません。ただし、ターゲットプラットフォームでは、floatを受け取るtrig(およびsqrt)関数(具体的にはsinf、cosf、atan2f、asinf、acosf、sqrtf)は、floatよりもdoubleで高速に動作します(おそらく、このような値を計算するための命令で実際にdouble; floatが渡された場合、値をdoubleにキャストする必要があります。これには、より多くのメモリがある領域(つまりオーバーヘッド)に値をコピーする必要があります。その結果、関数に含まれるすべての変数は倍精度になります。

問題は次のとおりです。1秒間に何度も呼び出すことができるように関数を最適化しようとしています。したがって、sin、cos、sqrtなどの呼び出しを、これらの関数の浮動小数点バージョンの呼び出しに置き換えました。これにより、全体で3〜4倍の速度が向上します。これはほとんどすべての入力で機能します。ただし、入力ベクトルが標準の単位ベクトル(i、j、またはk)とほぼ平行である場合、さまざまな関数の丸めエラーが蓄積され、後でsqrtfまたは逆三角関数(asinf、acosf、 atan2f)これらの関数のドメインのほんの少し外にある引数を渡すため。

したがって、このジレンマが残ります。倍精度関数を呼び出して問題を回避することしかできない(そして、1秒あたり約1,300,000のベクトル演算の制限になってしまう)か、別のことを考え出すことができます。最終的には、逆三角関数への入力をサニタイズしてエッジケースを処理する方法が必要です(sqrtの場合は簡単です。absを使用するだけです)。単一の条件ステートメントでさえ非常に多くのオーバーヘッドを追加するため、パフォーマンスの向上が失われるため、分岐はオプションではありません。

それで、何かアイデアはありますか?

編集:誰かが、doubleと浮動小数点演算の使用について混乱を表明しました。すべての値を実際にdoubleサイズのコンテナー(つまり、double型変数)に格納する場合は、floatサイズのコンテナーに格納する場合よりも、関数の方がはるかに高速です。ただし、明らかな理由により、浮動小数点精度の三角関数の演算は倍精度の三角関数の演算よりも高速です。

4

3 に答える 3

4

基本的に、問題を解決する数値的に安定したアルゴリズムを見つける必要があります。この種の一般的な解決策はありません。個々のステップの場合、条件数などの概念を使用して、特定のケースに対して実行する必要があります。そして、根本的な問題自体が悪条件である場合、それは実際には不可能かもしれません。

于 2010-11-13T07:51:50.510 に答える
4

単精度浮動小数点は本質的にエラーを引き起こします。したがって、イプシロン係数を使用してすべての比較である程度の「スロップ」が発生するように数学を構築する必要があります。また、ドメインが制限されている関数への入力をサニタイズする必要があります。

前者は、分岐するときに十分に簡単です。

bool IsAlmostEqual( float a, float b ) { return fabs(a-b) < 0.001f; } // or
bool IsAlmostEqual( float a, float b ) { return fabs(a-b) < (a * 0.0001f); } // for relative error

しかし、それは厄介です。ドメイン入力のクランプは少し注意が必要ですが、優れています。重要なのは、条件付き移動演算子を使用することです。これは、一般的に次のようなことを行います。

float ExampleOfConditionalMoveIntrinsic( float comparand, float a, float b ) 
{ return comparand >= 0.0f ? a : b ; }

ブランチを発生させることなく、単一の操作で。

これらはアーキテクチャによって異なります。x87浮動小数点ユニットでは、 FCMOV条件付き移動opを使用して実行できますが、以前に設定された条件フラグに依存するため、処理が遅くなります。また、cmovに固有の一貫したコンパイラはありません。これが、可能な場合はSSE2スカラー演算を優先してx87浮動小数点を回避する理由の1つです。

条件付き移動は、比較演算子をビット単位のANDとペアにすることにより、SSEではるかに適切にサポートされます。これは、スカラー数学の場合でも望ましいです。

// assuming you've already used _mm_load_ss to load your floats onto registers 
__m128 fsel( __m128 comparand, __m128 a, __m128 b ) 
{
    __m128 zero = {0,0,0,0};
    // set low word of mask to all 1s if comparand > 0
    __m128 mask = _mm_cmpgt_ss( comparand, zero );  
    a = _mm_and_ss( a, mask );    // a = a & mask 
    b = _mm_andnot_ss( mask, b ); // b = ~mask & b
    return _mm_or_ss( a, b );     // return a | b
    }
}

コンパイラは、SSE2スカラー演算が有効になっている場合に、この種のパターンを3値に出力することについては優れていますが、優れているわけではありません。/arch:sse2これは、MSVCまたは-mfpmath=sseGCCのコンパイラフラグを使用して行うことができます。

PowerPCおよび他の多くのRISCアーキテクチャでfsel()は、ハードウェアオペコードであるため、通常はコンパイラも組み込み関数です。

于 2010-11-13T08:04:45.440 に答える
1

グラフィックプログラミングブラックブックを見たことがありますか、それとも計算をGPUに渡しましたか?

于 2010-11-13T06:11:37.927 に答える