Якщо проти швидкості перемикання


112

Оператори перемикання, як правило, швидші, ніж еквівалентні операції if-else-if (як, наприклад, описано в цій статті ) завдяки оптимізаціям компілятора.

Як реально працює ця оптимізація? Хтось має гарне пояснення?



Можлива відповідь: dotnetperls.com/if-switch-performance
Babak

Відповіді:


185

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

Це має асимптотичну кращу тривалість виконання, ніж багато ланцюгових ifтестів, і насправді швидше навіть для порівняно небагато рядків.


6
Хороша відповідь, цікаво про хеш-таблицю.
BobbyShaftoe

4
Вони також перетворюються на порівняння дерев у деяких випадках. Міркування дещо складні, але в основному зводяться до таблиці непрямості, створюючи сучасні цільові буфери процесорного стрибка, і таким чином знищує передбачувач гілки. Я неясно згадую документ на конференції GCC про кодеген для комутаторів.
olliej

Це означає: switch (a) case "x": case "y": case "z": // щось перерва; } швидше, ніж: якщо (a == "x" || a == "b" || a == "c") // щось правильно?
yazanpro

тут ми не вкладені, якщо інше, тільки АБО так що ви думаєте?
yazanpro

@yazanpro На старих компіляторах можливо так (але зауважте, що кількість випадків настільки мала, що може не змінити значення!). Однак сучасні компілятори роблять набагато більше аналізу коду. Як наслідок, вони можуть з'ясувати, що ці два фрагменти коду є рівнозначними, і застосовують однакові оптимізації. Але це чиста спекуляція з мого боку, я не знаю, чи дійсно це робить якийсь компілятор.
Конрад Рудольф

15

Це незначне спрощення, як правило, будь-який сучасний компілятор, який стикається з if..else if ..послідовністю, яка може тривіально перетворитись на оператор переключення людиною, і компілятор. Але просто для додаткової забави компілятор не обмежений синтаксисом, тому він може генерувати "перемикання" на зразок операторів внутрішньо, які мають поєднання діапазонів, одиничних цілей тощо - і вони можуть (і робити) це робити і для переключення, і якщо. .else заяви.

Anyhoo, розширення відповіді Конрада полягає в тому, що компілятор може генерувати таблицю стрибків, але це не обов'язково гарантується (ні бажано). З різних причин таблиці стрибків роблять погані речі передбачувачам гілок сучасних процесорів, а самі таблиці погано діють, щоб кешувати поведінку, наприклад.

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

Якщо компілятор насправді створив для цього таблицю стрибків, то, ймовірно, повільніше буде if..else if..код альтернативного стилю через прогнозування гілки таблиці переходів.


4

Статистика без матчу може бути недоброю.

Якщо ви фактично завантажуєте джерело, значення не збігаються, як відомо, 21, як у випадку if, так і у випадку перемикання. Компілятор повинен мати можливість абстрагуватися, знаючи, який вислів слід запускати, і центральний процесор повинен мати можливість прогнозування гілок належним чином.

Більш цікавий випадок, коли не кожен випадок розривається, на мою думку, але це, можливо, не було предметом експерименту.


4

Оператори перемикання / регістру можуть бути, як правило, більш швидкими на 1 рівень, але коли ви починаєте вводити в 2 або більше, заяви коммутатора / випадку починають приймати в 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 (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;
    }

Так, це рудиментарний приклад, але він ілюструє суть.

Отже, для висновку може бути використання перемикача / випадку для простих типів, що мають глибину лише одного рівня, але для більш складних порівнянь та декількох вкладених рівнів використовують класичні конструкції if / else?


-1: 1. У статті повністю проігноровано передбачення галузі, 2. алгоритми не зовсім однакові (єдиний if-else один посилання вже кодується більш оптимізованим) і 3. знайдені відмінності настільки малі, що нічого не виправдовує використання правильного, чистого коду (близько 4 нс в 10
000 000

Цей приклад не буде оптимізований через те, як мало випадків має блок комутатора. Зазвичай після 5-6 елементів він генерує таблицю стрибків.
antiduh

0

Єдиною перевагою випадку "над" є те, коли спостерігається помітне збільшення частоти зустрічей у першому випадку.

Не точно знаю, де знаходиться поріг, але я використовую синтаксис регістру, якщо перший "майже завжди" не пройде перший тест.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.