.net开关语句散列或索引?
.Net 4(或任何以前的版本)是否对基于string的较长开关语句执行任何types的优化?
我正在解决一个潜在的性能瓶颈,由于一些长查找匹配string的switch语句,我一直认为这些是在线性时间(或接近线性search,即不使用索引来快速find匹配串)。 但是,这似乎是.Net可以优化的一个明显的领域,所以我认为我会检查是否是这种情况。
这是我最近的一个衍生问题: 索引切换语句,还是等价的? .net,C#
编译下面的代码。
public static int Main(string[] args) { switch (args[0]) { case "x": return 1; case "y": return 2; case "z": return 3; } return 0; }
现在使用Reflector或ILDASM来检查C#编译器生成的IL。 继续添加case语句并反编译并观察结果。
- 如果case语句的数量很less,那么编译器会发出一个顺序的相等比较。
- 如果case语句的数量很大,那么编译器会发出一个
Dictionary
查找。
我使用的是C#3.0编译器,我发现这个策略在7个case语句中改变了。 我怀疑你会看到类似C#4.0和其他的东西。
更新:
我应该指出,你将在IL输出中看到对Dictionary.Add
调用,在那里它将构build字典供以后使用。 不要被愚蠢地认为每次都会发生这种情况。 编译器实际上是生成一个单独的静态类,并对其进行内联静态初始化。 请特别注意L_0026的指示。 如果类已经被初始化,那么分支将跳过Add
调用。
L_0021: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string, int32> <PrivateImplementationDetails>{816396DD-F271-4C12-83D0-CC9C9CD67AD6}::$$method0x6000001-1 L_0026: brtrue.s L_0089 L_0028: ldc.i4.7 L_0029: newobj instance void [mscorlib]System.Collections.Generic.Dictionary`2<string, int32>::.ctor(int32) L_002e: dup L_002f: ldstr "x" L_0034: ldc.i4.0 L_0035: call instance void [mscorlib]System.Collections.Generic.Dictionary`2<string, int32>::Add(!0, !1) L_003a: dup L_003b: ldstr "y" L_0040: ldc.i4.1 L_0041: call instance void [mscorlib]System.Collections.Generic.Dictionary`2<string, int32>::Add(!0, !1) L_0046: dup L_0047: ldstr "z" L_004c: ldc.i4.2 L_004d: call instance void [mscorlib]System.Collections.Generic.Dictionary`2<string, int32>::Add(!0, !1)
另外请注意,字典实际上包含从原始string到整数的映射。 这个整数用于在IL中制定一个单独的开关。
L_0089: volatile. L_008b: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string, int32> <PrivateImplementationDetails>{816396DD-F271-4C12-83D0-CC9C9CD67AD6}::$$method0x6000001-1 L_0090: ldloc.2 L_0091: ldloca.s CS$0$0002 L_0093: call instance bool [mscorlib]System.Collections.Generic.Dictionary`2<string, int32>::TryGetValue(!0, !1&) L_0098: brfalse.s L_00da L_009a: ldloc.3 L_009b: switch (L_00be, L_00c2, L_00c6, L_00ca, L_00ce, L_00d2, L_00d6) L_00bc: br.s L_00da L_00be: ldc.i4.1 L_00bf: stloc.1 L_00c0: br.s L_00de L_00c2: ldc.i4.2 L_00c3: stloc.1 L_00c4: br.s L_00de L_00c6: ldc.i4.3
更新2:
对于什么是值得的VB.NET似乎没有对其Select
构造相同的优化。
看起来像较新的编译器使用ComputeStringHash()
,然后string比较散列命中,而不是字典构造。
// [19 6 - 19 22] IL_0037: ldarg.0 // args IL_0038: ldc.i4.0 IL_0039: ldelem.ref IL_003a: stloc.s V_5 IL_003c: ldloc.s V_5 IL_003e: call unsigned int32 '<PrivateImplementationDetails>'::ComputeStringHash(string) IL_0043: stloc.s V_6 IL_0045: ldloc.s V_6 IL_0047: ldc.i4 -502520314 // 0xe20c2606 IL_004c: bgt.un.s IL_007b IL_004e: ldloc.s V_6 IL_0050: ldc.i4 -536075552 // 0xe00c22e0 IL_0055: beq IL_00f9 IL_005a: br.s IL_005c IL_005c: ldloc.s V_6 IL_005e: ldc.i4 -519297933 // 0xe10c2473 IL_0063: beq IL_00e9 IL_0068: br.s IL_006a IL_006a: ldloc.s V_6 IL_006c: ldc.i4 -502520314 // 0xe20c2606 IL_0071: beq IL_0119 IL_0076: br IL_014c IL_007b: ldloc.s V_6 IL_007d: ldc.i4 -468965076 // 0xe40c292c IL_0082: bgt.un.s IL_009d IL_0084: ldloc.s V_6 IL_0086: ldc.i4 -485742695 // 0xe30c2799 IL_008b: beq.s IL_0109 IL_008d: br.s IL_008f IL_008f: ldloc.s V_6 IL_0091: ldc.i4 -468965076 // 0xe40c292c IL_0096: beq.s IL_00b6 IL_0098: br IL_014c IL_009d: ldloc.s V_6 IL_009f: ldc.i4 -435409838 // 0xe60c2c52 IL_00a4: beq.s IL_00d9 IL_00a6: br.s IL_00a8 IL_00a8: ldloc.s V_6 IL_00aa: ldc.i4 -418632219 // 0xe70c2de5 IL_00af: beq.s IL_00c9 IL_00b1: br IL_014c IL_00b6: ldloc.s V_5 IL_00b8: ldstr "a" IL_00bd: call bool [mscorlib]System.String::op_Equality(string, string) IL_00c2: brtrue.s IL_0129 IL_00c4: br IL_014c IL_00c9: ldloc.s V_5 IL_00cb: ldstr "b" IL_00d0: call bool [mscorlib]System.String::op_Equality(string, string) IL_00d5: brtrue.s IL_012e IL_00d7: br.s IL_014c IL_00d9: ldloc.s V_5 IL_00db: ldstr "c" IL_00e0: call bool [mscorlib]System.String::op_Equality(string, string) IL_00e5: brtrue.s IL_0133 IL_00e7: br.s IL_014c IL_00e9: ldloc.s V_5 IL_00eb: ldstr "d" IL_00f0: call bool [mscorlib]System.String::op_Equality(string, string) IL_00f5: brtrue.s IL_0138 IL_00f7: br.s IL_014c IL_00f9: ldloc.s V_5 IL_00fb: ldstr "e" IL_0100: call bool [mscorlib]System.String::op_Equality(string, string) IL_0105: brtrue.s IL_013d IL_0107: br.s IL_014c IL_0109: ldloc.s V_5 IL_010b: ldstr "f" IL_0110: call bool [mscorlib]System.String::op_Equality(string, string) IL_0115: brtrue.s IL_0142 IL_0117: br.s IL_014c IL_0119: ldloc.s V_5 IL_011b: ldstr "g" IL_0120: call bool [mscorlib]System.String::op_Equality(string, string) IL_0125: brtrue.s IL_0147 IL_0127: br.s IL_014c // [21 17 - 21 26] IL_0129: ldc.i4.0 IL_012a: stloc.s V_7 IL_012c: br.s IL_01ac // [22 17 - 22 26] IL_012e: ldc.i4.1 IL_012f: stloc.s V_7 IL_0131: br.s IL_01ac // [23 17 - 23 26] IL_0133: ldc.i4.2 IL_0134: stloc.s V_7 IL_0136: br.s IL_01ac // [24 17 - 24 26] IL_0138: ldc.i4.3 IL_0139: stloc.s V_7 IL_013b: br.s IL_01ac // [25 17 - 25 26] IL_013d: ldc.i4.4 IL_013e: stloc.s V_7 IL_0140: br.s IL_01ac // [26 17 - 26 26] IL_0142: ldc.i4.5 IL_0143: stloc.s V_7 IL_0145: br.s IL_01ac // [27 17 - 27 26] IL_0147: ldc.i4.6 IL_0148: stloc.s V_7 IL_014a: br.s IL_01ac // [28 16 - 28 26] IL_014c: ldc.i4.m1 IL_014d: stloc.s V_7 IL_014f: br.s IL_01ac