Преимущество переключения оператора if-else

168

Как лучше всего использовать switchоператор против использования ifоператора для 30unsigned перечислений, где около 10 имеют ожидаемое действие (в настоящее время это то же самое действие). Производительность и пространство необходимо учитывать, но это не критично. Я абстрагировал фрагмент, так что не ненавидите меня за соглашения об именах.

switch заявление:

// numError is an error enumeration type, with 0 being the non-error case
// fire_special_event() is a stub method for the shared processing

switch (numError)
{  
  case ERROR_01 :  // intentional fall-through
  case ERROR_07 :  // intentional fall-through
  case ERROR_0A :  // intentional fall-through
  case ERROR_10 :  // intentional fall-through
  case ERROR_15 :  // intentional fall-through
  case ERROR_16 :  // intentional fall-through
  case ERROR_20 :
  {
     fire_special_event();
  }
  break;

  default:
  {
    // error codes that require no additional action
  }
  break;       
}

if заявление:

if ((ERROR_01 == numError)  ||
    (ERROR_07 == numError)  ||
    (ERROR_0A == numError)  || 
    (ERROR_10 == numError)  ||
    (ERROR_15 == numError)  ||
    (ERROR_16 == numError)  ||
    (ERROR_20 == numError))
{
  fire_special_event();
}
Zing-
источник
26
Это редактируется как «субъективный»? В самом деле? Конечно, «субъективный» для вещей, которые не могут быть доказаны так или иначе?
Александра Франкс
Конечно, вы можете увидеть его с точки зрения генерации наиболее эффективного кода, но любой современный компилятор должен быть одинаково эффективным. В конце концов, это больше вопрос цвета сарая для велосипеда.
JFS
8
Я не согласен, я не думаю, что это субъективно. Простое отличие ASM имеет значение, во многих случаях нельзя просто игнорировать несколько секунд оптимизации. И в этом вопросе речь идет не о религиозной войне или дебатах, а рациональном объяснении того, почему кто-то будет быстрее, просто прочитайте принятый ответ.
Чакрит
1
Что быстрее: stackoverflow.com/questions/6805026/is-switch-faster-than-if
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
@RichardFranks оффтоп: черт возьми! ты первый человек, который принял модерацию на SO, которую я когда-либо видел
jungle_mole

Ответы:

162

Используйте переключатель.

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

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

