2013-04-16 25 views
7

我在做一些基準測試時遇到了這個問題。爲空比較而將結構體轉換爲對象不會導致裝箱?

bool b; 
MyStruct s; 
for (int i = 0; i < 10000000; i++) 
{ 
    b = (object)s == null; 
} 

調試:200毫秒

推出:5毫秒

bool b; 
MyStruct? s = null; 
for (int i = 0; i < 10000000; i++) 
{ 
    b = (object)s == null; 
} 

調試:800毫秒

推出:800毫秒

我可以理解這個結果,因爲將可空結構賦值給object給了我這個結構的盒裝類型。 但爲什麼不鑄造struct sobject做空比較(如在第一個方法)導致相同的性能?是否編譯器正在優化調用返回false總是作爲一個結構不能爲空?

+0

這種代碼不會編譯;錯誤1使用未分配的局部變量's'作爲第二個循環 – Fredou

+0

@Fredou沒錯。實際上是一個錯字。我會用一些基準更新我的答案 - 我發現我的計時錯誤 – nawfal

+0

這段代碼在編譯時似乎在運行空循環,你應該發佈你的實際基準代碼,因爲我沒有看到你如何得到7500ms – Fredou

回答

8

是的,編譯器正在優化它。

它知道一個結構永遠不能爲null,因此將其轉換爲對象的結果不能爲空,因此它只會在第一個示例中將b設置爲false。實際上,如果您使用Resharper,它會警告您表達式始終爲false。

對於第二個當然,可以爲空,所以它必須做檢查。

(您也可以使用Reflector檢查編譯器生成的IL代碼來驗證這一點。)

原來的測試代碼是不好的,因爲編譯器知道可空結構將永遠是零,因此也將優化該循環。不僅如此,在發佈版本中,編譯器意識到b未被使用,並優化了整個循環。

爲了防止這種情況,並表明會發生在更現實的代碼是什麼,測試它像這樣:

using System; 
using System.Diagnostics; 

namespace ConsoleApplication1 
{ 
    internal class Program 
    { 
     private static void Main(string[] args) 
     { 
      bool b = true; 
      MyStruct? s1 = getNullableStruct(); 
      Stopwatch sw = Stopwatch.StartNew(); 

      for (int i = 0; i < 10000000; i++) 
      { 
       b &= (object)s1 == null; // Note: Redundant cast to object. 
      } 

      Console.WriteLine(sw.Elapsed); 

      MyStruct s2 = getStruct(); 
      sw.Restart(); 

      for (int i = 0; i < 10000000; i++) 
      { 
       b &= (object)s2 == null; 
      } 

      Console.WriteLine(sw.Elapsed); 
     } 

     private static MyStruct? getNullableStruct() 
     { 
      return null; 
     } 

     private static MyStruct getStruct() 
     { 
      return new MyStruct(); 
     } 
    } 

    public struct MyStruct {} 
} 
+1

在這段代碼中,兩個循環都會有空體 – Fredou

+1

@Fredou我已經添加了一些明確的測試代碼來防止這個問題,所以這個正確地演示了它。 –

+1

其實複製你所需要的行爲就是'&='在原代碼中,不需要創建返回結構的方法 – Fredou

3

其實編譯當兩個環路將有一個空的身體!

使第二循環的行爲,您將不得不刪除(object)鑄造

這是個什麼樣子,當我編譯代碼,

public struct MyStruct 
{ 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     test1(); 
     test2(); 
    } 

    public static void test1() 
    { 
     Stopwatch sw = new Stopwatch(); 
     bool b; 
     MyStruct s; 
     for (int i = 0; i < 100000000; i++) 
     { 
      b = (object)s == null; 
     } 
     sw.Stop(); 
     Console.WriteLine(sw.ElapsedMilliseconds); 
     Console.ReadLine(); 
    } 

    public static void test2() 
    { 
     Stopwatch sw = new Stopwatch(); 
     bool b; 
     MyStruct? s = null; 
     for (int i = 0; i < 100000000; i++) 
     { 
      b = (object)s == null; 
     } 
     sw.Stop(); 
     Console.WriteLine(sw.ElapsedMilliseconds); 
     Console.ReadLine(); 
    } 
} 

IL:

的MYSTRUCT (空,因爲你沒有提供任何)

.class public sequential ansi sealed beforefieldinit ConsoleApplication1.MyStruct 
extends [mscorlib]System.ValueType 
{ 
    .pack 0 
    .size 1 

} // end of class ConsoleApplication1.MyStruct 

the fir在你的例子噸環

.method public hidebysig static 
void test1() cil managed 
{ 
// Method begins at RVA 0x2054 
// Code size 17 (0x11) 
.maxstack 2 
.locals init (
    [0] valuetype ConsoleApplication1.MyStruct s, 
    [1] int32 i 
) 

IL_0000: ldc.i4.0 
IL_0001: stloc.1 
IL_0002: br.s IL_0008 
// loop start (head: IL_0008) 
    IL_0004: ldloc.1 
    IL_0005: ldc.i4.1 
    IL_0006: add 
    IL_0007: stloc.1 

    IL_0008: ldloc.1 
    IL_0009: ldc.i4 100000000 
    IL_000e: blt.s IL_0004 
// end loop 

IL_0010: ret 
} // end of method Program::test1 

第二環路

.method public hidebysig static 
void test2() cil managed 
{ 
// Method begins at RVA 0x2074 
// Code size 25 (0x19) 
.maxstack 2 
.locals init (
    [0] valuetype [mscorlib]System.Nullable`1<valuetype ConsoleApplication1.MyStruct> s, 
    [1] int32 i 
) 

IL_0000: ldloca.s s 
IL_0002: initobj valuetype [mscorlib]System.Nullable`1<valuetype ConsoleApplication1.MyStruct> 
IL_0008: ldc.i4.0 
IL_0009: stloc.1 
IL_000a: br.s IL_0010 
// loop start (head: IL_0010) 
    IL_000c: ldloc.1 
    IL_000d: ldc.i4.1 
    IL_000e: add 
    IL_000f: stloc.1 

    IL_0010: ldloc.1 
    IL_0011: ldc.i4 100000000 
    IL_0016: blt.s IL_000c 
// end loop 

IL_0018: ret 
} // end of method Program::test2 
相關問題