C# Math.Atan2和FPATAN

C# Math.Atan2和FPATAN,c#,.net,x86,trigonometry,C#,.net,X86,Trigonometry,我正在用C#编写一些数学代码,迫使它在发行版中为x86编译,并进行了优化,我正在研究windbg中的反汇编。它通常相当不错,通常编写的汇编比我能写的要好(并不是说我在汇编方面很在行,但总算是这样了) 但是,我注意到这个函数: static void TemporaryWork() { double x = 4; double y = 3; double z = Math.Atan2(x, y); } 正在生成此分解: 001f0078 55 p

我正在用C#编写一些数学代码,迫使它在发行版中为x86编译,并进行了优化,我正在研究windbg中的反汇编。它通常相当不错,通常编写的汇编比我能写的要好(并不是说我在汇编方面很在行,但总算是这样了)

但是,我注意到这个函数:

static void TemporaryWork()
{
    double x = 4;
    double y = 3;
    double z = Math.Atan2(x, y);
}
正在生成此分解:

001f0078 55              push    ebp
001f0079 8bec            mov     ebp,esp
001f007b dd05a0001f00    fld     qword ptr ds:[1F00A0h]
001f0081 83ec08          sub     esp,8
001f0084 dd1c24          fstp    qword ptr [esp]
001f0087 dd05a8001f00    fld     qword ptr ds:[1F00A8h]
001f008d 83ec08          sub     esp,8
001f0090 dd1c24          fstp    qword ptr [esp]
001f0093 e86e9ba66f      call    clr!GetHashFromBlob+0x94e09 (6fc59c06) (System.Math.Atan2(Double, Double), mdToken: 06000de7)
001f0098 ddd8            fstp    st(0)
001f009a 5d              pop     ebp
001f009b c3              ret
即使您不是x86大师,您也会注意到其中的一些奇怪之处:调用System.Math.Atan2。在函数调用中

但实际上有一个x86操作码可以做到这一点:

当有实际的汇编指令执行操作时,为什么JITer调用函数?我认为System.Math基本上是本机汇编指令的包装器。其中的大多数操作都有直接汇编操作码。但情况显然不是这样


有人知道为什么JITer没有/不能执行这一非常明显的优化吗?

你可以从中找出原因,它显示了这些数学函数是如何通过jitter映射的

这将带您访问clr/src/classlibnative/float/comfloat.cpp,ComDouble::Atan2()函数。这就解释了原因:

   // the intrinsic for Atan2 does not produce Nan for Atan2(+-inf,+-inf)
   if (IS_DBL_INFINITY(x) && IS_DBL_INFINITY(y)) {
       return(x / y);      // create a NaN
   }
   return (double) atan2(x, y);

因此,这是修复不符合CLI的FPU行为的一种变通方法。

如果我在fpatan中搜索
错误,我会得到一些针对某些处理器型号的结果,因此可能是
Math。Atan2
解决了这些错误,并在
Math.Atan
中替代了它。我不是很确定,但也许这是应该寻找的方向?好问题。“有人知道为什么JITer没有/不能执行这种相当明显的优化吗?”我不会假装支持所讨论的JIT,但作为一个为多个平台实现了
atan2
的人,调用FPATAN指令显然是一种悲观。FPATAN比一个好的软件实现慢得多,甚至包括函数调用开销。(150-300个周期与50-100个周期,具体取决于所讨论的特定库和硬件)。我不会说这里有任何问题正在解决。这是一个适配器,而不是修复程序。啊,很酷,但根据来源,我不认为这一定是原因。看起来有很多函数没有映射到内部函数,但是它们没有做任何额外的事情。例如,COMDouble::Log实际上只是调用内部日志。在ecall.cpp中,只有sin、cos、sqrt、round和abs被直接映射到intrinsic。您还可以看到ComDouble::Exp和Log的代码,以及注释。它注意到它们的SSE2版本太慢,Exp也有类似的域问题。它们不使用intrisic,而是调用这些函数的CRT版本。英特尔手工编制的汇编代码。如果你有一个比非常明确的评论更好的理论,那么我们很乐意听到。我看到了这个评论,但它看起来像是在谈论fpfast和fpprecise。AFAIK.h中的数学函数是内在函数,至少在我所看到的所有平台中,它们都被编译器的操作码所取代(无可否认,这并不多)。据我所知,comfloat.cpp的数学函数(log、exp等)是从common.h引入的,它是从math引入的。我已经在几个地方提到过,它们不是本质函数。只需编写一个小的测试C程序就可以自己找到答案。