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

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

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 сможет оптимизировать это. Но если так, то почему первые два цикла все же заняли какое-то время?


person zw324    schedule 01.09.2011    source источник
comment
Я думаю, что System.nanoTime() больше подходит для этих тестов (клиент/серверная виртуальная машина также должна иметь некоторое значение)   -  person user85421    schedule 01.09.2011
comment
Да, верно, миллисекунды часто слишком крупнозернисты.   -  person Simone Gianni    schedule 01.09.2011


Ответы (2)


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

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

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

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

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

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

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

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

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

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

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

person Simone Gianni    schedule 01.09.2011

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

person Hank Gay    schedule 01.09.2011
comment
Спасибо! Понятно, но что может сделать пустой цикл? Или, как сказал @Simone Gianni, JIT (гарантированно) не будет запущен, если цикл не выполнялся в течение некоторого времени? РЕДАКТИРОВАТЬ: Еще раз спасибо! Я получил ответ от ответа Симоны :) - person zw324; 01.09.2011