Нильс Пипенбринк
источник
2
Технически все равно будет одно сравнение, чтобы убедиться, что значение перечисления лежит в таблице переходов.
0124816
Jep. Это правда. Включение перечислений и обработка всех дел может избавить от последнего сравнения.
Нильс Пипенбринк
4
Обратите внимание, что ряд ifs может быть теоретически проанализирован, чтобы быть таким же, как переключатель компилятором, но зачем рисковать? Используя переключатель, вы сообщаете именно то, что хотите, что облегчает генерацию кода.
jakobengblom2
5
jakoben: Это можно сделать, но только для цепочек if / else типа switch-like. На практике это не происходит, потому что программисты используют переключатель. Я углубился в технологию компиляции и поверил мне: поиск таких «бесполезных» конструкций занимает много времени. Для ребят из компиляторов такая оптимизация не имеет смысла.
Нильс Пипенбринк
5
@NilsPipenbrinck с легкостью создания псевдо-рекурсивная if- elseцепочки в шаблон мета - программирования и сложности генерации switch caseцепочек, что отображение может стать более важным. (и да, древний комментарий, но сеть навсегда, или, по крайней мере, до следующего
вторника
45

Для особого случая, который вы предоставили в своем примере, самый ясный код:

if (RequiresSpecialEvent(numError))
    fire_special_event();

Очевидно, что это просто перемещает проблему в другую область кода, но теперь у вас есть возможность повторно использовать этот тест. У вас также есть больше вариантов, как решить эту проблему. Вы можете использовать std :: set, например:

bool RequiresSpecialEvent(int numError)
{
    return specialSet.find(numError) != specialSet.end();
}

Я не утверждаю, что это лучшая реализация requireSpecialEvent, просто это вариант. Вы все еще можете использовать переключатель или цепочку if-else, или таблицу поиска, или некоторую битовую манипуляцию со значением, что угодно. Чем более непонятным становится ваш процесс принятия решений, тем большую ценность вы получите от его использования в изолированной функции.

Марк Рэнсом
источник
5
Это так верно. Удобочитаемость намного лучше, чем у переключателя и оператора if. На самом деле я собирался ответить на что-то подобное сам, но вы меня опередили. :-)
mlarsen
Если все значения перечисления малы, то вам не нужен хеш, просто таблица. Например, const std::bitset<MAXERR> specialerror(initializer); используйте его с if (specialerror[numError]) { fire_special_event(); }. Если вам нужна проверка границ, то bitset::test(size_t)будет сгенерировано исключение для значений за пределами. ( bitset::operator[]не проверяет дальность). cplusplus.com/reference/bitset/bitset/test . Это, вероятно, превзойдет сгенерированную компилятором таблицу переходов switch, особенно. в не особом случае, когда это будет одна не занятая ветвь.
Питер Кордес
@PeterCordes Я до сих пор утверждаю, что лучше поместить таблицу в ее собственную функцию. Как я уже сказал, когда вы делаете это, открывается множество вариантов, я не пытался перечислить их все.
Марк Рэнсом
@MarkRansom: я не хотел не соглашаться с его абстракцией. Просто, поскольку вы дали пример реализации с использованием std::set, я подумал, что, возможно, это плохой выбор. Оказывается, gcc уже компилирует код OP для немедленного тестирования растрового изображения в 32-битной версии. Godbolt: goo.gl/qjjv0e . GCC 5.2 даже сделает это для ifверсии. Кроме того, более поздние версии gcc будут использовать инструкцию битового теста btвместо сдвига, чтобы поместить 1бит в нужное место и использовать test reg, imm32.
Питер Кордес
Эта битовая карта с немедленной константой является большим выигрышем, потому что в битовой карте нет ошибок кэша. Это работает, если все «специальные» коды ошибок находятся в диапазоне 64 или меньше. (или 32 для устаревшего 32-битного кода.) Компилятор вычитает наименьшее значение регистра, если оно не равно нулю. Вывод заключается в том, что последние компиляторы достаточно умны, чтобы вы, вероятно, получили хороший код из любой используемой вами логики, если только вы не скажете ей использовать громоздкую структуру данных.
Питер Кордес
24

Переключатель находится быстрее.

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

Теперь у коммутатора есть одна реальная проблема : он должен знать во время компиляции значения внутри каждого случая. Это означает, что следующий код:

// WON'T COMPILE
extern const int MY_VALUE ;

void doSomething(const int p_iValue)
{
    switch(p_iValue)
    {
       case MY_VALUE : /* do something */ ; break ;
       default : /* do something else */ ; break ;
    }
}

не скомпилируется.

Большинство людей будут тогда использовать определения (Aargh!), А другие будут объявлять и определять постоянные переменные в том же модуле компиляции. Например:

// WILL COMPILE
const int MY_VALUE = 25 ;

void doSomething(const int p_iValue)
{
    switch(p_iValue)
    {
       case MY_VALUE : /* do something */ ; break ;
       default : /* do something else */ ; break ;
    }
}

Итак, в конце концов, разработчик должен выбрать между «скорость + четкость» и «кодовая связь».

(Не то, чтобы выключатель не мог быть написан так, чтобы он запутался как ад ... Большинство переключателей, которые я сейчас вижу, относятся к этой «запутанной» категории »... Но это другая история ...)

Изменить 2008-09-21:

bk1e добавил следующий комментарий: « Определение констант в виде перечислений в заголовочном файле - еще один способ справиться с этим».

Конечно, это является.

Смысл внешнего типа заключался в том, чтобы отделить значение от источника. Определение этого значения как макроса, простого объявления const int или даже перечисления имеет побочный эффект от вставки значения. Таким образом, если определение, значение перечисления или значение const int изменятся, потребуется перекомпиляция. Объявление extern означает, что нет необходимости перекомпилировать в случае изменения значения, но, с другой стороны, делает невозможным использование switch. Вывод « Использование переключателя» увеличит связь между кодом переключателя и переменными, используемыми в качестве случаев . Когда все в порядке, используйте переключатель. Когда это не так, то не удивительно.

