甚至无法获得内联的简单方法(?)

时间:2012-06-21 02:55:04

标签: c# visual-studio-2010 .net-4.0 inline

从逻辑上讲,我总是认为C#编译器可以使用非常简短的方法,因此只需手动输入方法中的代码就不会显示任何开销......

直到今天 - 当我尝试对各种方法进行基准测试并手动内联代码时。事实证明,对于我来说,即使最简单的代码也会显示一个方法调用开销,与其手动内联对应相比 实际上,我找不到关于内联任何方法的线索 - 所以我进行了一个简单的测试。

使用的系统:

  • Intel C2D E7200(双核)2.53GHz
  • 4GB DDR2
  • Windows 7 64位
  • .NET 4.0
  • Visual Studio 2010 Ultimate

所有测试都是没有调试并使用发布配置(优化代码)执行。

以下是我用来进行基准测试的代码:

static void Main()
{
    const int iterations = 250000000; // 250 million iterations
    Thread.Sleep(1000); // sleep for one second
    var sw = new Stopwatch();

    int s = 0;
    sw.Start();
    for (int i = 0; i < iterations; i++)
    {
        // incrementing s by 1 in various ways
    }
    sw.Stop();

    Console.WriteLine("Time: {0}ms", sw.ElapsedMilliseconds);
}

[1] 首先,我只是简单地对一个简单的增量命令进行基准测试:

// in Main
for (int i = 0; i < iterations; i++)
{
    s = s + 1;
}

5次运行的结果:

  1. 867ms
  2. 877ms
  3. 868ms
  4. 865ms
  5. 870ms

  6. [2] 切换到方法调用:

    static int Increment(int a)
    {
        return a + 1;
    }
    

    ...

    // in Main
    for (int i = 0; i < iterations; i++)
    {
        s = Increment(s);
    }
    

    5次运行的结果:

    1. 2161ms
    2. 2159ms
    3. 2194ms
    4. 2177ms
    5. 2163ms
    6. 哎哟!显然,该方法的开销很大。

      我尝试使用反射,并在MethodBase.GetCurrentMethod().Name方法中打印Increment;确实打印Increment - 意味着该方法没有内联。

      接下来,我尝试将[MethodImpl(MethodImplOptions.NoInlining)]属性添加到方法 - ,但基准时间保持完全相同

      在调试模式下,将Optimize Code设置为false,第一个测试稍慢,而第二个测试慢两倍;而 NoInlining 属性再次影响性能。


      我在这里做错了什么,即使这样一个简单的方法没有开销也无法工作?为什么会这样?
      当然,这不是预期的行为 - 或者是它?

      注意:Java中的类似测试显示此类方法调用没有开销。 (使用Eclipse + JDK 1.7,Java似乎也 A LOT 更快。)

1 个答案:

答案 0 :(得分:10)

如果从Visual Studio中运行程序,请确保使用 Start Without Debugging 命令;否则,可能会禁用内联等一些优化。

如果我首先使用 Start Without Debugging 命令来运行程序,然后附加调试器并查看for循环的x86反汇编,无论循环直接递增s还是调用Increment,我都会得到同样的结果;也就是说,方法调用是内联的:

00000049  xor         eax,eax
0000004b  inc         ebx
0000004c  inc         eax
0000004d  cmp         eax,0EE6B280h
00000052  jl          0000004B

相反,如果我使用 Start Debugging 命令来运行程序,则不会内联方法调用:

00000060  xor         edx,edx
00000062  mov         dword ptr [ebp-0Ch],edx
00000065  nop
00000066  jmp         0000007D
00000068  mov         ecx,dword ptr [ebp-8]
0000006b  call        dword ptr ds:[00801F50h]
00000071  mov         dword ptr [ebp-10h],eax
00000074  mov         eax,dword ptr [ebp-10h]
00000077  mov         dword ptr [ebp-8],eax
0000007a  inc         dword ptr [ebp-0Ch]
0000007d  cmp         dword ptr [ebp-0Ch],0EE6B280h
00000084  jl          00000068