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.

Avatar for Moscow JUG

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