Почему для инструкций 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 знает, что она будет выдвигать int0 iconst_0 не должен хранить операнд, чтобы сказать ему, какое значение выдавать, а также не должен извлекать или декодировать операнд. Компилирование нажатия 0 как bipush 0 было бы правильным, но сделало бы скомпилированный код для spin ¹ на один байт длиннее. Простая виртуальная машина также потратила бы дополнительное время на выборку и декодирование явного операнда каждый раз в цикле. Использование неявных операндов делает скомпилированный код более компактным и эффективным.

¹ это пример кода, который обсуждается, т.е. цикл for от нуля до ста

Это не единственная оптимизация такого рода, например, существуют специальные инструкции для доступа к первым локальным переменным в кадре стека.

Эти операции также имеют особую поддержку в наборе команд. В spin, значения передаются в и из локальных переменных с помощью инструкций istore_1 и iload_1, каждая из которых неявно работает с локальной переменной 1.

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

Оптимизация для небольших переменных индексов оправдана, так как эти индексы назначаются в порядке возрастания, this (если не static), за которыми следуют параметры, за которыми следуют первые локальные переменные. В принципе, компилятор мог бы оптимизировать это дальше, переупорядочив переменные так, чтобы они имели наиболее часто используемые переменные с оптимизированными индексами, но на практике этого не происходит.

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

Кто-нибудь может объяснить, почему определяется только этот диапазон констант?

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

Я предполагаю, что это связано с частотой использования этих констант,

Я провел некоторое исследование, основанное на том, как часто разные инструкции используются через несколько лет после его появления, и нашел мало доказательств эмпирического подхода к решению, какие инструкции должны быть 1 байт против 2 байт. С другой стороны, во время написания оригинального проекта было сгенерировано очень мало байт-кода.

Другие вопросы по тегам