私は自分がタイプしていることに気づきます
double foo=1.0/sqrt(...);
多くの場合、最近のプロセッサには逆平方根オペコードが組み込まれていると聞きました。
CまたはC++標準ライブラリの逆平方根関数はありますか?
- 倍精度浮動小数点を使用しますか?
- と同じくらい正確
1.0/sqrt(...)
ですか? - ?の結果と同じかそれよりも速い
1.0/sqrt(...)
ですか?
いいえ、ありません。C++ではありません。いいえ。
この関数を使用して、逆平方根計算を高速化できます
。ウィキペディアにその仕組みに関する記事があります。https
://en.wikipedia.org/wiki/Fast_inverse_square_root
このアルゴリズムのCバージョンもあります。
float invSqrt( float number ){
union {
float f;
uint32_t i;
} conv;
float x2;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
conv.f = number;
conv.i = 0x5f3759df - ( conv.i >> 1 );
conv.f = conv.f * ( threehalfs - ( x2 * conv.f * conv.f ) );
return conv.f;
}
このための標準化されたCAPIはわかりませんが、プラットフォームに依存する組み込み関数を記述できる限り、高速逆平方根命令を使用できないという意味ではありません。
たとえば、AVXで64ビットx86を取り上げます。ここでは、 _mm256_rsqrt_ps()を使用して、平方根の逆数を概算できます。または、より具体的には、SIMDを使用して、一度に8平方根を使用します。
#include <immintrin.h>
...
float inputs[8] = { ... } __attribute__ ((aligned (32)));
__m256 input = _mm256_load_ps(inputs);
__m256 invroot = _mm256_rsqrt_ps(input);
同様に、NEONを使用するARMで組み込みのvrsqrteq_f32を使用できます。この場合、SIMDは4幅であるため、一度に4つの逆平方根を計算します。
#include <arm_neon.h>
...
float32x4_t sqrt_reciprocal = vrsqrteq_f32(x);
バッチごとに1つのルート値だけが必要な場合でも、完全な平方根よりも高速です。SIMDレジスタのすべてまたは1つのレーンに入力を設定するだけです。そうすれば、ロード操作でメモリを調べる必要がなくなります。x86では、を介して実行され_mm256_set1_ps(x)
ます。
制約1.および2.に違反します(これも標準ではありません)が、それでも誰かが閲覧するのに役立つ可能性があります...
ASMJITを使用して、探している正確なアセンブリ操作をジャストインタイムでコンパイルしました:(単精度、わかりましRSQRTSS
たが、doubleと同様である必要があります)。
私のコードはこれです(別の投稿の私の答えも参照してください):
typedef float(*JITFunc)();
JITFunc func;
asmjit::JitRuntime jit_runtime;
asmjit::CodeHolder code;
code.init(jit_runtime.getCodeInfo());
asmjit::X86Compiler cc(&code);
cc.addFunc(asmjit::FuncSignature0<float>());
float value = 2.71; // Some example value.
asmjit::X86Xmm x = cc.newXmm();
uint32_t *i = reinterpret_cast<uint32_t*>(&value);
cc.mov(asmjit::x86::eax, i[0]);
cc.movd(x, asmjit::x86::eax);
cc.rsqrtss(x, x); // THE asm function.
cc.ret(x);
cc.endFunc();
cc.finalize();
jit_runtime.add(&func, &code);
// Now, func() can be used as the result to rsqrt(value).
JITコンパイル部分を1回だけ実行し、後で異なる値で呼び出す場合、これはより高速になるはずです(ただし、精度は少し劣りますが、これは、説明している組み込み操作に固有のものです)1.0/sqrt(...)
。
独自の関数を使用することを恐れていない場合は、次のことを試してください。
template <typename T>
T invsqrt(T x)
{
return 1.0 / std::sqrt(x);
}
1.0 / std::sqrt(x)
これは、最新の最適化コンパイラの元のコンパイラと同じくらい高速である必要があります。また、ダブルまたはフロートで使用できます。
同じことを何度も書いていることに気付いた場合は、自分自身に「機能」を考えるべきです。
double invsqrt(const double x)
{
return 1.0 / std::sqrt(x);
}
これで、コードはより自己文書化されます。人々は逆平方根であると推測する必要はなく、それを 読みます。さらに、必要な実装をプラグインできるようになり、各呼び出しサイトは更新された定義を自動的に使用します。1.0 / std::sqrt(x)
あなたの質問に答えるために、いいえ、そのためのC(++)関数はありませんが、パフォーマンスが不足していることがわかった場合は、C(++)関数を作成したので、独自の定義に置き換えることができます。
これを試してみませんか?#define INSQRT(x) (1.0/sqrt(x))
同じように高速で、入力が少なくて済み(関数のように感じられます)、倍精度を使用し、1 / sqrt(..)と同じくらい正確です。