Почему число локальных переменных, используемых в методе байт-кода Java, не самое экономичное?

У меня есть фрагмент простого кода Java:

public static void main(String[] args) {
    String testStr = "test";
    String rst = testStr + 1 + "a" + "pig" + 2;
    System.out.println(rst);
}

Скомпилируйте его с помощью компилятора Java Eclipse и проверьте байт-код с помощью AsmTools. Это показывает:

byte code

В методе есть три локальные переменные. Аргумент находится в слоте 0, а слоты 1 и 2 предположительно используются кодом. Но я думаю, что достаточно двух локальных переменных - индекс 0 в любом случае является аргументом, а коду нужна только еще одна переменная.

Чтобы проверить, верна ли моя идея, я отредактировал текстовый байт-код, уменьшил количество локальных переменных до 2 и скорректировал некоторые соответствующие инструкции:

edited

Я перекомпилировал его с AsmTools, и он отлично работает!

Так почему же Javac или компилятор Eclipse не проводят такую оптимизацию для использования минимальных локальных переменных?

Ответ 1

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

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

Значение: jvm решает во время выполнения, стоит ли переводить метод в (сильно оптимизированный!) Машинный код. Если jvm решает, что "не стоит оптимизировать", зачем делать javac более сложным и медленным из-за большого количества оптимизации? Плюс: чем проще и проще входящий байт-код, тем легче JIT анализировать и улучшать этот ввод!

Ответ 2

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

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

Ответ 3

Ну, вы просто сделали ложную зависимость между двумя совершенно разными местными жителями. Это будет означать, что JIT-компилятор либо должен быть более сложным/медленным, чтобы распутать изменение и в любом случае вернуться к исходному байт-коду, либо будет ограничен в способах оптимизации, которые он может выполнять.

Имейте в виду, что компилятор Java запускается один раз на вашей машине разработки (или сборки). Это JIT-компилятор, который знает оборудование (и программное обеспечение), на котором он работает. Компилятор Java должен создавать простой, простой код, который JIT легко обрабатывает и оптимизирует (или, в некоторых случаях, интерпретирует). Существует очень мало причин для чрезмерной оптимизации самого байт-кода - вы можете сократить несколько байт от размера исполняемого файла, но зачем беспокоиться, особенно если результатом будет либо менее эффективный код ЦП, либо более длительное время компиляции JIT?

У меня сейчас нет среды для проведения реального теста, но я почти уверен, что JIT будет выдавать один и тот же исполняемый код из двух байт-кодов (это, безусловно, происходит в .NET, что во многом похоже).

Ответ 4

Там также проблема с проверкой байт-кода. Вы знаете, что каждая переменная в Jave должна быть определена, прежде чем ее можно будет использовать. Если вы объединяете переменную x и переменную y вместе, и порядок "определите x, используйте x, используйте y", который должен быть обнаружен как ошибка верификатором байт-кода, но после объединения двух переменных он больше не будет обнаруживаться.

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

Ответ 5

Ява обещает, что один и тот же код может работать на нескольких системах. Java могла бы оптимизировать свой байт-код с самого начала. Но предпочитает ждать, пока все факты станут известны.

  • Оборудование: Один и тот же байт-код может работать на Raspberry Pi или на многоядерном Unix сервер с 64 ГБ.
  • Использование: Некоторые функции почти не вызываются, а другие Вызываются несколько раз в секунду.
  • Гибкость: в будущем байт-код может работать на другой JVM, которая предлагает новые оптимизации. (JDK x?)

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

Вывод: Не переименовывайте, не перемещайте и не устраняйте переменные только для того, чтобы сделать код быстрее.


Почему использование так важно:

Java отслеживает, какие методы вызываются чаще всего и какие потоки чаще всего отслеживаются в коде.

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