Улучшения Android 8.0 ART

Среда выполнения Android (ART) была значительно улучшена в выпуске Android 8.0. В приведенном ниже списке перечислены усовершенствования, которые производители устройств могут ожидать от ART.

Параллельный сборщик мусора

Как было объявлено на Google I/O, в Android 8.0 в ART появился новый параллельный сборщик мусора (GC). Этот сборщик сжимает кучу каждый раз при запуске сборщика мусора и во время работы приложения с одной короткой паузой для обработки корней потока. Вот его преимущества:

  • Сборщик мусора всегда сжимает кучу: размер кучи в среднем на 32 % меньше по сравнению с Android 7.0.
  • Уплотнение позволяет выделять объекты указателя рельефа локального потока: выделение происходит на 70 % быстрее, чем в Android 7.0.
  • Предлагает на 85% меньшее время паузы для теста H2 по сравнению с Android 7.0 GC.
  • Время паузы больше не зависит от размера кучи; приложения должны иметь возможность использовать большие кучи, не беспокоясь о мусоре.
  • Детали реализации GC — Барьеры чтения:
    • Барьеры чтения — это небольшой объем работы, выполняемый для каждого прочитанного поля объекта.
    • Они оптимизированы в компиляторе, но могут замедлить работу некоторых вариантов использования.

Оптимизация цикла

В версии Android 8.0 ART использует широкий спектр оптимизаций циклов:

  • Исключение проверки границ
    • Статический: диапазоны находятся в допустимых пределах во время компиляции.
    • Динамический: тесты во время выполнения гарантируют, что циклы остаются в пределах границ (в противном случае deopt)
  • Исключение индукционных переменных
    • Удалить мертвую индукцию
    • Замените индукцию, которая используется только после цикла, выражениями в закрытой форме.
  • Устранение мёртвого кода внутри тела цикла, удаление целых циклов, ставших мёртвыми
  • Снижение силы
  • Преобразования циклов: реверсивные, перестановочные, расщепляющие, разворачивающие, унимодулярные и др.
  • SIMDизация (также называемая векторизацией)

Оптимизатор циклов находится в собственном проходе оптимизации компилятора ART. Большинство оптимизаций цикла аналогичны оптимизации и упрощению в других местах. Проблемы возникают с некоторыми оптимизациями, которые переписывают CFG более сложным способом, чем обычно, потому что большинство утилит CFG (см. nodes.h) сосредоточены на создании CFG, а не на его переписывании.

Анализ иерархии классов

ART в Android 8.0 использует анализ иерархии классов (CHA), оптимизацию компилятора, которая превращает виртуальные вызовы в прямые вызовы на основе информации, полученной в результате анализа иерархий классов. Виртуальные вызовы дороги, поскольку они реализованы на основе поиска в виртуальной таблице и берут на себя пару зависимых нагрузок. Также виртуальные вызовы не могут быть встроены.

Вот сводка связанных улучшений:

  • Динамическое обновление состояния метода с одной реализацией. В конце времени связывания классов, когда виртуальная таблица заполнена, ART проводит поэлементное сравнение с виртуальной таблицей суперкласса.
  • Оптимизация компилятора. Компилятор будет использовать информацию об одной реализации метода. Если метод A.foo имеет установленный флаг единственной реализации, компилятор девиртуализует виртуальный вызов в прямой вызов и в дальнейшем попытается встроить прямой вызов в результате.
  • Недействительность скомпилированного кода — также в конце времени связывания классов, когда обновляется информация об одной реализации, если метод A.foo, который ранее имел одиночную реализацию, но теперь имеет этот статус, недействителен, весь скомпилированный код, зависящий от предположения, что метод A. foo имеет единственную реализацию, которая требует, чтобы их скомпилированный код был признан недействительным.
  • Деоптимизация — для живого скомпилированного кода, находящегося в стеке, будет инициирована деоптимизация, чтобы принудительно перевести недействительный скомпилированный код в режим интерпретатора, чтобы гарантировать правильность. Будет использован новый механизм деоптимизации, представляющий собой гибрид синхронной и асинхронной деоптимизации.

Встроенные кеши в файлах .oat

ART теперь использует встроенные кэши и оптимизирует сайты вызовов, для которых имеется достаточно данных. Функция встроенных кэшей записывает дополнительную информацию о времени выполнения в профили и использует ее для добавления динамических оптимизаций к предварительной компиляции.

Dexlayout

