Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Никита Липский — AOT для Java Мифы и Challenges

Никита Липский — AOT для Java Мифы и Challenges

Языку Java присущ встроенный динамизм.
Reflection, динамическая загрузка — это то, без чего современные Java приложения просто не могут существовать, поэтому существует мнение, что AOT (статическая) компиляция вряд ли применима к Java в общем случае, а там где применима не может составить конкуренцию JIT (динамической) компиляции в плане прозводительности.
В этом докладе мы разберемся, почему это не (совсем) так, при этом рассмотрим случаи,
где у статических компиляторов действительно есть определенные сложности в обработке динамической семантики Java.
Также поговорим о том, где статическая компиляции для Java может быть полезна.

План доклада:
- Мифы вокруг статической компиляции Java
- Java AOT Challenges
- Применение AOT для ускорения startup, защиты приложений от декомпиляции.
- Производительность. Какие есть преимущества у JIT и AOT в плане оптимизаций Java программ перед друг другом.
- AOT в разных областях применения Java: client side, server side, embedded, IOT, mobile.

Moscow JUG

May 22, 2017
Tweet

More Decks by Moscow JUG

Other Decks in Programming

Transcript

  1. Трансляция Java to NaIve •  Динамическая (Just-In-Time – JIT). – 

    Трансляция происходит во время исполнения программы •  Статическая (Ahead-Of-Time – AOT) –  Трансляция происходит до исполнения программы 8
  2. 9

  3. План доклада •  Мифы вокруг статической компиляции Java •  Java

    AOT Challenges •  Применение AOT для ускорения startup, защиты приложений от декомпиляции. •  Производительность •  AOT в разных областях применения Java –  client side, server side, embedded, IOT, mobile. 10
  4. 11

  5. Миф 2. AOT компиляция – убийца WORA WORA Write Once

    Run Anywhere BORA Build Once Run Anywhere 15 !=
  6. Миф 3. AOT = маленький Exe ”Я получу (маленький) исполняемый

    файл, который будет работать без JVM (как в С)” Но: – Стандартные классы? –  GC, reflecron? 16
  7. Миф 3. AOT = маленький Exe Тем не менее, используя

    AOT, можно получить дистрибутив Java приложения без зависимостей от Java значительно меньший, чем размер JRE: “Javа худеет”: htps://www.youtube.com/results?search_query=javaday+худеет 17
  8. Нестандартные загрузчики классов •  Переопределяют умолчательную логику разрешений ссылок между

    классами •  Уникальное пространство имен •  Позволяют управлять зависимостями, решая проблемы JAR hell (OSGi, Java Module System) •  Java EE сервера, Eclipse RCP, плагины 19
  9. Нестандартные загрузчики классов Как компилировать статически? •  Компилировать каждый класс

    изолировано от других –  Плохо для производительности •  Изучить логику разрешения ссылок для популярных загрузчиков –  Не работает для произвольных загрузчиков 20
  10. Нестандартные загрузчики классов Схема поддержки загрузчиков в AOT: •  Компонента

    разрешения ссылок в AOT компиляторе: –  Определяет, какие загрузчики будут созданы во время исполнения и назначает каждому статический ID –  Разбивает классы приложения по загрузчикам –  Разрешает ссылки между классами 28
  11. Нестандартные загрузчики классов Схема поддержки загрузчиков в AOT: •  Во

    время исполнения: –  По экземпляру загрузчика вычисляется ID –  Имея статический ID, мы можем грузить статически скомпилированные классы: •  создание экземпляра класса java.lang.Class •  наполнение его рефлективной информацией •  загрузка кода осуществляется OS 29
  12. Нестандартные загрузчики классов Схема поддержки загрузчиков в AOT: •  Во

    время исполнения: –  По экземпляру загрузчика вычисляется ID –  Имея статический ID, мы можем грузить статически скомпилированные классы: Как минимум работает для Eclipse RCP и Web приложений на базе Tomcat 30
  13. Защита кода приложения •  Java bytecode легко превращается в исходный

    код •  Процесс обфускации при активном использовании reflecron трудоемок и трудно поддерживаем •  Даже по обфусцированному коду часто можно понять, что код делает –  ссылки на JDK остаются в неизменном виде 33
  14. Защита кода приложения •  Машинный код можно только эффективно дизассемблировать

    •  По дизассемблированному коду не понять структуру приложения (нет имен классов, методов) •  После агрессивной оптимизации машинный код далек от оригинального кода 34
  15. Холодный старт vs теплый Во второй раз приложение стартует значительно

    быстрее, чем в первый –  Загрузка кода и данных приложения с диска –  Загрузка системных и сторонних динамических библиотек (dll, so) 36
  16. AOT быстрее? •  Машинный код “толще” Java bytecode •  Загрузка

    кода с диска занимает больше времени, чем его начальное исполнение 38
  17. AOT быстрее! •  Код исполняемый на старте – в начало

    исполняемого файла •  Можно предзагружать стартовый сегмент последовательным чтением 39
  18. 40

  19. Миф 4. AOT быстрее “Скомпилировав Java статически, мы получим скорость

    приложения как в C, C быстрее Java, следовательно AOT быстрее” 42
  20. Миф 5. JIT быстрее “Эффективно оптимизировать Java приложения можно только

    на основе динамического профиля исполнения” 43
  21. Виды оптимизаций –  Протяжка констант –  Удаление избыточного кода – 

    Удаление общих подвыражений –  Открытая подстановка –  Специализация методов –  Развертывание циклов –  Версионирование циклов –  Вынос инвариантов –  Удаление хвостовой рекурсии –  Девиртуализация вызовов –  Аллокация объектов на стэке и их взрыв –  Удаление проверок времени исполнения –  Удаление избыточной синхронизации –  Оптимальная выборка кода и свертка шаблонов –  Планировка инструкций –  Оптимальное распределение регистров 44
  22. Девиртуализация вызовов •  Предусловие дальнейшей открытой подстановки (inline) •  Анализ

    иерархии классов –  метод не перегружается – невиртуальный •  Типовый анализ –  new T().foo(); //вызов foo() // невиртуальный •  Profile guided 46
  23. A a; … è a.foo(); if (RT_Type(a) in CHA) {

    inlined body of foo() } else { a.foo(); } Идея: метод не перегружается – невиртуальный Анализ иерархии классов (CHA) 47
  24. Формальный vs фактический тип A a; //A – формальный тип

    Если A – формальный тип переменой a, то во время исполнения в a могут находится значения типа наследника A. Такие типы называются фактическими. A a = new B(); //B – фактический тип Источником фактических типов для статического анализа являются операторы new. 48
  25. Типовый анализ A a = b? new B() : new

    C(); a.foo(); Если foo() в B и С один и тоже, то a.foo() невиртуальный вызов (можно инлайнить) 50
  26. Типовый анализ A a = b? bar() : baz(); …

    a.foo(); Если bar() возвращает только new B, а baz() экземпляры new С, то a.foo() - опять невиртуальный вызов (можно инлайнить) 51
  27. Глобальный анализ •  Глобальный анализ – анализирует все методы программы,

    вычисляя про каждый метод полезную информацию •  Результаты глобального анализа используются для оптимизации конкретного метода 53
  28. Аллокация объектов на стэке •  Все Java объекты создаются в

    динамической памяти – Java heap (куча) •  Большинство объектов временные •  Хочется их размещать на стэке метода Escape анализ (анализ утечек) – определяет утекает ли объект в разделяемую память. 54
  29. Пример Допустим анализ показал, что getCollection() всегда возвращает экземпляр ArrayList

    ArrayList list = getCollection(); Iterator iter = list.iterator(); while (iter.hasNext()) { Object o = iter.next(); doSomething(o); } 57
  30. Пример ArrayList list = getCollection(); ArrayList.ListItr iter = new ListItr(list);

    while (iter.hasNext()) { Object o = iter.next(); doSomething(o); } 58
  31. Пример ArrayList list = getCollection(); ArrayList.ListItr iter = onStack ListItr();

    iter.this$0 = list; iter.cursor = 0; iter.size = list.elemData.length; while (iter.hasNext()) { Object o = iter.next(); doSomething(o); } 59
  32. Пример ArrayList list = getCollection(); ArrayList.ListItr iter = onStack ListItr(list);

    iter.this$0 = list; iter.cursor = 0; iter.size = list.elemData.length; while (iter.cursor < iter.size) { int index = iter.cursor++; Object o = iter.this$0.elemData[index]; doSomething(o); } 60
  33. Пример ArrayList list = getCollection(); int cursor = 0; int

    size = list.elemData.length; while (cursor < size) { Object o = list.elemData[cursor++]; doSomething(o); } 61
  34. Пример ArrayList list = getCollection(); int size = list.elemData.length; for

    (int i = 0; i < size; i++) { doSomething(list.elemData[i]); } 62
  35. •  Часто бывают довольно сложными •  Требуют итеративного пересчета • 

    Глобальный анализ зависит от ВСЕЙ программы. Все ли это может себе позволить JIT? Анализ и оптимизации 63
  36. •  Часто бывают довольно сложными •  Требуют итеративного пересчета • 

    Глобальный анализ зависит от ВСЕЙ программы. Все ли это может себе позволить JIT? Анализ и оптимизации 64
  37. •  Часто бывают довольно сложными •  Требуют итеративного пересчета • 

    Глобальный анализ зависит от ВСЕЙ программы. Все ли это может себе позволить JIT? Анализ и оптимизации 65
  38. Динамические оптимизации •  Профилировка и селективная компиляция •  Открытая подстановка

    на основе профиля исполнения •  Оптимизация трасс исполнения •  Выбор оптимальных инструкций 66
  39. Горячий код vs теплый Q: А что будет, если у

    приложения нет ярко выраженного горячего кода? A: Долгий прогрев, результаты прогрева не используются при дальнейших стартах приложения Типичный случай: UI приложения 67
  40. JFCMark (Short Run) 0% 50% 100% 150% 200% 250% 2

    core Celeron, 2.60Ghz 4 core i5, 3.8GZ Excelsior JET HotSpot client HotSpot server 68
  41. JFCMark (Long Run) 0% 20% 40% 60% 80% 100% 120%

    140% 160% 2 core Celeron, 2.60Ghz 4 core i5, 3.8GZ Excelsior JET HotSpot client HotSpot server 69
  42. Server side •  Процессорное время в облаках – дорого • 

    Через некоторое время работы сервера профиль исполнения стабилизируется •  Почему этот профиль не передать статическому компилятору? 71
  43. AOT on the Server side •  Стабильная производительность, предсказуемая latency

    –  нет деоптимизаций во время исполнения •  Нет прогрева, работает в полную силу прямо со старта –  Хорошо для load balancing •  Лучшее время старта –  хорошо, когда много серверов стартует одновременно •  Все еще защищает от декомпиляции 72
  44. Embedded •  Чем слабее железо, тем дороже динамическая компиляция • 

    Встроенные системы часто не обладают теми же вычислительными мощностями, что и настольные компьютеры/сервера 73
  45. Mobile •  JVM on Mobile: –  Платформенные классы –  GC

    и Memory Manager –  Reflecron –  JIT Чего не хватает? 79
  46. Mobile •  JVM on Mobile: –  Платформенные классы –  GC

    и Memory Manager –  Reflecron –  JIT Зарядки! 80
  47. Mobile •  У беспроводных устройств есть БАТАРЕЙКА •  Стоит ли

    ее тратить на динамическую компиляцию? 81
  48. iOS •  Политика распространения приложений на iOS запрещает любую динамическую

    загрузку кода •  Для Java возможен либо интерпретатор, либо AOT 82
  49. AOT Java Compilers in 2000 Desktop/Server: BulletTrain Excelsior JET GNU

    Compiler for Java (GCJ) IBM VisualAge for Java Supercede/JOVE TowerJ Embedded/Mobile: Diab FastJ Esmertec Jbed ME GCJ IBM J9 Sun Java ME (custom offerings) 83
  50. AOT Java Compilers in 2017 Desktop/Server: Excelsior JET GCJ (RIP)

    IBM Java SDK for AIX IBM Java SDK for z/OS Coming soon: HotSpot AOT (JEP-295) Embedded/Mobile: Excelsior JET Embedded IBM WebSphere Real Time Oracle Java ME Embedded Client Codename One Avian Android ART RoboVM (RIP) Mulr-OS Engine 84
  51. AOT Java Compilers in 2017 Desktop/Server: Excelsior JET GCJ (RIP)

    IBM Java SDK for AIX IBM Java SDK for z/OS Coming soon: HotSpot AOT (JEP-295) Embedded/Mobile: Excelsior JET Embedded IBM WebSphere Real Time Oracle Java ME Embedded Client Codename One Avian Android ART RoboVM (RIP) Mulr-OS Engine 85