2009-06-18 83 views
7

当您使用新的C#集合初始化语法:不C#集合初始化语法避免违约的初始化开销

string[] sarray = new[] { "A", "B", "C", "D" }; 

该编译器避免初始化每个阵列插槽为默认值,或者是它等价于:

string[] sarray = new string[4]; // all slots initialized to null 
sarray[0] = "A"; 
sarray[1] = "B"; 
sarray[2] = "C"; 
sarray[3] = "D"; 

回答

13

编译器仍然使用newarr IL指令,所以CLR仍然会初始化数组。

集合初始化为只是编译器魔术 - CLR不知道任何关于它的信息,所以它仍然认为它必须执行清理。

但是,这应该是真的很快 - 它只是擦拭记忆。我怀疑这在很多情况下是一个很大的开销。

+0

有趣。我想知道这种'内存擦除'的方法来进行数组初始化是结构不支持显式默认构造函数或成员初始化函数的原因之一。这会使阵列初始化复杂化。 – LBushkin 2009-06-18 17:53:31

+1

是的,这是很多。实际上,IL *中的结构支持无参数的构造函数,但它们只会在某些情况下被调用。 – 2009-06-18 17:59:35

10

快速测试:

 string[] arr1 = 
     { 
      "A","B","C","D" 
     }; 
     arr1.GetHashCode(); 

     string[] arr2 = new string[4]; 
     arr2[0] = "A"; 
     arr2[1] = "B"; 
     arr2[2] = "C"; 
     arr2[3] = "D"; 

     arr2.GetHashCode(); 

结果在这个IL(注意,它们都是相同的)

IL_0002: newarr  [mscorlib]System.String 
    IL_0007: stloc.2 
    IL_0008: ldloc.2 
    IL_0009: ldc.i4.0 
    IL_000a: ldstr  "A" 
    IL_000f: stelem.ref 
    IL_0010: ldloc.2 
    IL_0011: ldc.i4.1 
    IL_0012: ldstr  "B" 
    IL_0017: stelem.ref 
    IL_0018: ldloc.2 
    IL_0019: ldc.i4.2 
    IL_001a: ldstr  "C" 
    IL_001f: stelem.ref 
    IL_0020: ldloc.2 
    IL_0021: ldc.i4.3 
    IL_0022: ldstr  "D" 
    IL_0027: stelem.ref 
    IL_0028: ldloc.2 
    IL_0029: stloc.0 
    IL_002a: ldloc.0 
    IL_002b: callvirt instance int32 [mscorlib]System.Object::GetHashCode() 
    IL_0030: pop 
    IL_0031: ldc.i4.4 
    IL_0032: newarr  [mscorlib]System.String 
    IL_0037: stloc.1 
    IL_0038: ldloc.1 
    IL_0039: ldc.i4.0 
    IL_003a: ldstr  "A" 
    IL_003f: stelem.ref 
    IL_0040: ldloc.1 
    IL_0041: ldc.i4.1 
    IL_0042: ldstr  "B" 
    IL_0047: stelem.ref 
    IL_0048: ldloc.1 
    IL_0049: ldc.i4.2 
    IL_004a: ldstr  "C" 
    IL_004f: stelem.ref 
    IL_0050: ldloc.1 
    IL_0051: ldc.i4.3 
    IL_0052: ldstr  "D" 
    IL_0057: stelem.ref 
    IL_0058: ldloc.1 
    IL_0059: callvirt instance int32 [mscorlib]System.Object::GetHashCode() 
1

我跑使用您所描述的语法instantianting数组一个简短的测试,发现使用非默认值实例化比使用默认值的实例化时间长2.2倍。

当我切换并使用默认值实例化时,大概需要相同的时间量。

事实上,当我查看反编译时,看起来会发生什么是数组被初始化,然后填充任何非默认值。

与非默认值的实例化:

  bool[] abPrimes = new[] { 
       true, true 
      }; 
0000007e mov   edx,2 
00000083 mov   ecx,79114A46h 
00000088 call  FD3006F0 
0000008d mov   dword ptr [ebp-64h],eax 
00000090 mov   eax,dword ptr [ebp-64h] 
00000093 mov   dword ptr [ebp-54h],eax 
00000096 mov   eax,dword ptr [ebp-54h] 
00000099 cmp   dword ptr [eax+4],0 
0000009d ja   000000A4 
0000009f call  76A9A8DC 
000000a4 mov   byte ptr [eax+8],1 
000000a8 mov   eax,dword ptr [ebp-54h] 
000000ab cmp   dword ptr [eax+4],1 
000000af ja   000000B6 
000000b1 call  76A9A8DC 
000000b6 mov   byte ptr [eax+9],1 
000000ba mov   eax,dword ptr [ebp-54h] 
000000bd mov   dword ptr [ebp-40h],eax 

具有默认值的实例化:

bool[] abPrimes2 = new[] { 
       false, false 
      }; 
000000c0 mov   edx,2 
000000c5 mov   ecx,79114A46h 
000000ca call  FD3006F0 
000000cf mov   dword ptr [ebp-68h],eax 
000000d2 mov   eax,dword ptr [ebp-68h] 
000000d5 mov   dword ptr [ebp-54h],eax 
000000d8 mov   eax,dword ptr [ebp-54h] 
000000db mov   dword ptr [ebp-5Ch],eax 
0

这是不可能的,以避免每个阵列插槽初始化为默认值,至少在IL级。

字符串是一个类,而不是一个结构。

这意味着A,B,C,D和sarray可以存储在任何位置。 A,B,C和D可能来自Intern池,对对象的引用可能是动态的。

但我相信JIT可以足够聪明地减少这些开销的一半。

PS。过早优化是所有邪恶的根源。