Почему компилятор добавляет операторы в коммутатор? - PullRequest
0 голосов
/ 18 декабря 2018

У меня есть следующее достаточно простое заявление о переключении.// ранее string fullPath = GetFullPath ();string type = GetEntityType ();

switch (type.ToLower()) {
    case "tables":
        tables.Add(fullPath);
        break;
    case "views":
        views.Add(fullPath);
        break;
    case "functions":
        functions.Add(fullPath);
        break;
    case "storedprocs":
        storedprocs.Add(fullPath);
        break;
    case "data":
        data.Add(fullPath);
        break;
    case "layouts":
        layouts.Add(fullPath);
        break;
    case "scripts":
        scripts.Add(fullPath);
        break;
    default:
        Console.WriteLine($"What is this: {type}");
        break;
}

Когда я декомпилирую полученный бинарный файл с помощью Reflector, switch(string) был изменен на ComputeStringHash, а затем внутри каждого оператора case он проверяет значение с помощью оператора if,Похоже, он делает двойную работу.

    string s = str2.ToLower();
    switch (<PrivateImplementationDetails>.ComputeStringHash(s))
    {
        case 0x20890fc4:
            if (s == "tables")
            {
                break;
            }
            goto Label_0218;

        case 0x454a414e:
            if (s == "functions")
            {
                goto Label_01DE;
            }
            goto Label_0218;

        case 0x4facf6d1:
            if (s == "views")
            {
                goto Label_01D3;
            }
            goto Label_0218;

        case 0xcdfe2cb3:
            if (s == "storedprocs")
            {
                goto Label_01E9;
            }
            goto Label_0218;

        case 0xd872e2a5:
            if (s == "data")
            {
                goto Label_01F4;
            }
            goto Label_0218;

        case 0x9b4a129b:
            if (s == "scripts")
            {
                goto Label_020C;
            }
            goto Label_0218;

        case 0xba971064:
            if (s == "layouts")
            {
                goto Label_0200;
            }
            goto Label_0218;

        default:
            goto Label_0218;
    }

    first.Add(fullPath);
    continue;
  Label_01D3:
      second.Add(fullPath);
      continue;
  Label_01DE:
      list3.Add(fullPath);
      continue;
  Label_01E9:
      list4.Add(fullPath);
      continue;
  Label_01F4:
      list5.Add(fullPath);
      continue;
  Label_0200:
      list6.Add(fullPath);
      continue;
  Label_020C:
      list7.Add(fullPath);
      continue;
  Label_0218:
    Console.WriteLine("What is this: " + str2);
}

1 Ответ

0 голосов
/ 18 декабря 2018

Это очень умная оптимизация, которая позволяет switch выполнять свою работу во времени, практически не зависящей от количества строк в блоке case оператора.

Эта оптимизация основанапри наблюдении, что хеш-коды одинаковых строк должны быть одинаковыми.Вместо того, чтобы проверять строки на равенство один за другим, компилятор вычисляет хеш целевой строки один раз и выполняет поиск на основе таблиц в O (1).Это приводит компилятор к желаемому случаю, и в этот момент компилятор должен проверить, что строки на самом деле равны.

Обратите внимание, что бывают редкие ситуации, когда несколько строк поиска имеют одинаковый хэш-код,В таких ситуациях сгенерированный оператор case будет содержать несколько if для выбора среди строк с одинаковыми хеш-кодами.

В целом, это поведение имитирует поведение словарей на основе хеш-функции: хеш-код определяет case (эквивалент хеша) и серия if внутри определяет, есть ли совпадение.Это приводит к повышению производительности, поскольку позволяет компилятору пропускать ненужные проверки.

...