Среда выполнения Android (ART) была значительно улучшена в версии Android 8.0. В приведенном ниже списке представлены усовершенствования, которые производители устройств могут ожидать в ART.
Параллельно уплотняющий сборщик мусора
Как было объявлено на Google I/O, ART включает в себя новый сборщик мусора (GC) с параллельным сжатием в Android 8.0. Этот сборщик уплотняет кучу каждый раз при запуске GC и во время работы приложения, оставляя только одну короткую паузу для обработки корней потоков. Вот его преимущества:
- GC всегда уплотняет кучу: размер кучи в среднем на 32% меньше по сравнению с Android 7.0.
- Сжатие позволяет выделять объекты локального указателя рельефа потока: выделение происходит на 70 % быстрее, чем в Android 7.0.
- Обеспечивает на 85 % меньшее время паузы для теста H2 по сравнению с Android 7.0 GC.
- Время паузы больше не зависит от размера кучи; приложения должны иметь возможность использовать большие кучи, не беспокоясь о зависаниях.
- Подробности реализации GC. Прочтите препятствия:
- Барьеры чтения — это небольшой объем работы, выполняемой для каждого чтения поля объекта.
- Они оптимизированы в компиляторе, но могут замедлить работу в некоторых случаях использования.
Оптимизация цикла
В версии Android 8.0 ART использует широкий спектр оптимизаций цикла:
- Исключения проверки границ
- Статический: во время компиляции доказано, что диапазоны находятся в пределах допустимых значений.
- Динамический: тесты во время выполнения гарантируют, что циклы остаются в пределах границ (в противном случае отключите их).
- Исключение переменных индукции
- Удалить мертвую индукцию
- Замените индукцию, которая используется только после цикла, выражениями в замкнутой форме.
- Устранение мертвого кода внутри тела цикла, удаление целых циклов, которые стали мертвыми
- Снижение прочности
- Циклические преобразования: обращение, перестановка, расщепление, развертывание, унимодулярность и т. д.
- SIMDization (также называемая векторизацией)
Оптимизатор цикла находится в отдельном проходе оптимизации компилятора ART. Большинство оптимизаций цикла аналогичны оптимизациям и упрощениям в других местах. Проблемы возникают с некоторыми оптимизациями, которые переписывают CFG более сложным, чем обычно, способом, поскольку большинство утилит CFG (см. nodes.h) сосредоточены на построении CFG, а не на ее переписывании.
Анализ иерархии классов
ART в Android 8.0 использует анализ иерархии классов (CHA) — оптимизацию компилятора, которая превращает виртуальные вызовы в прямые вызовы на основе информации, полученной в результате анализа иерархии классов. Виртуальные вызовы являются дорогостоящими, поскольку они реализуются вокруг поиска в виртуальной таблице и требуют нескольких зависимых нагрузок. Также виртуальные вызовы не могут быть встроены.
Вот краткий обзор связанных улучшений:
- Динамическое обновление статуса метода с одной реализацией. В конце времени связывания классов, когда виртуальная таблица заполнена, ART выполняет поэлементное сравнение с виртуальной таблицей суперкласса.
- Оптимизация компилятора. Компилятор будет использовать информацию об одной реализации метода. Если у метода A.foo установлен флаг единственной реализации, компилятор девиртуализует виртуальный вызов в прямой вызов и в дальнейшем попытается встроить прямой вызов в результат.
- Недействительность скомпилированного кода — также в конце времени связывания классов, когда обновляется информация об одной реализации, если метод A.foo, который ранее имел одиночную реализацию, но этот статус теперь признан недействительным, весь скомпилированный код, который зависит от предположения, что метод A. foo требует единой реализации, чтобы их скомпилированный код был признан недействительным.
- Деоптимизация. Для живого скомпилированного кода, находящегося в стеке, будет инициирована деоптимизация, чтобы перевести недействительный скомпилированный код в режим интерпретатора, чтобы гарантировать корректность. Будет использован новый механизм деоптимизации, представляющий собой гибрид синхронной и асинхронной деоптимизации.
Встроенные кэши в файлах .oat
ART теперь использует встроенные кэши и оптимизирует сайты вызовов, для которых существует достаточно данных. Функция встроенного кэширования записывает дополнительную информацию времени выполнения в профили и использует ее для добавления динамической оптимизации к предварительной компиляции.
Декслейаут
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 Арта примерно сравним с быстрым интерпретатором Dalvik. Однако в некоторых ситуациях это может быть значительно (и даже значительно) медленнее:
- Вызвать производительность.
- Манипулирование строками и другие активные пользователи методов, признанных встроенными в Dalvik.
- Более высокое использование памяти стека.
Android 8.0 решает эти проблемы.
Больше встраивания
Начиная с Android 6.0, ART может встраивать любой вызов в одни и те же файлы dex, но может встраивать только конечные методы из разных файлов dex. Причин такого ограничения было две:
- Для встраивания из другого файла dex требуется использовать кеш dex этого другого файла dex, в отличие от встраивания того же файла dex, при котором можно просто повторно использовать кеш dex вызывающего абонента. Кэш dex необходим в скомпилированном коде для нескольких инструкций, таких как статические вызовы, загрузка строки или загрузка классов.
- Карты стека кодируют только индекс метода в текущем файле dex.
Чтобы устранить эти ограничения, Android 8.0:
- Удаляет доступ к декс-кэшу из скомпилированного кода (см. также раздел «Удаление декс-кэша»)
- Расширяет кодировку карты стека.
Улучшения синхронизации
Команда ART настроила пути кода MonitorEnter/MonitorExit и уменьшила нашу зависимость от традиционных барьеров памяти в ARMv8, заменив их новыми инструкциями (получение/выпуск), где это возможно.
Более быстрые собственные методы
Более быстрые собственные вызовы собственного интерфейса Java (JNI) доступны с использованием аннотаций @FastNative
и @CriticalNative
. Эти встроенные оптимизации среды выполнения ART ускоряют переходы JNI и заменяют устаревшую нотацию !bang JNI . Аннотации не влияют на неродные методы и доступны только для кода платформы Java Language в bootclasspath
(без обновлений Play Store).
Аннотация @FastNative
поддерживает нестатические методы. Используйте это, если метод обращается к jobject
как к параметру или возвращаемому значению.
Аннотация @CriticalNative
обеспечивает еще более быстрый способ запуска собственных методов со следующими ограничениями:
- Методы должны быть статическими — никаких объектов для параметров, возвращаемых значений или неявного
this
. - В собственный метод передаются только примитивные типы.
- Собственный метод не использует параметры
JNIEnv
иjclass
в определении функции. - Метод необходимо зарегистрировать в
RegisterNatives
, а не полагаться на динамическое связывание JNI.
@FastNative
может повысить производительность собственных методов до 3 раз, а @CriticalNative
до 5 раз. Например, переход JNI, измеренный на устройстве Nexus 6P:
Вызов собственного интерфейса Java (JNI) | Время выполнения (в наносекундах) |
---|---|
Обычный JNI | 115 |
!бац JNI | 60 |
@FastNative | 35 |
@CriticalNative | 25 |