C# Double.IsNaN测试快100倍?

C# Double.IsNaN测试快100倍?,c#,floating-point,nan,C#,Floating Point,Nan,我在中发现了这一点:它声称比System.Double.IsNaN快100倍。是否有理由不使用此功能而不是System.Double.IsNaN [StructLayout(LayoutKind.Explicit)] private struct NanUnion { [FieldOffset(0)] internal double DoubleValue; [FieldOffset(0)] internal UInt64 UintValue; } // The standar

我在中发现了这一点:它声称比
System.Double.IsNaN
快100倍。是否有理由不使用此功能而不是
System.Double.IsNaN

[StructLayout(LayoutKind.Explicit)]
private struct NanUnion
{
    [FieldOffset(0)] internal double DoubleValue;
    [FieldOffset(0)] internal UInt64 UintValue;
}

// The standard CLR double.IsNaN() function is approximately 100 times slower than our own wrapper,
// so please make sure to use DoubleUtil.IsNaN() in performance sensitive code.
// PS item that tracks the CLR improvement is DevDiv Schedule : 26916.
// IEEE 754 : If the argument is any value in the range 0x7ff0000000000001L through 0x7fffffffffffffffL 
// or in the range 0xfff0000000000001L through 0xffffffffffffffffL, the result will be NaN.         
public static bool IsNaN(double value)
{
    NanUnion t = new NanUnion();
    t.DoubleValue = value;

    UInt64 exp = t.UintValue & 0xfff0000000000000;
    UInt64 man = t.UintValue & 0x000fffffffffffff;

    return (exp == 0x7ff0000000000000 || exp == 0xfff0000000000000) && (man != 0);
}
编辑:但是,
System.Double.IsNaN
的代码如下:

public unsafe static bool IsNaN(double d)
{
    return (*(UInt64*)(&d) & 0x7FFFFFFFFFFFFFFFL) > 0x7FF0000000000000L;
}

这里有一个简单的基准:

