Почему переключение происходит быстрее, чем если
Я нашел много книг в java, говорящих, что оператор switch работает быстрее, чем оператор if else. Но я не нашел муравейника, говорящего почему переключатель быстрее, чем если.
пример
у меня есть ситуация, я должен выбрать любой один элемент из двух я могу использовать любой из следующих способов
switch(item){
case BREAD:
//eat Bread
break;
default:
//leave the restaurant
}
или с помощью оператора if, как показано ниже
if(item== BREAD){
//eat Bread
}else{
//leave the restaurant
}
рассматривая деталь и хлеб постоянн инт значение
В приведенном выше примере, который быстрее в действии и почему?
5 ответов:
потому что существуют специальные байт-коды, которые позволяют эффективно оценивать оператор switch, когда есть много случаев.
Если реализовано с помощью if-операторов, у вас будет проверка, переход к следующему предложению, проверка, переход к следующему предложению и т. д. С помощью switch JVM загружает значение для сравнения и повторяет таблицу значений, чтобы найти совпадение, что в большинстве случаев происходит быстрее.
A
switch
утверждение не всегда быстрее, чемif
заявление. Он масштабируется лучше, чем длинный списокif-else
заявленияswitch
выполнить поиск на основе всех ценностей. Однако для короткого состояния он не будет быстрее и может быть медленнее.
текущий JVM имеет два вида байтовых кодов коммутатора: LookupSwitch и TableSwitch.
каждый случай в операторе switch имеет целочисленное смещение, если эти смещения являются непрерывными (или в основном непрерывными без больших зазоров) (случай 0: случай 1: случай 2 и т. д.), затем используется TableSwitch.
Если смещения распределены с большими зазорами (случай 0: случай 400:случай 93748: и т.д.), затем используется LookupSwitch.
разница в том, что TableSwitch делается постоянное время, поскольку каждому значению в диапазоне возможных значений задается определенное смещение байт-кода. Таким образом, когда вы даете оператору смещение 3, он знает, чтобы перейти вперед 3, чтобы найти правильную ветвь.
переключатель поиска использует двоичный поиск для поиска правильной ветви кода. Это работает в O (log n) время, которое все еще хорошо, но не самое лучшее.
для получения дополнительной информации об этом см. здесь: разница между LookupSwitch JVM и TableSwitch?
Итак, насколько какой из них самый быстрый, используйте этот подход: Если у вас есть 3 или более случаев, значения которых являются последовательными или почти последовательными, всегда используйте переключатель.
Если у вас есть 2 случая, используйте оператор if.
для любой другой ситуации, выключатель скорее всего быстрее, но это не гарантировано, так как двоичный поиск в LookupSwitch может ударить по плохому сценарию.
кроме того, имейте в виду, что JVM будет запускать JIT оптимизация операторов if, которые будут пытаться разместить самую горячую ветвь сначала в коде. Это называется "предсказание ветвей". Для получения дополнительной информации об этом см. здесь:https://dzone.com/articles/branch-prediction-in-java
ваш опыт может отличаться. Я не знаю, что JVM не выполняет аналогичную оптимизацию на LookupSwitch, но я научился доверять JIT-оптимизации и не пытаться перехитрить компилятор.
на уровне байт-кода переменная subject загружается только один раз в регистр процессора из адреса памяти в структурированном виде .файл класса загружается во время выполнения, и это находится в операторе switch; в то время как в if-операторе другая инструкция jvm создается вашим компилятором кода DE, и это требует, чтобы каждая переменная была загружена в регистры, хотя используется та же переменная, что и в следующем предшествующем if-операторе. Если вы знаете кодирование на ассемблере, то это было бы обычным делом; хотя скомпилированные Кокс java не являются байт-кодом или прямым машинным кодом, условная концепция здесь все еще последовательна. Ну, я попытался избежать более глубоких формальностей при объяснении. Я надеюсь, что сделал концепцию ясной и демистифицированной. Спасибо.
Если вы выполняете безумное количество проверок, таких как 100+, вы можете рассмотреть некоторую абстракцию.
у вас есть входящие пакеты, которые варьируются от коды от 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%, но я считаю, что каждый из них указывает на null в любом случае, поэтому никаких реальных проблем с памятью не будет.