,

Изменить 2013-01-15:

Влад Лазаренко прокомментировал мой ответ, дав ссылку на подробное изучение кода сборки, генерируемого переключателем. Очень поучительно: http://lazarenko.me/switch/

paercebal
источник
Определение констант как перечислений в заголовочном файле - еще один способ справиться с этим.
bk1e
6
Переключатель не всегда быстрее .
1
@ Влад Лазаренко: Спасибо за ссылку! Это было очень интересное чтение.
paercebal
1
@AhmedHussein user404725 ссылка мертва. К счастью, я нашел его в WayBack Machine: web.archive.org/web/20131111091431/http://lazarenko.me/2013/01/… . Действительно, WayBack Machine может быть настоящим благословением.
Джек Гиффин
Большое спасибо, это очень полезно
Ахмед Хуссейн
20

Компилятор все равно его оптимизирует - переключайтесь, так как он наиболее читабелен.

Александра Франкс
источник
3
Скорее всего, компилятор не будет касаться if-then-else. На самом деле, gccне буду этого делать наверняка (для этого есть веская причина). Clang оптимизирует оба случая в бинарный поиск. Например, посмотрите это .
7

Переключатель, если только для удобства чтения. Гигантские, если заявления труднее поддерживать и труднее читать по моему мнению.

ERROR_01 : // преднамеренный провал

или

(ERROR_01 == numError) ||

Последнее более подвержено ошибкам и требует большего набора текста и форматирования, чем первое.

scubabbl
источник
6

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

Bdoserror
источник
6

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

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

Мартин Беккет
источник
4

Компиляторы действительно хороши в оптимизации switch. Недавние gcc также хороши для оптимизации множества условий в if.

Я сделал несколько тестов на Godbolt .

Когда caseзначения сгруппированы близко друг к другу, gcc, clang и icc достаточно умны, чтобы использовать растровое изображение, чтобы проверить, является ли значение одним из специальных.

например, gcc 5.2 -O3 компилирует в switch(и что- ifто очень похожее):

errhandler_switch(errtype):  # gcc 5.2 -O3
    cmpl    $32, %edi
    ja  .L5
    movabsq $4301325442, %rax   # highest set bit is bit 32 (the 33rd bit)
    btq %rdi, %rax
    jc  .L10
.L5:
    rep ret
.L10:
    jmp fire_special_event()

Обратите внимание, что растровое изображение является непосредственными данными, поэтому нет потенциальной ошибки при обращении к кешу данных или к таблице переходов.

gcc 4.9.2 -O3 компилирует в switchрастровое изображение, но делает 1U<<errNumberс mov / shift. Компилирует ifверсию в серию веток.

errhandler_switch(errtype):  # gcc 4.9.2 -O3
    leal    -1(%rdi), %ecx
    cmpl    $31, %ecx    # cmpl $32, %edi  wouldn't have to wait an extra cycle for lea's output.
              # However, register read ports are limited on pre-SnB Intel
    ja  .L5
    movl    $1, %eax
    salq    %cl, %rax   # with -march=haswell, it will use BMI's shlx to avoid moving the shift count into ecx
    testl   $2150662721, %eax
    jne .L10
.L5:
    rep ret
.L10:
    jmp fire_special_event()

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

Более короткая (в машинном коде) последовательность будет:

    cmpl    $32, %edi
    ja  .L5
    mov     $2150662721, %eax
    dec     %edi   # movabsq and btq is fewer instructions / fewer Intel uops, but this saves several bytes
    bt     %edi, %eax
    jc  fire_special_event
.L5:
    ret

(Отказ от использования jc fire_special_eventвездесущ и является ошибкой компилятора .)

rep retиспользуется в целях ветвления и следующих условных ветвлениях для старых AMD K8 и K10 (pre-Bulldozer): Что означает «rep ret»? , Без этого предсказание ветвлений не работает так же хорошо на этих устаревших процессорах.

