私はa b (と としますa = 2
)を計算するための効率的な方法を探していましたb = 50
。まずはMath.Pow()
関数の実装を見てみることにしました。しかし、.NET リフレクター私が見つけたのはこれだけです:
[MethodImpl(MethodImplOptions.InternalCall), SecuritySafeCritical]
public static extern double Pow(double x, double y);
関数を呼び出したときに内部で何が起こっているかを確認できるリソースにはどのようなものがありますかMath.Pow()
?
ベストアンサー1
MethodImplOptions.InternalCall
つまり、メソッドは実際には C++ で記述された CLR に実装されています。ジャストインタイム コンパイラは、内部的に実装されたメソッドのテーブルを参照し、C++ 関数への呼び出しを直接コンパイルします。
コードを見るにはCLRのソースコードが必要です。これは以下から入手できます。SSCLI20 分布これは .NET 2.0 の頃に書かれたものですが、低レベルの実装はMath.Pow()
CLR のそれ以降のバージョンでもほぼ正確であることがわかりました。
ルックアップ テーブルは clr/src/vm/ecall.cpp にあります。関連するセクションはMath.Pow()
次のようになります。
FCFuncStart(gMathFuncs)
FCIntrinsic("Sin", COMDouble::Sin, CORINFO_INTRINSIC_Sin)
FCIntrinsic("Cos", COMDouble::Cos, CORINFO_INTRINSIC_Cos)
FCIntrinsic("Sqrt", COMDouble::Sqrt, CORINFO_INTRINSIC_Sqrt)
FCIntrinsic("Round", COMDouble::Round, CORINFO_INTRINSIC_Round)
FCIntrinsicSig("Abs", &gsig_SM_Flt_RetFlt, COMDouble::AbsFlt, CORINFO_INTRINSIC_Abs)
FCIntrinsicSig("Abs", &gsig_SM_Dbl_RetDbl, COMDouble::AbsDbl, CORINFO_INTRINSIC_Abs)
FCFuncElement("Exp", COMDouble::Exp)
FCFuncElement("Pow", COMDouble::Pow)
// etc..
FCFuncEnd()
「COMDouble」を検索すると、clr/src/classlibnative/float/comfloat.cpp にアクセスできます。コードは省略しますので、ご自身で確認してください。基本的には、コーナー ケースをチェックし、CRT バージョンの を呼び出しますpow()
。
興味深い実装の詳細は、表の FCIntrinsic マクロだけです。これは、ジッターが関数を組み込み関数として実装する可能性があることを示唆しています。言い換えると、関数呼び出しを浮動小数点マシンコード命令に置き換えます。これは には当てはまりません。Pow()
これには FPU 命令がありません。しかし、他の単純な操作では確かに当てはまります。注目すべきは、これにより、C# での浮動小数点演算が C++ での同じコードよりも大幅に高速化される可能性があることです。この答えその理由について。
ちなみに、Visual Studio の vc/crt/src ディレクトリのフル バージョンをお持ちの場合は、CRT のソース コードも入手できます。pow()
ただし、Microsoft は Intel からそのコードを購入したため、壁にぶつかることになります。Intel のエンジニアよりも優れた仕事をすることはまずありません。ただし、高校の教科書の ID を試してみたところ、2 倍の速さでした。
public static double FasterPow(double x, double y) {
return Math.Exp(y * Math.Log(x));
}
しかし、これは真の代替手段ではありません。なぜなら、3 つの浮動小数点演算からエラーが蓄積され、Pow() が抱える奇妙なドメイン問題 (0^0 や -Infinity の任意の累乗など) に対処できないからです。