Я запускаю следующий код Java на ноутбуке с процессором Intel Core i7 с тактовой частотой 2,7 ГГц. Я намеревался позволить ему измерить, сколько времени требуется для завершения цикла с 2 ^ 32 итерациями, что, как я ожидал, составит примерно 1,48 секунды (4 / 2,7 = 1,48).
Но на самом деле это занимает всего 2 миллисекунды вместо 1,48 с. Мне интересно, является ли это результатом какой-либо оптимизации JVM внизу?
public static void main(String[] args)
{
long start = System.nanoTime();
for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++){
}
long finish = System.nanoTime();
long d = (finish - start) / 1000000;
System.out.println("Used " + d);
}
javap -v
чтобы увидеть.javac
выполняет очень небольшую фактическую оптимизацию и оставляет большую часть ее JIT-компилятору.Ответы:
Здесь возможны два варианта:
Компилятор понял, что цикл избыточен и ничего не делает, поэтому оптимизировал его.
JIT (JIT-компилятор) понял, что цикл избыточен и ничего не делает, поэтому оптимизировал его.
Современные компиляторы очень умны; они могут видеть, когда код бесполезен. Попробуйте поместить пустой цикл в GodBolt и посмотрите на результат, затем включите
-O2
оптимизацию, вы увидите, что результат похож наЯ хотел бы кое-что прояснить, в Java большая часть оптимизаций выполняется JIT. В некоторых других языках (например, C / C ++) большая часть оптимизации выполняется первым компилятором.
источник
Похоже, он был оптимизирован JIT-компилятором. Когда я выключаю его (
-Djava.compiler=NONE
), код работает намного медленнее:Я поместил код OP внутрь
class MyClass
.источник
Я просто констатирую очевидное - что это оптимизация JVM, цикл просто будет удален. Вот небольшой тест, который показывает огромную разницу
JIT
при включении / включении толькоC1 Compiler
и отключении вообще.Отказ от ответственности: не пишите подобные тесты - это просто для того, чтобы доказать, что фактическое «удаление» цикла происходит в
C2 Compiler
:Результаты показывают, что в зависимости от того, какая часть
JIT
включена, метод становится быстрее (настолько быстрее, что кажется, что он «ничего не делает» - удаление цикла, которое, похоже, происходитC2 Compiler
на максимальном уровне):источник
Как уже отмечалось, JIT -компилятор (точно в срок) может оптимизировать пустой цикл, чтобы удалить ненужные итерации. Но как?
На самом деле существует два JIT-компилятора: C1 и C2 . Сначала код компилируется с помощью C1. C1 собирает статистику и помогает JVM обнаружить, что в 100% случаев наш пустой цикл ничего не меняет и бесполезен. В этой ситуации на сцену выходит C2. Когда код вызывается очень часто, его можно оптимизировать и скомпилировать с C2, используя собранную статистику.
В качестве примера я протестирую следующий фрагмент кода (мой JDK настроен на slowdebug build 9-internal ):
Со следующими параметрами командной строки:
И есть разные версии моего метода запуска , скомпилированные соответственно с C1 и C2. Для меня окончательный вариант (C2) выглядит примерно так:
Это немного запутано, но если вы присмотритесь, вы можете заметить, что здесь нет длительного цикла. Есть 3 блока: B1, B2 и B3, и шаги выполнения могут быть
B1 -> B2 -> B3
илиB1 -> B3
. ГдеFreq: 1
- нормированная расчетная частота выполнения блока.источник
Вы измеряете время, необходимое для обнаружения, что цикл ничего не делает, компилируете код в фоновом потоке и удаляете код.
Если вы запустите это с,
-XX:+PrintCompilation
вы увидите, что код был скомпилирован в фоновом режиме до уровня 3 или компилятора C1 и после нескольких циклов до уровня 4 C4.Если вы измените цикл на использование,
long
он не будет оптимизирован.вместо этого вы получаете
источник
long
счетчик предотвращает такую же оптимизацию?int
note char и short фактически одинаковы на уровне байтового кода.Вы считаете время начала и окончания в наносекундах и делите на 10 ^ 6 для вычисления задержки.
это должно быть
10^9
потому, что1
секунда =10^9
наносекунда.источник