Java: сколько времени использует пустой цикл?

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

for (int j = 0; j < 10; j++) { long t = System.currentTimeMillis(); for (int i = 0; i < 10000000; i++) ; t = System.currentTimeMillis() - t; System.out.print(t + " "); } 

Каждый раз, когда я запускаю это, он возвращает тот же результат:

6 7 0 0 0 0 0 0 0 0

Почему первые две петли всегда занимают некоторое время, а остальные просто пропускаются системой?

В этом ответе на этот пост говорится, что компиляция Just-In-Time сможет оптимизировать это. Но если да, то почему первые две петли все-таки заняли какое-то время?

JIT триггеры ПОСЛЕ того, что определенный fragment кода был выполнен много раз.

HotSpot JVM попытается определить «горячие точки» в вашем коде. Горячие точки – это fragmentы вашего кода, которые выполняются много раз. Для этого JVM будет «подсчитывать» выполнение различных инструкций, и когда он определяет, что определенная часть выполняется часто, она вызывает JIT. (это приближение, но это легко понять объясняется таким образом).

JIT (Just-In-Time) берет этот кусок кода и пытается сделать его быстрее.

Методы, используемые JIT для ускорения вашего кода, много, но тот, который обычно создает путаницу:

  1. Он попытается определить, использует ли этот fragment переменные, которые нигде не используются (бесполезные переменные) и удаляют их.
  2. Если вы приобретете и отпустите один и тот же замок несколько раз (например, вызовите синхронизированные методы одного и того же объекта), он может получить блокировку один раз и выполнить все вызовы в одном синхронизированном блоке
  3. Если вы обращаетесь к членам объекта, которые не объявляют volatile, он может решить оптимизировать его (размещение значений в регистрах и т. Д.), Создавая странные результаты в многопоточном коде.
  4. Это позволит встроить методы, чтобы избежать стоимости вызова.
  5. Он преобразует байт-код в машинный код.
  6. Если цикл полностью бесполезен, его можно полностью удалить.

Итак, правильный ответ на ваш вопрос заключается в том, что пустая петля после JITed не требует времени для выполнения. Скорее всего, этого больше нет.

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

Более того, JIT улучшается в любой новой версии Java, и иногда она даже немного отличается в зависимости от платформы (поскольку она в определенной степени зависит от платформы). Оптимизации, сделанные JIT, трудно понять, потому что вы обычно не можете найти их с помощью javap и проверять байт-код, даже если в последних версиях Java некоторые из этих оптимизаций были перемещены непосредственно в компилятор (например, поскольку Java 6 компилятор способный обнаруживать и предупреждать о неиспользуемых локальных переменных и частных методах).

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

Обычно это запускает JIT в простой программе, такой как ваша, даже если нет гарантии, что она фактически инициирует (или что она даже существует на определенной платформе).

Если вы хотите получить параноик о JIT или не JIT-времени (я сделал): сделайте первый раунд, определите время выполнения цикла и подождите, пока синхронизация стабилизируется (например, разница в среднем меньше 10%), затем начните с вашего «реального» времени.

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

Давайте будем гением компьютера.