Почему переключатель быстрее, чем если

Я нашел много книг в java, говоря, что оператор switch быстрее, чем оператор else. Но я не нашел, что сказать, почему переключатель быстрее, чем если бы .

пример

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

switch(item){ case BREAD: //eat Bread break; default: //leave the restaurant } 

или используя оператор if, как показано ниже

 if(item== BREAD){ //eat Bread }else{ //leave the restaurant } 

рассматриваемый элемент и BREAD – постоянная стоимость int

В приведенном выше примере, который быстрее в действии и почему?

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

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

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

Текущая JVM имеет два типа байтовых кодов переключателей: LookupSwitch и TableSwitch.

Каждый случай в операторе switch имеет целочисленное смещение, если эти смещения смежны (или в основном смежны без больших зазоров) (случай 0: случай 1: случай 2 и т. Д.), Тогда используется TableSwitch.

Если смещения распределены с большими разрывами (случай 0: случай 400: случай 93748: и т. Д.), То используется LookupSwitch.

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

Коммутатор поиска использует двоичный поиск для поиска правильной ветви кода. Это выполняется в O (log n) времени, которое по-прежнему хорошо, но не самое лучшее.

Для получения дополнительной информации об этом см. Здесь: Разница между LookupSwitch и TableSwitch от JVM?

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

Если у вас есть 2 случая, используйте оператор if.

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

Кроме того, имейте в виду, что JVM будет запускать JIT-оптимизации для операторов if, которые будут пытаться разместить самую горячую ветвь в коде. Это называется «Branch Prediction». Для получения дополнительной информации об этом см. Здесь: https://dzone.com/articles/branch-prediction-in-java

Ваш опыт может отличаться. Я не знаю, что JVM не запускает подобную оптимизацию на LookupSwitch, но я научился доверять оптимизации JIT и не пытаюсь перехитрить компилятор.

На уровне байт-кода предметная переменная загружается только один раз в регистр процессора из адреса памяти в структурированном файле .class, загруженном Runtime, и это находится в инструкции switch; тогда как в выражении if другая команда jvm создается вашим компилятором кода DE, и это требует, чтобы каждая переменная загружалась в регистры, хотя одна и та же переменная используется как в следующем предыдущем if-statement. Если вы знаете кодировку на ассемблере, это будет обычным делом; хотя java скомпилированные coxes – это не байт-код или прямой машинный код, условная концепция здесь по-прежнему непротиворечива. Ну, я старался избегать более глубоких технических объяснений. Надеюсь, я сделал концепцию понятной и демистифицированной. Спасибо.

Если вы выполняете безумное количество проверок, например, 100+, вы можете рассмотреть некоторые абстракции.

У вас есть входящие пакеты, которые варьируются от ids от 0 до 255. Вы используете, может быть, 150 из них. Возможно, вы захотите рассмотреть что-то вроде ниже, вместо переключателя из 150 идентификаторов.

 Packets[] packets = new Packets[150]; static { packets[0] = new Login(); packets[2] = new Logout(); packets[3] = new GetMessage(); packets[7] = new AddFriend(); packets[9] = new JoinGroupChat(); // etc... not going to finish. } static final byte[] INDEX_LIST = { 0, 2, 3, 7, 9, // etc... Not going to do it, but this will convert packet id to index. }; public void handlePacket(IncomingData data) { int id = data.readByte(); packets[INDEX_LIST[id]].execute(data); } 

Я также должен указать, что индексный список действительно не нужен и, вероятно, замедлит код. Это было просто предложение, поэтому у вас нет пустых мест. Не говоря уже об этой ситуации, вы теряете только из 106 индексов. Я не уверен на 100%, но я считаю, что каждый из них указывает на нуль, так что реальных проблем с памятью не будет.

  • Как скомпилировать коммутатор в Visual C ++ и насколько он оптимизирован и быстро?
  • «Else if» быстрее, чем «switch () case»?
  • Случайный охват шкафа
  • Использовать «переход» в коммутаторе?
  • Scanf не сканирует символ% c, но пропускает утверждение, почему?
  • Тест для нескольких случаев в коммутаторе, например, OR (||)
  • Почему этот переключатель на корпусе типа считается запутанным?
  • Должны ли команды switch всегда содержать предложение по умолчанию?
  • Как выбрать диапазон значений в инструкции switch?
  • Если против скорости переключения
  • Преимущество перехода на оператор if-else
  • Давайте будем гением компьютера.