bt(битовый тест) с регистром arg быстрый. Он сочетает в себе работу сдвига влево на 1 errNumberбит и делаетtest , но все еще имеет задержку в 1 цикл и только один цикл Intel UOP. Он медленен с аргументом памяти из-за его семантики "слишком слишком CISC": с операндом памяти для "битовой строки" адрес проверяемого байта вычисляется на основе другого аргумента (деленного на 8), и isn не ограничивается 1, 2, 4 или 8-байтовым фрагментом, на который указывает операнд памяти.

Из таблиц команд Agner Fog команда сдвига с переменным счетом медленнее, чем btна недавнем Intel (2 моп вместо 1, а смещение не делает все остальное, что нужно).

Питер Кордес
источник
4

Преимущества switch () по сравнению с if case: - 1. Switch гораздо эффективнее по сравнению с if else, потому что каждый случай не зависит от предыдущего, в отличие от случая, когда необходимо проверить отдельное утверждение на наличие условий true или false.

  1. Когда нет. значений для одного выражения, переключение регистра является более гибким, чем в другом случае, потому что в этом случае суждение основывается только на двух значениях, то есть либо true, либо false.

  2. Значения в switch определены пользователем, тогда как значения в случае if else основаны на ограничениях.

  3. В случае ошибки операторы в switch могут быть легко перепроверены и исправлены, что сравнительно трудно проверить в случае оператора if else.

  4. Корпус коммутатора гораздо компактнее, его легче читать и понимать.

Vineeth Krishna K
источник
2

ИМО, это прекрасный пример того, для чего был сделан провал переключателя.

Nescio
источник
в c # это единственный случай, когда возникает мысль о падении. Хороший аргумент прямо там.
БКС,
2

Если ваши дела, вероятно, останутся сгруппированными в будущем - если более одного случая соответствуют одному результату - переключение может оказаться проще для чтения и обслуживания.

TSomKes
источник
2

Они работают одинаково хорошо. Производительность примерно такая же, учитывая современный компилятор.

Я предпочитаю операторы if, а не операторы case, потому что они более читабельны и более гибки - вы можете добавить другие условия, не основанные на числовом равенстве, такие как «|| max <min». Но для простого случая, который вы разместили здесь, это не имеет значения, просто делайте то, что наиболее читабельно для вас.

SquareCog
источник
2

переключатель определенно предпочтительнее. Проще посмотреть список дел коммутатора и точно знать, что он делает, чем читать длинное условие if.

Дублирование в ifсостоянии тяжело для глаз. Предположим, что один из ==был написан !=; вы заметили? Или, если один экземпляр 'numError' был написан 'nmuError', который только что скомпилировался?

Обычно я предпочитаю использовать полиморфизм вместо переключателя, но без более подробной информации о контексте трудно сказать.

Что касается производительности, лучше всего использовать профилировщик для измерения производительности вашего приложения в условиях, которые похожи на то, что вы ожидаете в дикой природе. В противном случае вы, вероятно, оптимизируете не в том месте и не в том направлении.

Джей Базузи
источник
2

Я согласен с гибкостью решения коммутатора, но, по-моему, вы угоняете коммутатор здесь.
Назначение переключателя состоит в том, чтобы иметь различную обработку в зависимости от значения.
Если бы вам пришлось объяснять свой алгоритм в псевдокоде, вы бы использовали if, потому что семантически, вот что это такое: если what_error делает это ...
Так что если вы не собираетесь когда-нибудь изменить свой код, чтобы иметь конкретный код для каждой ошибки Я бы использовал, если .

Francesca
источник
2
Я не согласен по той же причине, что и с падением. Я читаю переключатель как «В случаях 01,07,0A, 10,15,16 и 20 срабатывание специального события». Нет другого перехода в другой раздел. Это просто артефакт синтаксиса C ++, где вы повторяете ключевое слово case для каждого значения.
MSalters
1

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

Уильям Келлер
источник
1

Я бы сказал, используйте переключатель. Таким образом, вам нужно реализовать разные результаты. Ваши десять одинаковых кейсов могут использовать по умолчанию. Если нужно изменить все, что вам нужно, это явно реализовать это изменение, нет необходимости редактировать значение по умолчанию. Также гораздо проще добавлять или удалять дела из SWITCH, чем редактировать IF и ELSEIF.

