O Android Runtime (ART) foi melhorado significativamente na versão Android 8.0. A lista abaixo resume as melhorias que os fabricantes de dispositivos podem esperar no ART.
Coletor de lixo compactador simultâneo
Como anunciado na Google I/O, o ART apresenta um novo coletor de lixo compactante simultâneo (GC) no Android 8.0. Esse coletor compacta a pilha toda vez que o GC é executado e enquanto o app está em execução, com apenas uma pausa curta para processar as raízes da linha de execução. Confira os benefícios:
- O GC sempre compacta a pilha: tamanhos de pilha 32% menores em média em comparação com o Android 7.0.
- A compactação permite a alocação de objetos de ponteiro de aumento local de linha de execução: as alocações são 70% mais rápidas do que no Android 7.0.
- Oferece 85% menos tempo de pausa para o comparativo H2 em comparação com o GC 7.0 do Android.
- Os tempos de pausa não são mais dimensionados com o tamanho do heap. Os apps podem usar heaps grandes sem se preocupar com o lag.
- Detalhes da implementação do GC: barreiras de leitura:
- As barreiras de leitura são uma pequena quantidade de trabalho realizada para cada leitura de campo de objeto.
- Elas são otimizadas no compilador, mas podem desacelerar alguns casos de uso.
Otimizações de loop
Uma grande variedade de otimizações de loop são usadas pelo ART na versão do Android 8.0:
- Eliminações de verificação de limites
- Estática: os intervalos são comprovados como estando dentro dos limites no momento da compilação
- Dinâmico: os testes de execução garantem que os loops permaneçam dentro dos limites (deopt caso contrário)
- Eliminações de variáveis de indução
- Remover indução morta
- Substituir a indução usada apenas após o loop por expressões fechadas
- Eliminação de código inoperante dentro do corpo do loop, remoção de loops inteiros que se tornam inoperantes
- Redução de força
- Transformações de loop: reversão, troca, divisão, desenrolamento, unimodular, etc.
- SIMDização (também chamada de vetorização)
O otimizador de loops fica em sua própria passagem de otimização no compilador do ART. A maioria das otimizações de loop é semelhante a otimizações e simplificações em outros lugares. Os desafios surgem com algumas otimizações que reescrevem o CFG de uma forma mais elaborada do que o normal, porque a maioria dos utilitários de CFG (consulte nodes.h) se concentra na criação de um CFG, não na reescrita dele.
Análise da hierarquia de classes
O ART no Android 8.0 usa a análise de hierarquia de classes (CHA, na sigla em inglês), uma otimização do compilador que desvirtualiza chamadas virtuais em chamadas diretas com base nas informações geradas pela análise de hierarquias de classes. As chamadas virtuais são caras, já que são implementadas em torno de uma pesquisa de tabela virtual e exigem algumas cargas dependentes. Além disso, as chamadas virtuais não podem ser inline.
Confira um resumo das melhorias relacionadas:
- Atualização de status de método de implementação única dinâmica: no final do tempo de vinculação de classe, quando a vtable é preenchida, o ART realiza uma comparação de entrada por entrada com a vtable da superclasse.
- Otimização do compilador: o compilador vai aproveitar as informações de implementação única de um método. Se um método A.foo tiver a flag de implementação única definida, o compilador vai desvirtualizar a chamada virtual em uma chamada direta e tentará inlinear a chamada direta como resultado.
- Invalidação do código compilado: também no final do tempo de vinculação de classe, quando as informações de implementação única são atualizadas, se o método A.foo que tinha implementação única, mas esse status agora está inválido, todo o código compilado que depende da suposição de que o método A.foo tem implementação única precisa ter o código compilado invalidado.
- Desotimização: para códigos compilados em tempo real que estão na pilha, a desotimização será iniciada para forçar o código compilado inválido no modo de interpretador e garantir a correção. Um novo mecanismo de desotimização, que é um híbrido de desotimização síncrona e assíncrona, será usado.
Caches inline em arquivos .oat
O ART agora usa caches inline e otimiza os sites de chamada para os quais dados suficientes existem. O recurso de caches inline registra informações adicionais de execução em perfis e as usa para adicionar otimizações dinâmicas à compilação antecipada.
Dexlayout
O Dexlayout é uma biblioteca introduzida no Android 8.0 para analisar arquivos dex e reorganizá-los de acordo com um perfil. O objetivo do Dexlayout é usar informações de perfil de execução para reordenar seções do arquivo dex durante a compilação de manutenção ociosa no dispositivo. Ao agrupar partes do arquivo dex que são acessadas com frequência, os programas podem ter padrões de acesso à memória melhores com uma localidade melhor, economizando RAM e reduzindo o tempo de inicialização.
Como as informações do perfil atualmente só estão disponíveis após a execução dos apps, o dexlayout é integrado à compilação no dispositivo do dex2oat durante a manutenção em modo inativo.
Remoção do cache do Dex
Até o Android 7.0, o objeto DexCache tinha quatro matrizes grandes, proporcionais ao número de determinados elementos no DexFile, a saber:
- strings (uma referência por DexFile::StringId),
- tipos (uma referência por DexFile::TypeId),
- métodos (um ponteiro nativo por DexFile::MethodId),
- campos (um ponteiro nativo por DexFile::FieldId).
Essas matrizes foram usadas para a recuperação rápida de objetos que já resolvemos. No Android 8.0, todas as matrizes foram removidas, exceto a matriz de métodos.
Desempenho do intérprete
O desempenho do interpretador melhorou significativamente na versão do Android 7.0 com a introdução do "mterp", um interpretador com um mecanismo de busca/decodificação/interpretação principal escrito em linguagem assembly. O Mterp é modelado de acordo com o intérprete rápido do Dalvik e oferece suporte a arm, arm64, x86, x86_64, mips e mips64. Para o código computacional, o mterp do ART é aproximadamente comparável ao intérprete rápido do Dalvik. No entanto, em algumas situações, ela pode ser significativamente mais lenta:
- Invocar performance.
- A manipulação de strings e outros usuários pesados de métodos reconhecidos como intrínsecos no Dalvik.
- Maior uso da memória da pilha.
O Android 8.0 resolve esses problemas.
Mais inline
Desde o Android 6.0, o ART pode inlinear qualquer chamada nos mesmos arquivos dex, mas só pode inlinear métodos de folhas de arquivos dex diferentes. Houve dois motivos para essa limitação:
- O inline de outro arquivo dex requer o uso do cache dex desse outro arquivo dex, ao contrário do inline do mesmo arquivo dex, que pode reutilizar o cache do autor da chamada. O cache dex é necessário no código compilado para algumas instruções, como chamadas estáticas, carregamento de string ou carregamento de classe.
- Os mapas de pilha estão codificando apenas um índice de método no arquivo dex atual.
Para resolver essas limitações, o Android 8.0:
- Remove o acesso ao cache dex do código compilado. Consulte também a seção "Remoção do cache dex".
- Expande a codificação do mapa de pilhas.
Melhorias na sincronização
A equipe do ART ajustou os caminhos de código MonitorEnter/MonitorExit e reduziu nossa dependência de barreiras de memória tradicionais no ARMv8, substituindo-as por instruções (aquisição/liberação) mais recentes sempre que possível.
Métodos nativos mais rápidos
Chamadas nativas mais rápidas para a Java Native Interface (JNI) estão disponíveis usando
as anotações @FastNative
e @CriticalNative
. Essas otimizações integradas do tempo de execução
do ART aceleram as transições de JNI e substituem a notação
!bang JNI, que foi descontinuada. As anotações não têm efeito em métodos não nativos
e estão disponíveis apenas para o código da linguagem Java da plataforma no
bootclasspath
(sem atualizações da Play Store).
A anotação @FastNative
oferece suporte a métodos não estáticos. Use essa
opção se um método acessar um jobject
como um parâmetro ou valor de retorno.
A anotação @CriticalNative
oferece uma maneira ainda mais rápida de executar
métodos nativos, com as seguintes restrições:
-
Os métodos precisam ser estáticos, sem objetos para parâmetros, valores de retorno ou um
this
implícito. - Somente tipos primitivos são transmitidos para o método nativo.
-
O método nativo não usa os parâmetros
JNIEnv
ejclass
na definição da função. -
O método precisa ser registrado com
RegisterNatives
em vez de depender do link JNI dinâmico.
O @FastNative
pode melhorar o desempenho do método nativo em até três vezes e
@CriticalNative
em até cinco vezes. Por exemplo, uma transição JNI medida
em um dispositivo Nexus 6P:
Invocação da Java Native Interface (JNI) | Tempo de execução (em nanossegundos) |
---|---|
JNI regular | 115 |
!bang JNI | 60 |
@FastNative |
35 |
@CriticalNative |
25 |