Почему переключение происходит быстрее, чем если


Я нашел много книг в 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 92

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 в любом случае, поэтому никаких реальных проблем с памятью не будет.