switch(numerror){
    ERROR_20 : { fire_special_event(); } break;
    default : { null; } break;
}

Может быть, даже проверить ваше состояние (в данном случае numerror) по списку возможностей, возможно, по массиву, чтобы ваш SWITCH даже не использовался до тех пор, пока не будет достигнут определенный результат.

Льюис
источник
Всего около 30 ошибок. 10 требуют специальных действий, поэтому я использую значение по умолчанию для ~ 20 ошибок, которые не требуют действий ...
Цин-
1

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

Грег Уитфилд
источник
Вау, это похоже на способ превратить простую проблему в сложную. Зачем идти на все эти неприятности, когда компилятор сделает большую работу за вас. Плюс это, по-видимому, обработчик ошибок, поэтому он вряд ли будет критичным по скорости. Переключатель - безусловно, самая легкая вещь для чтения и обслуживания.
MrZebra
Таблица вряд ли сложна - на самом деле это, вероятно, проще, чем переключение на код. И в заявлении упоминается производительность.
Грег Уитфилд
Это звучит как преждевременная оптимизация. Пока вы сохраняете значения enum маленькими и непрерывными, компилятор должен делать это за вас. Помещение переключателя в отдельную функцию делает код, который его использует, красивым и маленьким, как предлагает Марк Рэнсом в своем ответе, дает такое же преимущество для небольшого кода.
Питер Кордес
Также, если вы собираетесь что-то реализовать самостоятельно, сделайте std::bitset<MAXERR> specialerror;, тогда if (specialerror[err]) { special_handler(); }. Это будет быстрее, чем таблица переходов, особенно в не взятом случае.
Питер Кордес
1

Я не уверен насчет наилучшей практики, но я бы использовал переключатель - и затем ловил намеренный провал через «default»

da5id
источник
1

Эстетически я склоняюсь к такому подходу.

unsigned int special_events[] = {
    ERROR_01,
    ERROR_07,
    ERROR_0A,
    ERROR_10,
    ERROR_15,
    ERROR_16,
    ERROR_20
 };
 int special_events_length = sizeof (special_events) / sizeof (unsigned int);

 void process_event(unsigned int numError) {
     for (int i = 0; i < special_events_length; i++) {
         if (numError == special_events[i]) {
             fire_special_event();
             break;
          }
     }
  }

Сделайте данные немного умнее, чтобы мы могли сделать логику немного тупее.

Я понимаю, это выглядит странно. Вот вдохновение (из того, как я это сделал в Python):

special_events = [
    ERROR_01,
    ERROR_07,
    ERROR_0A,
    ERROR_10,
    ERROR_15,
    ERROR_16,
    ERROR_20,
    ]
def process_event(numError):
    if numError in special_events:
         fire_special_event()
mbac32768
источник
4
Синтаксис языка действительно оказывает влияние на то, как мы реализуем решение ... => Это выглядит некрасиво в C и хорошо в Python. :)
rlerallut
Использовать растровые изображения? Если error_0a равен 0x0a и т. Д., Вы можете поместить их как биты в long long. long long special_events = 1LL << 1 | 1LL << 7 | 1LL << 0xa ... Тогда используйте if (special_events & (1LL << numError) fire_special_event ()
paperhorse
1
Тьфу. Вы превратили O (1) наихудшую операцию (если генерируются таблицы переходов) в O (N) наихудший случай (где N - количество обработанных случаев), и вы использовали breakвнешнюю case(да, второстепенную) грех, но, тем не менее, грех). :)
Mac
Тьфу? Он сказал, что производительность и пространство не являются критическими. Я просто предлагал другой способ взглянуть на проблему. Если мы можем представить проблему таким образом, чтобы люди думали меньше, то мне, как правило, все равно, означает ли это, что компьютеры должны думать больше.
mbac32768
1
while (true) != while (loop)

Вероятно, первый оптимизируется компилятором, что объясняет, почему второй цикл медленнее при увеличении числа циклов.