public static void Main()
{
    int iterations = 500 * 1000 * 1000;

    double nan = double.NaN;
    double notNan = 42;

    Stopwatch sw = Stopwatch.StartNew();

    bool isNan;
    for (int i = 0; i < iterations; i++)
    {
        isNan = IsNaN(nan);     // true
        isNan = IsNaN(notNan);  // false
    }

    sw.Stop();
    Console.WriteLine("IsNaN: {0}", sw.ElapsedMilliseconds);

    sw = Stopwatch.StartNew();

    for (int i = 0; i < iterations; i++)
    {
        isNan = double.IsNaN(nan);     // true
        isNan = double.IsNaN(notNan);  // false
    }

    sw.Stop();
    Console.WriteLine("double.IsNaN: {0}", sw.ElapsedMilliseconds);

    Console.Read();
}
publicstaticvoidmain()
{
整数迭代次数=500*1000*1000;
双nan=双nan;
双notNan=42;
秒表sw=Stopwatch.StartNew();
布尔伊斯南;
对于(int i=0;i
显然他们错了:

伊斯南:15012

double.IsNaN:6243


编辑+注意:我确信时间会根据输入值、许多其他因素等而变化,但声称一般来说,这个包装比默认实现快100倍似乎是错误的。

我称之为恶作剧。“快速”版本有相当多的操作,甚至从内存执行更多的读取(堆栈,所以在L1中,但仍然比寄存器慢)

与.NET版本相比:

            return (*(UInt64*)(&d) & 0x7FFFFFFFFFFFFFFFL) > 0x7FF0000000000000L;
00007FFAC53D3DE0  movsd       mmword ptr [rsp+8],xmm0  
00007FFAC53D3DE6  sub         rsp,38h  
00007FFAC53D3DEA  mov         rax,7FFAC5423D40h  
00007FFAC53D3DF4  mov         eax,dword ptr [rax]  
00007FFAC53D3DF6  test        eax,eax  
00007FFAC53D3DF8  je          00007FFAC53D3DFF  
00007FFAC53D3DFA  call        00007FFB24EE39F0  
00007FFAC53D3DFF  mov         rdx,qword ptr [rsp+40h]  
00007FFAC53D3E04  mov         rax,7FFFFFFFFFFFFFFFh  
00007FFAC53D3E0E  and         rdx,rax  
00007FFAC53D3E11  xor         ecx,ecx  
00007FFAC53D3E13  mov         rax,7FF0000000000000h  
00007FFAC53D3E1D  cmp         rdx,rax  
00007FFAC53D3E20  seta        cl  
00007FFAC53D3E23  mov         dword ptr [rsp+20h],ecx  
00007FFAC53D3E27  movzx       eax,byte ptr [rsp+20h]  
00007FFAC53D3E2C  jmp         00007FFAC53D3E2E  
00007FFAC53D3E2E  nop  
00007FFAC53D3E2F  add         rsp,38h  
00007FFAC53D3E33  ret  
它声称比System.Double.IsNaN快100倍

是的,以前是这样。你错过了知道这个决定是何时做出的时间机器。Double.IsNaN()以前不是这样的。从SSCLI10源代码:

   public static bool IsNaN(double d)
   {
       // Comparisions of a NaN with another number is always false and hence both conditions will be false.
       if (d < 0d || d >= 0d) {
          return false;
       }
       return true;
   }
它使用了经过微优化的Double.IsNaN()版本。这种微优化在框架中并不是坏事,顺便说一句,微软.NET程序员的巨大负担是他们很少能猜到他们的代码何时在应用程序的关键路径上

针对32位代码(Haswell mobile core)时在我的机器上的结果:


嗯,评论说它更快,所以它可能更快。。。您是否对其进行了基准测试?这是一个有趣的问题(尤其是“有什么理由使用慢一点的吗?”部分。在不知道任何关于NaN测试或此方法正确性的情况下,我建议坚持使用.NET实现,前提是存在性能差异的充分理由。NET开发人员知道他们在做什么,并且必须考虑所有的边缘情况等,以生成健壮的库。Well、 那个类是
内部的
,所以你最终会复制源代码并维护它以备将来使用。@paqogomez不是真的。这是一个有趣的问题。@paqogomez我读这篇引文的次数,我开始讨厌它了…这不是这里的主题。这也是我注意到的。即使在一百万次迭代之后,
double.IsNaN
的速度实际上是.NET 1.1的两倍。我想知道该注释是来自.NET 1.1还是其他地方。@TyCobb我用更多的迭代更新了结果,以反映您提到的“两倍的速度”。另外+1对于.NET 1.1的注释,
double.IsNaN
在过去可能没有那么快。+1我做了同样的事情。我的测试(发布模式,无调试器)显示
double.IsNan
的速度接近10倍。注释没有编译。在阅读此答案时,我在想,谁他妈的知道这么多东西,然后我看到了海报名称。+1感谢您为我们提供了如此精彩的答案必须在C、64位、2.4 GHz的Core 2 Duo上进行测量:这是最简单的比较方法(d!=d)在2.7纳秒内给出正确的结果。比较!(d<0 | | d>=0)在3.5纳秒内给出正确的结果。建议的位模式测试需要4.7纳秒(所有测量都超过100亿次迭代)。所有测量都关闭了优化,否则所有比较都会被优化。嗯,“优化已关闭”不会产生有意义的数字。请注意我发布的代码段中result1和result2的奇怪用法,包括在控制台中使用它们。WriteLine()调用没有实际显示它们。基准测试是一门棘手的艺术:)我们讨论的是每个循环占用6到12个时钟周期的代码。再加上假设浮点比较在涉及NAN的情况下速度慢得离谱。所以这个测试是绝对有意义的。Core 2 Duo处理器在比较NAN时不会慢下来,Clang不会优化比较x==x,测试(x!=x)是检查数字是否为NaN的最快方法。不需要技巧。叹气。不要将SSE2与FPU代码进行比较。我已经注意到.NET中的x64抖动没有这个问题。
   public static bool IsNaN(double d)
   {
       // Comparisions of a NaN with another number is always false and hence both conditions will be false.
       if (d < 0d || d >= 0d) {
          return false;
       }
       return true;
   }
using System;
using System.Diagnostics;

class Program {
    static void Main(string[] args) {
        double d = double.NaN;
        for (int test = 0; test < 10; ++test) {
            var sw1 = Stopwatch.StartNew();
            bool result1 = false;
            for (int ix = 0; ix < 1000 * 1000; ++ix) {
                result1 |= double.IsNaN(d);
            }
            sw1.Stop();
            var sw2 = Stopwatch.StartNew();
            bool result2 = false;
            for (int ix = 0; ix < 1000 * 1000; ++ix) {
                result2 |= IsNaN(d);
            }
            sw2.Stop();
            Console.WriteLine("{0} - {1} x {2}%", sw1.Elapsed, sw2.Elapsed, 100 * sw2.ElapsedTicks / sw1.ElapsedTicks, result1, result2);

        }
        Console.ReadLine();
    }
    public static bool IsNaN(double d) {
        // Comparisions of a NaN with another number is always false and hence both conditions will be false.
        if (d < 0d || d >= 0d) {
            return false;
        }
        return true;
    }
}
00:00:00.0027095 - 00:00:00.2427242 x 8957%
00:00:00.0025248 - 00:00:00.2191291 x 8678%
00:00:00.0024344 - 00:00:00.2209950 x 9077%
00:00:00.0024144 - 00:00:00.2321169 x 9613%
00:00:00.0024126 - 00:00:00.2173313 x 9008%
00:00:00.0025488 - 00:00:00.2237517 x 8778%
00:00:00.0026940 - 00:00:00.2231146 x 8281%
00:00:00.0025052 - 00:00:00.2145660 x 8564%
00:00:00.0025533 - 00:00:00.2200943 x 8619%
00:00:00.0024406 - 00:00:00.2135839 x 8751%