Why the execution time of a million double-int conversion is the same as an empty loop?

前端 未结 1 1425
暗喜
暗喜 2021-01-27 21:48

I\'m writing a high-performance component with a lot of int-double-int conversions, so I need to know the execution time between them.

static double ToDouble(int         


        
相关标签:
1条回答
  • 2021-01-27 22:34

    You should use StopWatch.Start and Stop then Elapsed!

    const int TIMES = 100_000_000;
    
    var chrono = new Stopwatch();
    int val = 0;
    
    chrono.Start();
    for ( int i = 1; i <= TIMES; i++ )
      val = ToInt(ToDouble(i));
    chrono.Stop();
    Console.WriteLine(val);
    Console.WriteLine(chrono.ElapsedMilliseconds.ToString());
    
    chrono.Restart();
    for ( int i = 1; i <= TIMES; i++ )
    {
      var v1 = (double)i;
      val = (int)v1;
    }
    chrono.Stop();
    Console.WriteLine(val);
    Console.WriteLine(chrono.ElapsedMilliseconds.ToString());
    
    chrono.Restart();
    for ( int i = 1; i <= TIMES; i++ )
      val = i;
    chrono.Stop();
    Console.WriteLine(val);
    Console.WriteLine(chrono.ElapsedMilliseconds.ToString());
    
    chrono.Restart();
    for ( int i = 1; i <= TIMES; i++ )
      ;
    chrono.Stop();
    Console.WriteLine(chrono.ElapsedMilliseconds.ToString());
    

    Output with Debug mode:

    729
    270
    194
    218
    

    Using Release build optimized:

    84
    61
    57
    31
    

    The first loop in Debug IL:

      // value = ToInt(ToDouble(i));
      IL_0015: ldloc.2
      IL_0016: call float64 ConsoleApp.Program::ToDouble(int32)
      IL_001b: call int32 ConsoleApp.Program::ToInt(float64)
      IL_0020: stloc.1
    

    The first loop in Release IL:

      // value = ToInt(ToDouble(i));
      IL_0012: ldloc.2
      IL_0013: call float64 ConsoleApp.Program::ToDouble(int32)
      IL_0018: call int32 ConsoleApp.Program::ToInt(float64)
      IL_001d: stloc.1
    

    The second loop Debug:

      // double num = j;
      IL_0065: ldloc.s 5
      IL_0067: conv.r8
      IL_0068: stloc.s 6
      // value = (int)num;
      IL_006a: ldloc.s 6
      IL_006c: conv.i4
      IL_006d: stloc.1
    

    The second loop Release:

      // value = (int)(double)j;
      IL_0054: ldloc.s 4
      IL_0056: conv.r8
      IL_0057: conv.i4
      IL_0058: stloc.1
    

    Proc calls eat a lot of CPU ticks and this is the first thing to consider when optimizing, with loops and calculation.

    The compiler optimization is mainly with the loop itself:

    • Debug:
    // for (int i = 1; i <= 100000000; i++)
    IL_0010: ldc.i4.1
    IL_0011: stloc.2
    // (no C# code)
    IL_0012: br.s IL_0026
    // loop start (head: IL_0026)
      //...
      // for (int i = 1; i <= 100000000; i++)
      IL_0022: ldloc.2
      IL_0023: ldc.i4.1
      IL_0024: add
      IL_0025: stloc.2
      // for (int i = 1; i <= 100000000; i++)
      IL_0026: ldloc.2
      IL_0027: ldc.i4 100000000
      IL_002c: cgt
      // (no C# code)
      IL_002e: ldc.i4.0
      IL_002f: ceq
      IL_0031: stloc.3
      IL_0032: ldloc.3
      IL_0033: brtrue.s IL_0014
    // end loop
    
    • Release:
    // for (int i = 1; i <= 100000000; i++)
    IL_000c: ldc.i4.1
    IL_000d: stloc.1
    // (no C# code)
    IL_000e: br.s IL_0020
    // loop start (head: IL_0020)
      // ...
      // for (int i = 1; i <= 100000000; i++)
      IL_001c: ldloc.1
      IL_001d: ldc.i4.1
      IL_001e: add
      IL_001f: stloc.1
      // for (int i = 1; i <= 100000000; i++)
      IL_0020: ldloc.1
      IL_0021: ldc.i4 100000000
      IL_0026: ble.s IL_0010
    // end loop
    

    The loops in debug without the console.writeline(val):

      // value = ToInt(ToDouble(i));
      IL_0015: ldloc.2
      IL_0016: call float64 ConsoleApp.Program::ToDouble(int32)
      IL_001b: call int32 ConsoleApp.Program::ToInt(float64)
      IL_0020: stloc.1
    
      // double num = j;
      IL_0065: ldloc.s 5
      IL_0067: conv.r8
      IL_0068: stloc.s 6
      // value = (int)num;
      IL_006a: ldloc.s 6
      IL_006c: conv.i4
      IL_006d: stloc.1
    
      // value = k;
      IL_00b7: ldloc.s 8
      IL_00b9: stloc.1
    
      // nothing
    

    The loops in release without the console.writeline(val):

      // ToInt(ToDouble(i));
      IL_0010: ldloc.1
      IL_0011: call float64 ConsoleApp.Program::ToDouble(int32)
      IL_0016: call int32 ConsoleApp.Program::ToInt(float64)
      IL_001b: pop
    
      // _ = (double)j;
      IL_004b: ldloc.3
      IL_004c: conv.r8
      IL_004d: pop
    
      // nothing
    
      // nothing
    
    0 讨论(0)
提交回复
热议问题