Если против скорости переключения

Операторы Switch обычно быстрее, чем эквивалентные операторы if-else-if (как, например, описано в этой статье. ) из-за оптимизации компилятора.

Как на самом деле работает эта оптимизация? У кого-нибудь есть хорошее объяснение?

106 голосов | спросил Dirk Vollmar 15 Jam1000000amThu, 15 Jan 2009 02:13:28 +030009 2009, 02:13:28

8 ответов


0

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

Асимптотически лучше во время выполнения, чем множество цепочечных if тестов, и на самом деле быстрее даже для относительно небольшого количества строк.

ответил Konrad Rudolph 15 Jam1000000amThu, 15 Jan 2009 02:16:04 +030009 2009, 02:16:04
0

Конрад верен. В случае включения смежных диапазонов целых чисел (например, когда у вас есть регистр 0, регистр 1, регистр 2 ... регистр n), компилятор может сделать что-то еще лучше, потому что ему даже не нужно создавать хеш-таблицу; он просто хранит массив указателей на функции и, таким образом, может загружать свою цель перехода в постоянное время.

ответил Crashworks 15 Jam1000000amThu, 15 Jan 2009 02:24:29 +030009 2009, 02:24:29
0

Это небольшое упрощение, как обычно у любого современного компилятора, который встречает последовательность if..else if .., которая может быть легко преобразована в оператор switch с помощью человек, компилятор будет также. Но просто для дополнительного удовольствия компилятор не ограничен синтаксисом, поэтому может генерировать «переключатели», подобные внутренним операторам, которые имеют сочетание диапазонов, отдельных целей и т. Д., И они могут (и делают) это как для переключателя, так и для if. .else заявления.

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

switch(a) { case 0: ...; break; case 1: ...; break; }

Если компилятор фактически сгенерировал таблицу переходов для этого, вероятно, будет медленнее, чем альтернативный код в стиле if..else if.. из-за таблицы переходов победить предсказание ветви.

ответил olliej 15 Jam1000000amThu, 15 Jan 2009 02:56:28 +030009 2009, 02:56:28
0

Как сказал Конрад, компилятор может создать таблицу переходов.

В C ++ это может быть вызвано ограниченностью переключателей.

ответил J.J. 15 Jam1000000amThu, 15 Jan 2009 02:28:04 +030009 2009, 02:28:04
0

Операторы switch /case обычно бывают быстрее на 1 уровень, но когда вы начинаете входить в 2 или более, операторы switch /case начинают занимать в 2-3 раза больше времени, чем вложенные операторы if /else.

В этой статье приводятся некоторые сравнения скоростей , в которых выделяются различия в скорости, когда такие операторы вложены.

Например, согласно их тестам, пример кода выглядит следующим образом:

if (x % 3 == 0)
            if (y % 3 == 0)
                total += 3;
            else if (y % 3 == 1)
                total += 2;
            else if (y % 3 == 2)
                total += 1;
            else
                total += 0;
        else if (x % 3 == 1)
            if (y % 3 == 0)
                total += 3;
            else if (y % 3 == 1)
                total += 2;
            else if (y % 3 == 2)
                total += 1;
            else
                total += 0;
        else if (x % 3 == 2)
            if (y % 3 == 0)
                total += 3;
            else if (y % 3 == 1)
                total += 2;
            else if (y % 3 == 2)
                total += 1;
            else
                total += 0;
        else
            if (y % 3 == 0)
                total += 3;
            else if (y % 3 == 1)
                total += 2;
            else if (y % 3 == 2)
                total += 1;
            else
                total += 0;

завершено за половину времени, которое потребовалось для выполнения эквивалентного оператора switch /case:

switch (x % 3)
    {
        case 0:
            switch (y % 3)
            {
                case 0: total += 3;
                    break;
                case 1: total += 2;
                    break;
                case 2: total += 1;
                    break;
                default: total += 0;
                    break;
            }
            break;
        case 1:
            switch (y % 3)
            {
                case 0: total += 3;
                    break;
                case 1: total += 2;
                    break;
                case 2: total += 1;
                    break;
                default: total += 0;
                    break;
            }
            break;
    case 2:
            switch (y % 3)
            {
                case 0: total += 3;
                    break;
                case 1: total += 2;
                    break;
                case 2: total += 1;
                    break;
                default: total += 0;
                    break;
            }
            break;
    default:
        switch (y % 3)
        {
            case 0: total += 3;
                break;
            case 1: total += 2;
                break;
            case 2: total += 1;
                break;
            default: total += 0;
                break;
        }
        break;
    }

Да, это элементарный пример, но он иллюстрирует суть.

Таким образом, можно сделать вывод об использовании switch /case для простых типов, которые имеют глубину только на один уровень, но для более сложных сравнений и нескольких вложенных уровней используйте классические конструкции if /else?

ответил Thrawn Wannabe 27 J000000Sunday14 2014, 06:54:56
0

Статистика не может быть хорошей.

Если вы на самом деле загружаете исходный код, значения совпадений, как известно, равны 21 как в случае if, так и в переключателе. Компилятор должен уметь абстрагироваться, зная, какой оператор должен выполняться в любое время, а процессор должен уметь правильно предсказывать переходы.

Более интересный случай, когда, на мой взгляд, не каждый случай нарушается, но это, возможно, не входило в рамки эксперимента.

ответил Calyth 15 Jam1000000amThu, 15 Jan 2009 02:42:57 +030009 2009, 02:42:57
0

Это код для микроконтроллера PIC18 на языке C:

void main() {
int s1='0';
int d0;
int d1;
//if (s1 == '0') {d1 = '0'; d0 = '0';}
//else if (s1 == '1') {d1 = '0';d0 = '1';}
//else if (s1 == '2') {d1 = '1';d0 = '0';}
//else if (s1 == '3') {d1 = '1';d0 = '1';}
switch (s1) {
      case  '0': {d1 = '0';d0 = '0';} break;
      case  '1': {d1 = '0';d0 = '1';} break;
      case  '2': {d1 = '1';d0 = '0';} break;
      case  '3': {d1 = '1';d0 = '1';} break;
    }
}

С помощью ifs

s1='0' - 14 cycles
s1='1' - 21 cycles
s1='2' - 28 cycles
s1='3' - 33 cycles
s1='4' - 34 cycles

С делами

s1='0' - 17 cycles
s2='1' - 23 cycles
s3='2' - 29 cycles
s4='3' - 35 cycles
s5='4' - 32 cycles

Так что я могу предположить, что на очень низком уровне, если быстрее. Код в ПЗУ тоже короче.

ответил Denys Titarenko 20 TueEurope/Moscow2016-12-20T03:34:10+03:00Europe/Moscow12bEurope/MoscowTue, 20 Dec 2016 03:34:10 +0300 2016, 03:34:10
0

Единственным преимуществом случая if over является заметное увеличение частоты появления первого случая.

Не знаю точно, где находится пороговое значение, но я использую синтаксис регистра, если только первое «почти всегда» не проходит первый тест.

ответил Ralph 6 PMpFri, 06 Apr 2018 19:31:05 +030031Friday 2018, 19:31:05

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

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132