2012-06-21 34 views
3

从逻辑上讲,我总是认为非常简短的方法会被C#编译器内联,因此与简单地在代码中输入代码相比,不会显示任何开销手工方法...甚至没有简单的方法来内联(?)

直到今天 - 当我试图基准各种方法和手动内联代码。结果是(对我来说)即使是最简单的代码也会显示一个方法调用开销,而不是手动内联对手。
其实,我找不到任何线索任何方法被内联 - 所以我跑了一个简单的测试。使用

系统:

  • 英特尔C2D E7200(双核)2.53GHz的
  • 4GB DDR2
  • 64位Windows 7
  • .NET 4.0
  • 的Visual Studio 2010旗舰版

所有的测试都进行了而没有得到g和使用版本配置(优化代码)

下面是我用于基准的代码:

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]首先,我已经简单地基准一个简单的增量命令:从5次

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

结果:

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

[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

哎哟!显然这个方法有一个开销。

我试过使用反射,并从Increment方法中打印MethodBase.GetCurrentMethod().Name;它确实在打印Increment - 意味着该方法不是内联的。

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

在调试模式下,如果将优化代码设置为false,则第一个测试稍慢,而第二个测试慢两倍;并且否内联属性不会影响性能。


上午我在这里做得不对,我无法让即使是这样一个简单的方法,没有工作的开销?这是为什么发生?
当然,这不能被期望的行为 - 或者它?

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

+0

注意,在.NET 4.5中,你可以强制编译器尝试并内联你的方法:http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.methodimploptions%28v=VS.110%29.aspx – Polity

回答

10

如果你正在运行在Visual Studio中的程序,请确保您使用不开始调试命令;否则,可能会禁用某些内嵌优化。

如果我先用不开始调试命令来运行程序,然后附加调试器,并期待在for循环的86拆卸,我得到同样的事情是否循环增值s直接或来电Increment;也就是说,方法调用内联:

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

相反,如果我用启动调试命令来运行该程序,然后调用该方法不内联:

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 
+0

事实上,我正在使用*开始无调试*命令(Ctrl + F5)。它显示VS不会进入调试视图。另外,如何查看由VM编译的程序的x86反汇编? – Acidic

+0

下面是我所做的:我在'Main'的开头放置了一个'Console.ReadLine()'调用,在'for'循环中设置了一个断点,并使用* Start Without Debugging *命令。然后我使用* Attach to Process *命令开始调试,并在控制台窗口中按下Enter键。当遇到断点时,我进入了Debug> Windows> Disassembly。 –

+0

说实话,仍然没有帮助我。事实是,有一个我无法摆脱的开销 - 一个不应该在那里的人。 – Acidic

相关问题