JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

28
JIT-компиляция в виртуальной машине Java Алексей Рагозин

description

Обеспечение достойной производительности высокоуровневого языка с динамической типизацией - непростая задача. Just-in-time (JIT) компиляция - динамическая генерация машинного кода с учетом информации, собранной во время выполнения приложения - ключевой элемент производительности виртуальной машины (будь то Java, .NET или даже JavaScript). JIT-компилятор, в свою очередь, должен иметь впечатляющий набор трюков и оптимизаций, что бы компенсировать "динамизм" языка. В докладе речь пойдет о достижениях современной JIT компиляции в целом и более подробно будут освещены особенности HotSpot JVM (бесплатной JVM от Oracle)

Transcript of JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Page 1: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

JIT-компиляция в виртуальной машине Java

Алексей Рагозин

Page 2: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Обзор доклада

Проблемы динамических языков

Виртуальные вызовы

Нетипизированные данные

Подходы к JIT компиляции

Method based JIT

Tracing JIT

“Грязные трюки” JIT компиляторов

Page 3: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Старый добрый C++

Простое наследование

010110010010

101010100110

101010100101

010101001010

101010101010

101010101010

101010101010

101010100010

00: methodA

02: methodC

03: methodD

CODEOBJECT

VTABLE

01: methodB

Page 4: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Старый добрый C++

Множественное наследование

010110010010

101010100110

101010100101

010101001010

101010101010

101010101010

101010101010

101010100010

111010100100

011110000010

101001010100

00: methodA

02: methodC

03: methodD

CODEOBJECT

VTABLE

01: methodB

00: methodX

02: methodZ

01: methodY

VTABLE

Page 5: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Старый добрый C++

Множественное наследование

A

B C

D D

A

B C

D

Page 6: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Проблемы виртуальных вызовов

Branch misprediction penalty

• Intel Nehalem – 17 cycles

• Intel Sandy/Ivy bridge – 15 cycles

• Intel Haskwell – 15 - 20 cycles

• AMD K8 / K10 – 13 cycles

• AMD Buldozer – 19 - 22 cycles

http://www.agner.org/optimize/microarchitecture.pdf

Page 7: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Проблемы виртуальных вызовов

Два обращения в память до перехода

• Обращения в память упорядочены

• Конвейер процессор блокирован

Время доступа к памяти

• L1 кэш ~0.5 ns

• L2 кэш ~7 ns

• RAM ~100 ns

Page 8: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Проблемы динамических типов

Поля хранятся как хэш-таблица

Доступ к полю

• Арифметическая операция

• Чтение из памяти

• Условная операция

• Чтение из памяти

Page 9: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Как сделать быстрый интерпретатор?

switch(byteCode) {

case STORE: ...

case LOAD: ...

case ASTORE: ...

case ALOADE: ...

...

}

?

Page 10: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Как сделать быстрый интерпретатор?

Интерпретатор HotSpot JVM

• Для каждой инструкции написана процедура на ассемблере

• Dispath – jump по адресу из таблицы процедур

• Каждая процедура заканчивается jump на dispatch

Интерпретация кода в одном кадре стека

Код и таблица переходов кэшируются

Конвейер процессора остаётся загруженным

Page 11: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Подходы к JIT компиляции

Классический подход Независимая компиляция методов

+ использование динамических оптимизаций

Трассирующая компиляция Генерация кода для участков без ветвления

+ использование гардов

Page 12: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Подходы к JIT компиляции

Классический подход Независимая компиляция методов

• JVM, V8, Ion Monkey

Трассирующая компиляция Генерация кода для участков без ветвления

• Flash, Trace Monkey, PyPy, LuaJIT

Page 13: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Трассирующий JIT

Интерпретация • Логирование операций и условий ветвления

Профилирование • Выявление часто используемых трасс

Компиляция трасс • Машинный код без ветвлений

• Гарды в местах проверки условий

• Глобальная оптимизация трассы

