解析一个.NET程序集时发现了一段<PrivateImplementationDetails>{A_GUID}模样的代码,这是.NET编译器对某些指令做得优化,经分析会在如下情况下产生。
优化数组创建
var int1 = new[] { 1, 2, 3, 4, 5, 6 };
在解析上面的代码时,发现其产生了一些由编译器控制的IL代码
IL_0008: ldc.i4.6 IL_0009: newarr [mscorlib]System.Int32 IL_000e: dup IL_000f: ldtoken field valuetype '<PrivateImplementationDetails>{393AE015-A550-4A61-9BFF-186AFB6A4D12}'/'__StaticArrayInitTypeSize=24' '<PrivateImplementationDetails>{393AE015-A550-4A61-9BFF-186AFB6A4D12}'::'$$method0x60000f8-1' IL_0014: call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle) IL_0019: stloc.0
如果没有使用初始化器
var int1 = new int[6];
生成的IL代码如下
IL_0008: ldc.i4.6 IL_0009: newarr [mscorlib]System.Int32 IL_000e: stloc.0
上面是创建值类型数组的情况,如果是引用类型的数组呢?
var obj1 = new object[3]; var obj2 = new[] {new object(), "test", 1};
生成的IL代码如下
IL_0001: ldc.i4.3 IL_0002: newarr [mscorlib]System.Object IL_0007: stloc.0 IL_0008: ldc.i4.3 IL_0009: newarr [mscorlib]System.Object IL_000e: stloc.2 IL_000f: ldloc.2 IL_0010: ldc.i4.0 IL_0011: newobj instance void [mscorlib]System.Object::.ctor() IL_0016: stelem.ref IL_0017: ldloc.2 IL_0018: ldc.i4.1 IL_0019: ldstr "test" IL_001e: stelem.ref IL_001f: ldloc.2 IL_0020: ldc.i4.2 IL_0021: ldc.i4.1 IL_0022: box [mscorlib]System.Int32 IL_0027: stelem.ref IL_0028: ldloc.2 IL_0029: stloc.1
从下图可以看到,在产生的.NET程序集中有一个<PrivateImplementationDetails>开头的Module
<PrivateImplementationDetails>{0BAF451E-34D3-4B3B-8567-B578D84A6965}是这个编译器产生的Module的名字,{0BAF451E-34D3-4B3B-8567-B578D84A6965}是这个dll的MVID(Module Version Identifier)。
那么为什么要通过调用System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray这个静态方法来初始化数组呢?通过这位同学的试验可以得出结论这是为提高效率而做出的编译时优化。
那什么情况下.NET编译器会产生PrivateImplementationDetails这种机制呢,我还没找到一个完整的列表。除数组外我还发现大的switch表也会产生PrivateImplementationDetails,并简单的试出了启用这种机制的case数量的临界值。
优化case比较多的switch
在代码中加入两个方法SwitchWith6Cases()和SwitchWith7Cases()
public void SwitchWith6Cases() { var param = "a"; switch (param) { case "a": break; case "b": break; case "c": break; case "d": break; case "e": break; case "f": break; } } public void SwitchWith7Cases() { var param = "a"; switch (param) { case "a": break; case "b": break; case "c": break; case "d": break; case "e": break; case "f": break; case "g": break; } }
前者生成的代码如下,基本符合C#代码的逻辑
.method public hidebysig instance void SwitchWith6Cases() cil managed { // Code size 105 (0x69) .maxstack 2 .locals init ([0] string param, [1] string CS$4$0000) IL_0000: nop IL_0001: ldstr "a" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: brfalse.s IL_0068 IL_000c: ldloc.1 IL_000d: ldstr "a" IL_0012: call bool [mscorlib]System.String::op_Equality(string, string) IL_0017: brtrue.s IL_005c IL_0019: ldloc.1 IL_001a: ldstr "b" IL_001f: call bool [mscorlib]System.String::op_Equality(string, string) IL_0024: brtrue.s IL_005e IL_0026: ldloc.1 IL_0027: ldstr "c" IL_002c: call bool [mscorlib]System.String::op_Equality(string, string) IL_0031: brtrue.s IL_0060 IL_0033: ldloc.1 IL_0034: ldstr "d" IL_0039: call bool [mscorlib]System.String::op_Equality(string, string) IL_003e: brtrue.s IL_0062 IL_0040: ldloc.1 IL_0041: ldstr "e" IL_0046: call bool [mscorlib]System.String::op_Equality(string, string) IL_004b: brtrue.s IL_0064 IL_004d: ldloc.1 IL_004e: ldstr "f" IL_0053: call bool [mscorlib]System.String::op_Equality(string, string) IL_0058: brtrue.s IL_0066 IL_005a: br.s IL_0068 IL_005c: br.s IL_0068 IL_005e: br.s IL_0068 IL_0060: br.s IL_0068 IL_0062: br.s IL_0068 IL_0064: br.s IL_0068 IL_0066: br.s IL_0068 IL_0068: ret } // end of method SwitchSample::SwitchWith6Cases
而当switch有超过6个case的时候,所生成的IL代码如下。
.method public hidebysig instance void SwitchWith7Cases() cil managed { // Code size 189 (0xbd) .maxstack 4 .locals init ([0] string param, [1] string CS$4$0000, [2] int32 CS$0$0001) IL_0000: nop IL_0001: ldstr "a" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: brfalse IL_00bc IL_000f: volatile. IL_0011: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>{E6F6F304-0E96-4AD2-AF7A-7D08372A387B}'::'$$method0x60000fd-1' IL_0016: brtrue.s IL_0079 IL_0018: ldc.i4.7 IL_0019: newobj instance void class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::.ctor(int32) IL_001e: dup IL_001f: ldstr "a" IL_0024: ldc.i4.0 IL_0025: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::Add(!0, !1) IL_002a: dup IL_002b: ldstr "b" IL_0030: ldc.i4.1 IL_0031: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::Add(!0, !1) IL_0036: dup IL_0037: ldstr "c" IL_003c: ldc.i4.2 IL_003d: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::Add(!0, !1) IL_0042: dup IL_0043: ldstr "d" IL_0048: ldc.i4.3 IL_0049: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::Add(!0, !1) IL_004e: dup IL_004f: ldstr "e" IL_0054: ldc.i4.4 IL_0055: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::Add(!0, !1) IL_005a: dup IL_005b: ldstr "f" IL_0060: ldc.i4.5 IL_0061: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::Add(!0, !1) IL_0066: dup IL_0067: ldstr "g" IL_006c: ldc.i4.6 IL_006d: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::Add(!0, !1) IL_0072: volatile. IL_0074: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>{E6F6F304-0E96-4AD2-AF7A-7D08372A387B}'::'$$method0x60000fd-1' IL_0079: volatile. IL_007b: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>{E6F6F304-0E96-4AD2-AF7A-7D08372A387B}'::'$$method0x60000fd-1' IL_0080: ldloc.1 IL_0081: ldloca.s CS$0$0001 IL_0083: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::TryGetValue(!0, !1&) IL_0088: brfalse.s IL_00bc IL_008a: ldloc.2 IL_008b: switch ( IL_00ae, IL_00b0, IL_00b2, IL_00b4, IL_00b6, IL_00b8, IL_00ba) IL_00ac: br.s IL_00bc IL_00ae: br.s IL_00bc IL_00b0: br.s IL_00bc IL_00b2: br.s IL_00bc IL_00b4: br.s IL_00bc IL_00b6: br.s IL_00bc IL_00b8: br.s IL_00bc IL_00ba: br.s IL_00bc IL_00bc: ret } // end of method SwitchSample::SwitchWith7Cases
这时候在<PrivateImplementationDetails>*模块中会有如下的一个叫做$$method0×60000fd-1的静态字段,辅助switch的case匹配。
.field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '$$method0x60000fd-1'
通过Dictionary<string,int32>::TryGetValue方法去查找匹配的case。O(1)的效率在case多的情况下比线性O(n)的查找快的多。
