2012-06-21 40 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

相關問題