MarioFrost
источник
Похоже, это комментарий к ответу McAnix. Это только одна из проблем, связанных с этой попыткой синхронизации по ifсравнению switchс условием завершения цикла в Java.
Питер Кордес
1

Когда дело доходит до компиляции программы, я не знаю, есть ли разница. Но что касается самой программы и того, чтобы код был максимально простым, я лично думаю, что это зависит от того, что вы хотите сделать. если еще, если еще заявления имеют свои преимущества, которые я считаю:

Позволяет вам тестировать переменную по определенным диапазонам. Вы можете использовать функции (Стандартная библиотека или Личные) в качестве условных выражений.

(пример:

`int a;
 cout<<"enter value:\n";
 cin>>a;

 if( a > 0 && a < 5)
   {
     cout<<"a is between 0, 5\n";

   }else if(a > 5 && a < 10)

     cout<<"a is between 5,10\n";

   }else{

       "a is not an integer, or is not in range 0,10\n";

Тем не менее, если еще, если еще заявления могут быть сложными и грязными (несмотря на ваши лучшие попытки) в спешке. Операторы Switch имеют тенденцию быть более ясными, понятными и более легкими для чтения; но может использоваться только для проверки определенных значений (пример:

`int a;
 cout<<"enter value:\n";
 cin>>a;

 switch(a)
 {
    case 0:
    case 1:
    case 2: 
    case 3:
    case 4:
    case 5:
        cout<<"a is between 0,5 and equals: "<<a<<"\n";
        break;
    //other case statements
    default:
        cout<<"a is not between the range or is not a good value\n"
        break;

Я предпочитаю, если - еще, если - еще заявления, но это действительно зависит от вас. Если вы хотите использовать функции в качестве условий, или вы хотите что-то протестировать с диапазоном, массивом или вектором и / или вы не против иметь дело со сложным вложением, я бы рекомендовал использовать блоки If if if else. Если вы хотите проверить на единичные значения или хотите получить чистый и легко читаемый блок, я бы порекомендовал вам использовать блоки case ().

Джордан Эффингер
источник
0

Я не тот человек, что расскажет вам о скорости и использовании памяти, но смотреть на состояние переключателя - чертовски легче понять, чем большой оператор if (особенно через 2-3 месяца)

Эд Браун
источник
0

Я знаю его старое но

public class SwitchTest {
static final int max = 100000;

public static void main(String[] args) {

int counter1 = 0;
long start1 = 0l;
long total1 = 0l;

int counter2 = 0;
long start2 = 0l;
long total2 = 0l;
boolean loop = true;

start1 = System.currentTimeMillis();
while (true) {
  if (counter1 == max) {
    break;
  } else {
    counter1++;
  }
}
total1 = System.currentTimeMillis() - start1;

start2 = System.currentTimeMillis();
while (loop) {
  switch (counter2) {
    case max:
      loop = false;
      break;
    default:
      counter2++;
  }
}
total2 = System.currentTimeMillis() - start2;

System.out.println("While if/else: " + total1 + "ms");
System.out.println("Switch: " + total2 + "ms");
System.out.println("Max Loops: " + max);

System.exit(0);
}
}

Изменение количества циклов сильно меняется:

В то время как если / иначе: 5 мс Переключатель: 1 мс Макс. Циклов: 100000

В то время как если / иначе: 5 мс Переключатель: 3 мс Макс. Петли: 1000000

В то время как если / иначе: 5 мс Переключатель: 14 мс Макс. Петли: 10000000

В то время как если / иначе: 5 мс Переключатель: 149 мс Макс. Циклов: 100000000

(добавьте больше заявлений, если хотите)

McAnix
источник
3
Хороший вопрос, но извините, чувак, вы не на том языке. Изменение языка сильно меняется;)
Габриэль Шрайбер
2
if(max) breakЦикл работает в постоянная время независимо от числа петель? Похоже, JIT-компилятор достаточно умен, чтобы оптимизировать цикл до counter2=max. А может быть, это медленнее, чем switch, если первый вызов to currentTimeMillisимеет больше накладных расходов, потому что еще не все JIT-скомпилировано? Размещение петель в другом порядке, вероятно, даст другие результаты.
Питер Кордес