Dexlayout — это библиотека, представленная в Android 8.0 для анализа файлов dex и изменения их порядка в соответствии с профилем. Dexlayout стремится использовать информацию профилирования во время выполнения для изменения порядка разделов файла dex во время компиляции в режиме ожидания на устройстве. Группируя вместе части файла dex, к которым часто обращаются вместе, программы могут иметь лучшие схемы доступа к памяти благодаря улучшенной локализации, экономии ОЗУ и сокращению времени запуска.

Поскольку информация профиля в настоящее время доступна только после запуска приложений, dexlayout интегрируется в компиляцию dex2oat на устройстве во время обслуживания в режиме ожидания.

Удаление кеша Dex

До Android 7.0 объект DexCache владел четырьмя большими массивами, пропорциональными количеству определенных элементов в DexFile, а именно:

  • строки (одна ссылка на DexFile::StringId),
  • типы (одна ссылка на DexFile::TypeId),
  • методы (один собственный указатель на DexFile::MethodId),
  • поля (один собственный указатель на DexFile::FieldId).

Эти массивы использовались для быстрого поиска объектов, которые мы ранее разрешили. В Android 8.0 все массивы были удалены, кроме массива методов.

Производительность переводчика

Производительность интерпретатора значительно улучшилась в выпуске Android 7.0 с введением «mterp» — интерпретатора с основным механизмом выборки/декодирования/интерпретации, написанным на языке ассемблера. Mterp создан по образцу быстрого интерпретатора Dalvik и поддерживает arm, arm64, x86, x86_64, mips и mips64. Для вычислительного кода mterp Art примерно сравним с быстрым интерпретатором Dalvik. Однако в некоторых ситуациях это может быть значительно — и даже значительно — медленнее:

  1. Вызов производительности.
  2. Манипуляции со строками и другие активные пользователи методов, признанных встроенными в Dalvik.
  3. Более высокое использование памяти стека.

Android 8.0 решает эти проблемы.

Больше встраивания

Начиная с Android 6.0, ART может встраивать любой вызов в одни и те же файлы dex, но может встраивать только конечные методы из разных файлов dex. Это ограничение было вызвано двумя причинами:

  1. Для встраивания из другого файла dex требуется использовать кеш dex этого другого файла dex, в отличие от встраивания того же файла dex, которое может просто повторно использовать кеш dex вызывающего объекта. Кэш dex необходим в скомпилированном коде для нескольких инструкций, таких как статические вызовы, загрузка строк или загрузка классов.
  2. Карты стека кодируют только индекс метода в текущем файле dex.

Чтобы устранить эти ограничения, Android 8.0:

  1. Удаляет доступ к кешу dex из скомпилированного кода (см. также раздел «Удаление кеша dex»)
  2. Расширяет кодировку карты стека.

Улучшения синхронизации

Команда ART настроила пути кода MonitorEnter/MonitorExit и уменьшила нашу зависимость от традиционных барьеров памяти в ARMv8, заменив их более новыми инструкциями (получение/выпуск), где это возможно.

Более быстрые нативные методы

Более быстрые собственные вызовы собственного интерфейса Java (JNI) доступны с использованием аннотаций @FastNative и @CriticalNative . Эти встроенные средства оптимизации времени выполнения ART ускоряют переходы JNI и заменяют устаревшую нотацию !bang JNI . Аннотации не влияют на несобственные методы и доступны только для кода языка Java платформы в пути bootclasspath (без обновлений Play Store).

Аннотация @FastNative поддерживает нестатические методы. Используйте это, если метод обращается к jobject в качестве параметра или возвращаемого значения.

Аннотация @CriticalNative обеспечивает еще более быстрый способ запуска собственных методов со следующими ограничениями:

  • Методы должны быть статическими — никаких объектов для параметров, возвращаемых значений или неявного this .
  • В собственный метод передаются только примитивные типы.
  • Собственный метод не использует параметры JNIEnv и jclass в определении своей функции.
  • Метод должен быть зарегистрирован в RegisterNatives вместо того, чтобы полагаться на динамическую компоновку JNI.

@FastNative может повысить производительность собственных методов до 3 раз, а @CriticalNative — до 5 раз. Например, переход JNI, измеренный на устройстве Nexus 6P:

Вызов Java Native Interface (JNI) Время выполнения (в наносекундах)
Обычный JNI 115
!банг JNI 60
@FastNative 35
@CriticalNative 25