2013-04-16 3 views
7

ベンチマークをしながらこれを見つけました。null比較のためにオブジェクトに構造体をキャストしてもボクシングが発生していませんか?

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

デバッグ:200のMS

リリース:5ミリ秒

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

デバッグ:800のMS

発売:800のMS

null可能な構造体をobjectにキャストすると、私はその構造体のボックス型を得ることができます。 しかし、同じパフォーマンスでnull比較(最初の方法のように)を実行すると、struct sobjectがキャスティングされないのはなぜですか?そのコンパイラは、構造体がnullにはならないので、常にfalseを返すように呼び出しを最適化していますか?

+0

このコードはコンパイルされません。エラー1 2番目のループに割り当てられていないローカル変数 's'の使用 – Fredou

+0

@Fredouそうです。実際にはタイプミス。私はいくつかのベンチマークで私の答えを更新します - 私は自分のタイミングで間違いを見つけました – nawfal

+0

このコードは、コンパイルされたときに空のループを実行しているようですが、7500msを得る方法がわからないので実際のベンチマークコードを投稿する必要があります – Fredou

答えて

8

はい、コンパイラが最適化しています。

structがnullになることはないことを知っています。そのため、オブジェクトにキャストした結果はnullになることはありません。最初のサンプルでbをfalseに設定するだけです。実際、Resharperを使用すると、式が常にfalseであることが警告されます。

もちろん、nullableはnullにすることができますので、チェックする必要があります。

(あなたはまたこれを確認するために、コンパイラが生成したILコードを検査するReflectorを使用することができます。)

コンパイラはNULL可能構造体は常にnullでもそのためであろうことを知っているので、元のテストコードは良くありませんそのループを最適化してください。それだけでなく、リリースビルドでは、コンパイラは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

コンパイルした場合、両方のループが空のボディを持っています!それを防ぐために、そしてより現実的なコードの中で何が起こるかを示すために、そのようにそれをテスト

第2のループ振る舞いをするために、あなたは(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 

あなたの例では、Tループ

.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 
関連する問題