高速な平方根などを行うためのトリックは、精度を犠牲にすることでパフォーマンスを得ています。(まぁ、そのほとんどです。)
double
本当に精度が必要ですか?精度を簡単に犠牲にすることができます:
double d = somevalue();
float c = 1.0f / ((float) d * (float) d);
この1.0f
場合、 は絶対に必須です。1.0
代わりに使用すると、double
精度が得られます。
コンパイラで「ずさんな」計算を有効にしてみましたか? を使用できる GCC-ffast-math
では、他のコンパイラにも同様のオプションがあります。ずさんな計算は、アプリケーションには十分すぎるかもしれません。(編集:結果のアセンブリに違いは見られませんでした。)
GCC を使用している場合、使用を検討しました-mrecip
か? 約 12 ビットの精度しかない「逆数推定」機能がありますが、はるかに高速です。Newton-Raphson 法を使用して、結果の精度を上げることができます。この-mrecip
オプションを指定すると、逆数推定とニュートン ラフソン ステップがコンパイラによって自動的に生成されますが、パフォーマンスと精度のトレードオフを微調整したい場合は、いつでも自分でアセンブリを作成できます。(Newton-Raphsonは非常に迅速に収束します。) (編集: GCC で RCPSS を生成できませんでした。以下を参照してください。)
あなたが経験している正確な問題について議論しているブログ投稿 ( source ) を見つけました。著者の結論は、Carmack メソッドのような手法は RCPSS 命令 ( -mrecip
GCC のフラグが使用する) と競合しないというものです。
除算が非常に遅くなる理由は、通常、プロセッサには除算ユニットが 1 つしかなく、パイプライン化されていないことが多いためです。したがって、パイプ内でいくつかの乗算をすべて同時に実行できますが、前の除算が終了するまで除算を発行することはできません。
うまくいかないトリック
カーマックの方法: 相反推定オペコードを持つ最近のプロセッサでは時代遅れです。逆数の場合、私が見た中で最高のバージョンは 1 ビットの精度しか提供しません。12 ビットのRCPSS
. このトリックが平方根の逆数でうまく機能するのは偶然だと思います。二度とない偶然。
変数の再ラベル付け。コンパイラに関する限り、 と の間にほとんど違いはありませ1.0/(x*x)
んdouble x2 = x*x; 1.0/x2
。最適化を最低レベルまでオンにして、2 つのバージョンで異なるコードを生成するコンパイラを見つけたら、私は驚かれることでしょう。
を使用してpow
います。pow
ライブラリ機能は総モンスターです。GCC-ffast-math
がオフになっていると、ライブラリの呼び出しはかなり高価になります。GCC を-ffast-math
オンにすると、 の場合とまったく同じアセンブリ コードが得pow(x, -2)
られるため1.0/(x*x)
、メリットはありません。
アップデート
倍精度浮動小数点値の逆二乗のニュートン ラフソン近似の例を次に示します。
static double invsq(double x)
{
double y;
int i;
__asm__ (
"cvtpd2ps %1, %0\n\t"
"rcpss %0, %0\n\t"
"cvtps2pd %0, %0"
: "=x"(y)
: "x"(x));
for (i = 0; i < RECIP_ITER; ++i)
y *= 2 - x * y;
return y * y;
}
残念ながら、RECIP_ITER=1
私のコンピューターのベンチマークでは、単純なバージョンよりもわずかに遅くなりました (~5%) 1.0/(x*x)
。反復がゼロの場合は高速 (2 倍) ですが、12 ビットの精度しか得られません。12ビットで十分かどうかはわかりません。
ここでの問題の 1 つは、マイクロ最適化が小さすぎることだと思います。この規模では、コンパイラの作成者はアセンブリ ハッカーとほぼ同等の立場にあります。より大きな全体像があれば、それを高速化する方法がわかるかもしれません。
たとえば、それ-ffast-math
が望ましくない精度の低下を引き起こしたとあなたは言いました。これは、使用しているアルゴリズムの数値安定性の問題を示している可能性があります。float
アルゴリズムを正しく選択すれば、多くの問題はの代わりに解決できますdouble
。(もちろん、24 ビット以上が必要な場合もあります。わかりません。)
RCPSS
これらのいくつかを並行して計算したい場合、この方法が優れていると思います。