8

次のコードを検討してください。

static void FillUsingAsNullable()
{
  int?[] arr = new int?[1 << 24];
  var sw = System.Diagnostics.Stopwatch.StartNew();
  for (int i = 0; i < arr.Length; ++i)
    arr[i] = GetObject() as int?;
  Console.WriteLine("{0:N0}", sw.ElapsedTicks);
}

static void FillUsingOwnCode()
{
  int?[] arr = new int?[1 << 24];
  var sw = System.Diagnostics.Stopwatch.StartNew();
  for (int i = 0; i < arr.Length; ++i)
  {
    object temporary = GetObject();
    arr[i] = temporary is int ? (int?)temporary : null;
  }
  Console.WriteLine("{0:N0}", sw.ElapsedTicks);
}

static object GetObject()
{
//Uncomment only one:
  //return new object();
  //return 42;
  //return null;
}

私が見る限り、メソッドFillUsingAsNullableとメソッドFillUsingOwnCodeは同等であるはずです。

しかし、「独自のコード」バージョンの方が明らかに速いようです。

2「x86」または「x64」をコンパイルするための選択肢、および「2デバッグ」または「リリース (最適化)」をコンパイルするための選択肢、およびメソッド3で何を返すかの選択肢があります。GetObject私が見る限り、これらすべての2*2*3 == 12ケースで、「独自のコード」バージョンは「null 許容」バージョンよりも大幅に高速です。

質問:不必要asNullable<>遅いですか、それともここに何かが欠けていますか(かなりの可能性があります)?

関連スレッド: 「as」および null 許容型によるパフォーマンスの驚き.

4

3 に答える 3

2

生成された IL は異なりますが、根本的なものではありません。JIT が適切であった場合 (これはニュースではありません)、これはまったく同じ x86 コードにコンパイルされる可能性があります。

これを VS2010 Release AnyCPU でコンパイルしました。

asバージョン:

L_0015: call object ConsoleApplication3.Program::GetObject()
L_001a: stloc.3 
L_001b: ldloc.0 
L_001c: ldloc.2 
L_001d: ldelema [mscorlib]System.Nullable`1<int32>
L_0022: ldloc.3 
L_0023: isinst [mscorlib]System.Nullable`1<int32>
L_0028: unbox.any [mscorlib]System.Nullable`1<int32>
L_002d: stobj [mscorlib]System.Nullable`1<int32>

?:バージョン:

L_0015: call object ConsoleApplication3.Program::GetObject()
L_001a: stloc.3 
L_001b: ldloc.0 
L_001c: ldloc.2 
L_001d: ldelema [mscorlib]System.Nullable`1<int32>
L_0022: ldloc.3 
L_0023: isinst int32
L_0028: brtrue.s L_0036 //**branch here**
L_002a: ldloca.s nullable
L_002c: initobj [mscorlib]System.Nullable`1<int32>
L_0032: ldloc.s nullable
L_0034: br.s L_003c
L_0036: ldloc.3 
L_0037: unbox.any [mscorlib]System.Nullable`1<int32>
L_003c: stobj [mscorlib]System.Nullable`1<int32>

オペコードの説明は MSDN にあります。この IL を理解することは難しくなく、誰でも理解できます。ただし、経験の浅い目には少し時間がかかります。

主な違いは、ソース コードに分岐があるバージョンは、生成された IL にも分岐があることです。少しだけエレガントではありません。C# コンパイラは、必要に応じてこれを最適化することもできましたが、チームのポリシーは、JIT に最適化について心配させることです。JITが必要な投資を得ていればうまくいくでしょう。

JIT によって出力された x86 を調べることで、これをさらに分析できます。明らかな違いが見つかりますが、目立たない発見になるでしょう。私はそのために時間を投資しません。


as公正な比較を行うために、一時的なものも使用するようにバージョンを変更しました。

            var temporary = GetObject();
            arr[i] = temporary as int?;
于 2014-01-27T20:31:50.887 に答える