Операторы переключения .NET хешируются или индексируются?

.Net 4 (или какая-либо предыдущая версия) выполняют какой-либо вид оптимизации на более длительных операторах переключения на основе строк?

Я работаю вокруг потенциального узкого места производительности из-за некоторых долгих операторов переключения, ища соответствующие строки в случаях, и я всегда предполагал, что они ищутся в линейное время (или почти линейные, т.е. не использование индекса для быстрого нахождения соответствующей строки). Но это походит на очевидную область, которую мог оптимизировать .NET, поэтому думал, что я проверю, если это верно, или нет.

Это - производный вопрос от моего недавнего: индексируемый оператор переключения, или эквивалентный? .NET, C#

46
задан Community 23 May 2017 в 11:46
поделиться

1 ответ

Скомпилируйте следующий код.

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 , чтобы проверить IL, генерируемый компилятором C #. Продолжайте добавлять операторы case и декомпилировать и наблюдать за результатом.

  • Если количество операторов case невелико, компилятор производит последовательное сравнение на равенство.
  • Если количество операторов case велико, компилятор выполняет поиск в Dictionary .

Я использовал компилятор C # 3.0 и заметил, что стратегия меняется в 7 случаях. Я подозреваю, что вы увидите что-то похожее на C # 4.0 и другие.

Обновление:

Я должен указать, что вы увидите вызовы Dictionary.Add в выводе IL, где он создает словарь для дальнейшего использования. Не обманывайтесь, думая, что это происходит каждый раз. Компилятор фактически создает отдельный статический класс и выполняет его встроенную статическую инициализацию. Обратите особое внимание на инструкцию на L_0026. Если класс уже инициализирован, ветвь будет пропускать вызовы Добавить .

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)

Также обратите внимание, что словарь фактически содержит преобразование исходной строки в целое число. Это целое число используется для формулирования отдельного переключателя в 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 .

113
ответ дан 26 November 2019 в 20:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: