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

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

Одновременный сборщик мусора для уплотнения

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

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

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

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

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

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

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

В Android 8.0 ART использует анализ иерархии классов (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
!bang JNI 60
@FastNative 35
@CriticalNative 25