Java: сколько времени использует пустой цикл? - PullRequest
7 голосов
/ 01 сентября 2011

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

Ответы [ 2 ]

20 голосов
/ 01 сентября 2011

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

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

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

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

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

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

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

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

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

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

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

8 голосов
/ 01 сентября 2011

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

...