Почему для инструкций JVM *const_n определен только такой диапазон констант?
Согласно спецификации JVM, есть несколько инструкций, оптимизированных для работы с определенным набором констант. Кто-нибудь может объяснить, почему определяется только этот диапазон констант?
- iconst_n: нажмите целочисленную константу n, 0 ≤ n ≤ 5
- lconst_n: push длинная постоянная n, 0 ≤ n ≤ 1
- fconst_n: push-константа n, 0 ≤ n ≤ 2
- dconst_n: push двойная константа n, 0 ≤ n ≤ 1
Я предполагаю, что это связано с частотой использования этих констант, но я не могу найти подтверждение своих мыслей или какой-либо другой информации об этом.
2 ответа
Намерение было упомянуто явно, например, в JVMS, п. 3.2. Использование констант, локальных переменных и управляющих конструкций:
Виртуальная машина Java часто использует вероятность определенных операндов (
int
константы -1, 0, 1, 2, 3, 4 и 5 в случае инструкций iconst_ ), делая эти операнды неявными в коде операции. Поскольку инструкция iconst_0 знает, что она будет выдвигатьint
0
iconst_0 не должен хранить операнд, чтобы сказать ему, какое значение выдавать, а также не должен извлекать или декодировать операнд. Компилирование нажатия 0 как bipush 0 было бы правильным, но сделало бы скомпилированный код дляspin
¹ на один байт длиннее. Простая виртуальная машина также потратила бы дополнительное время на выборку и декодирование явного операнда каждый раз в цикле. Использование неявных операндов делает скомпилированный код более компактным и эффективным.
¹ это пример кода, который обсуждается, т.е. цикл for от нуля до ста
Это не единственная оптимизация такого рода, например, существуют специальные инструкции для доступа к первым локальным переменным в кадре стека.
Эти операции также имеют особую поддержку в наборе команд. В
spin
, значения передаются в и из локальных переменных с помощью инструкций istore_1 и iload_1, каждая из которых неявно работает с локальной переменной 1.
Обратите внимание также на наличие удобной инструкции iinc, единственной, которая напрямую работает с локальной переменной. Таким образом, подсчет циклов, часто начинающихся с нуля или единицы и увеличивающих счетчик на небольшое значение, такое как единица, является основным вариантом использования этих оптимизированных инструкций.
Оптимизация для небольших переменных индексов оправдана, так как эти индексы назначаются в порядке возрастания, this
(если не static
), за которыми следуют параметры, за которыми следуют первые локальные переменные. В принципе, компилятор мог бы оптимизировать это дальше, переупорядочив переменные так, чтобы они имели наиболее часто используемые переменные с оптимизированными индексами, но на практике этого не происходит.
Для оптимизирующей JVM, такой как HotSpot, безусловно, нет никакого преимущества в производительности при использовании этих инструкций, но они по-прежнему делают байт-код немного короче.
Кто-нибудь может объяснить, почему определяется только этот диапазон констант?
В то время это казалось хорошей идеей. Байт-код основан на более старых реализациях виртуальных машин и, возможно, унаследовал эти ограничения.
Я предполагаю, что это связано с частотой использования этих констант,
Я провел некоторое исследование, основанное на том, как часто разные инструкции используются через несколько лет после его появления, и нашел мало доказательств эмпирического подхода к решению, какие инструкции должны быть 1 байт против 2 байт. С другой стороны, во время написания оригинального проекта было сгенерировано очень мало байт-кода.