• Нарушение гарда – возврат к интерпретации

Page 14: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Трассирующий JIT

Достоинства

• Девиртуализация и инлайнинг

• Сглаживает проблему динамических типов

• Глубокая оптимизация горячих участков

Недостатки

• Трассировка – ОЧЕНЬ медленная интерпретация

• Долгое время разогрева

Page 15: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Проблема виртуальных типов

V8 – скрытые типы

• Строгая типизация во время выполнения

TraceMonkey – shape inference/property cache

• “Инлайн” кэш в скомпилированном коде

LuaJIT – трейс компиляция поиска по хешу HREFK: if (hash[17].key != key) goto exit

HLOAD: x = hash[17].value

-or-

HSTORE: hash[17].value = x

Page 17: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

HotSpot JVM

Page 18: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

HotSpot JVM JIT

• Быстрый интерпретатор

• Два JIT компилятора (C1 / C2)

• Профилирование для управления компиляцией

• Деоптимизация кода

• On Stack Replacement (OSR)

Page 19: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Девиртуализация

Профилирование точек вызовов (call site)

• Мономорфный – большинство переходов на одну реализацию

• Биморфный - большинство переходов на одну из двух реализаций

• Полиморфный

Page 20: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Девиртуализация

“Инлайн” кэш переходов

if (list.getClass == ArrayList.class) {

/* NON VIRTUAL */ list.ArrayList#size()

}

else {

/* VIRTUAL */ list.size();

}

Page 21: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Инкрементальная компиляция

Collections.indexedBinarySearch()

MyPojo

int mid = (low + high) >>> 1;

Comparable<? super T> midVal = list.get(mid);

int cmp = midVal.compareTo(key);

Полиморфный

Полиморфный

List<String> keys = new ArrayList<String>();

List<String> vals = new ArrayList<String>();

public String get(String key) {

int n = Collections.binarySearch(keys, key);

return n < 0 ? null ? vals.get(n);

}

Page 22: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Инкрементальная компиляция

JIT компилирует MyPojo.get() • Collections.binarySort() – инлайнится

Вызовы в Collections.binarySort() становятся мономорфными

JIT продолжает профилирование и перекомпилирует метод

Вызовы get() и compareTo() девиртуализированны и заинлайнены

Page 23: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

On Stack Replacement

JIT может перекомпилировать main и подменить точку возврата на стеке, находясь внутри цикла

public static void main() {

long s = System.nanotime();

for(int i = 0; i != N; ++i) {

/* a lot of code */

...

}

long avg = (System.nanotime() - s) / N;

}

Page 24: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Escape analysis

Тяжёлое наследие молодости – synchronize

buf не выходит за пределы метода

все методы buf заинлайнены

удаляем код синхронизации

public String toString() {

StringBuffer buf = new StringBuffer();

buf.append("X=").append(x);

buf.append(",Y=").append(y);

return buf.toString();

}

Page 25: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Scalar replacement

После инлайна distance в length

JIT заменяет объекты Point на скалярные переменные

public double length() {

return distance(

new Point(ax, ay),

new Point(bx, by));

}

public double distance(Point a, Point b) {

double w = a.x - b.x;

double h = a.y - b.y;

return Math.sqrt(w*w + h*h);

}

Page 26: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Сборка мусора и JIT

JIT инлайнит final static переменные

• Адрес объекта в памяти в машинном коде

• С точки зрения GC код метода структура Учитывается как корень при маркировке

Адрес в коде корректируется при перемещении объекта

public class Singleton {

public static final

Singleton INSTANCE = new Singleton()

}

Page 27: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Оптимизация кода

“Красивые самолёты летают быстрее” – поговорка авиаконструкторов

Page 28: JIT-компиляция в виртуальной машине Java (HighLoad++ 2013)

Спасибо Алексей Рагозин ([email protected])

http://blog.ragozin.info

http://aragozin.timepad.ru