Почему число локальных переменных, используемых в методе байт-кода 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. Это показывает:
В методе есть три локальные переменные. Аргумент находится в слоте 0, а слоты 1 и 2 предположительно используются кодом. Но я думаю, что достаточно двух локальных переменных - индекс 0 в любом случае является аргументом, а коду нужна только еще одна переменная.
Чтобы проверить, верна ли моя идея, я отредактировал текстовый байт-код, уменьшил количество локальных переменных до 2 и скорректировал некоторые соответствующие инструкции:
Я перекомпилировал его с 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 отслеживает, какие методы вызываются чаще всего и какие потоки чаще всего отслеживаются в коде.
Одна из возможных оптимизаций - это "Встраивание методов", что означает, что целые методы не просто реструктурируются, а объединяются. После объединения методов вы можете работать с большими блоками кода и оптимизировать их еще лучше. На самом деле вы можете исключить переменные еще больше, используя их во всех потоках.