2013-04-16 33 views
7

Bazı kıyaslama yaparken buna rastladım.Boş karşılaştırma için nesneye dökülecek yapı boksa neden olmaz?

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

ayıklama: 200 ms

Yayın: 5 ms

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

ayıklama: 800 ms

Yayın: 800 ms

Bu sonucu anlayabilen null yapıyı object'a gönderdiğimden bu kutunun bir kutulu tipini verdiğini anlayabiliyorum. Ancak, null karşılaştırma yapmak için struct sobject'a neden basma (ilk yöntemdeki gibi) aynı performansa neden olur? Bu derleyici, false numaralı çağrıyı her zaman bir struct boş olamazken döndürecek şekilde optimize ediyor mu?

+0

bu kod derlemek olmaz; Hata 1 İkinci döngü için atanmamış yerel değişken 's' kullanımı – Fredou

+0

@Fredou Bu doğru. Bir yazım hatası aslında. Cevabımı bazı kıyaslamalarla güncelleyeceğim - zamanlamada – nawfal

+0

kodunun bir hata buldum, derlendiğinde, boş döngü çalışıyormuş gibi görünüyorsun, gerçek benchmark kodunuzu yayınlamalısınız çünkü 7500ms – Fredou

cevap

8

Evet, derleyici onu iyileştiriyor.

Bir yapının hiçbir zaman boş olamayacağını bilir, bu nedenle bir nesneyi bir nesneye dönüştürmenin sonucu asla boş olamaz - bu nedenle ilk örnekte b değerini false olarak ayarlar. Aslında, Resharper kullanırsanız, bu ifadenin her zaman yanlış olduğunu bildirir.

İkincisi için, bir null imi boş olabilir, bu yüzden kontrolü yapmak zorundadır.

(Ayrıca bu doğrulamak için derleyici tarafından oluşturulan IL kodlarını incelemek için Reflector kullanabilirsiniz.) Derleyici null yapı her zaman boş ve irade dolayısıyla da olacağını bildiği için

orijinal test kodu iyi değil Bu döngüyü en iyi duruma getir. Sadece bu değil, bir sürüm oluştururken derleyici, b'un kullanılmadığını ve tüm döngüyü en iyi duruma getirdiğini fark eder.

Bunu önlemek için, ve daha gerçekçi kodunda meydana şöyle test ne olacağını göstermek için: derlenmiş zaman

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

, her iki döngü boş gövdeye sahip olacak – Fredou

+1

@Fredou Bu sorunu önlemek için bazı açık sınama kodları ekledim, bu yüzden bunu doğru şekilde gösterir. –

+1

aslında tüm ihtiyacınız olan davranışı çoğaltmak için orijinal kodda '& = '= – Fredou

3

aslında hem döngü boş vücuda sahip olacak!

, sen (object) bu ben senin kod derleme yaparken böyle görünüyor

döküm çıkarmak zorunda kalacak, ikinci döngü davranacağınızı yapmak

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 (sağladığınızdan beri boş)

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

} // end of class ConsoleApplication1.MyStruct 

sizin örnekte t döngü

.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 

ikinci döngü

.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 
İlgili konular