Criar e avaliar perfis de referência manualmente

É altamente recomendável automatizar a geração de regras de perfil usando a biblioteca Macrobenchmark do Jetpack para reduzir o esforço manual e aumentar a escalonabilidade geral. No entanto, é possível criar e medir manualmente as regras de perfil no seu app.

Definir regras de perfil manualmente

Para definir as regras de perfil manualmente em um app ou módulo de biblioteca, crie um arquivo chamado baseline-prof.txt no diretório src/main. Essa é a mesma pasta que contém o arquivo AndroidManifest.xml.

O arquivo especifica uma regra por linha. Cada regra representa um padrão de métodos ou classes de correspondência que precisa ser otimizado no app ou na biblioteca.

A sintaxe dessas regras é um superconjunto do formato de perfil do ART legível por humanos (HRF, na sigla em inglês) ao usar adb shell profman --dump-classes-and-methods. Ela é semelhante à sintaxe para descritores e assinaturas, mas permite usar caracteres curinga para simplificar o processo de criação de regras.

O exemplo abaixo mostram algumas regras de perfil de referência incluídas na biblioteca do Jetpack Compose:

HSPLandroidx/compose/runtime/ComposerImpl;->updateValue(Ljava/lang/Object;)V
HSPLandroidx/compose/runtime/ComposerImpl;->updatedNodeCount(I)I
HLandroidx/compose/runtime/ComposerImpl;->validateNodeExpected()V
PLandroidx/compose/runtime/CompositionImpl;->applyChanges()V
HLandroidx/compose/runtime/ComposerKt;->findLocation(Ljava/util/List;I)I
Landroidx/compose/runtime/ComposerImpl;

Tente modificar as regras de perfil neste exemplo de projeto do Compiler Explorer. O Compiler Explorer oferece suporte apenas ao formato de perfil do ART legível por humanos (HRF, na sigla em inglês). Portanto, não há suporte para caracteres curinga.

Sintaxe de regras

Essas regras podem ter uma de duas formas destinadas a métodos ou classes:

[FLAGS][CLASS_DESCRIPTOR]->[METHOD_SIGNATURE]

Uma regra de classe usa este padrão:

[CLASS_DESCRIPTOR]

Consulte a tabela abaixo para uma descrição detalhada:

Sintaxe Descrição
FLAGS Representa um ou mais dos caracteres H, S e P para indicar se esse método será sinalizado como Hot, Startup ou Post Startup em relação ao tipo de inicialização.

Um método com a flag H indica que ele é "quente", ou seja, chamado várias vezes durante a vida útil do app.

Um método com a flag S indica que ele é chamado na inicialização.

Um método com a flag P indica que ele é chamado após a inicialização.

Uma classe presente nesse arquivo indica que ela é usada durante a inicialização e precisa ser pré-alocada no heap para evitar o custo do carregamento. O compilador do ART emprega várias estratégias de otimização, como a compilação antecipada desses métodos e otimizações de layout no arquivo gerado antecipadamente.
CLASS_DESCRIPTOR Descritor da classe do método. Por exemplo, androidx.compose.runtime.SlotTable tem um descritor de Landroidx/compose/runtime/SlotTable;. Aqui, L é anexado ao formato Dalvik Executable (DEX).
METHOD_SIGNATURE Assinatura do método, incluindo o nome, os tipos de parâmetro e os tipos de retorno. Por exemplo:

// LayoutNode.kt

fun isPlaced():Boolean {
// ...
}

em LayoutNode tem a assinatura isPlaced()Z.

Esses padrões podem ter caracteres curinga para que uma única regra inclua vários métodos ou classes. Para receber assistência guiada ao programar usando a sintaxe de regra no Android Studio, consulte o plug-in de perfis de referência do Android.

Este é um exemplo de regra de caractere curinga:

HSPLandroidx/compose/ui/layout/**->**(**)**

Tipos com suporte nas regras de perfil de referência

As regras de perfil de referência oferecem suporte aos tipos abaixo. Para conferir detalhes sobre esses tipos, consulte o formato Dalvik Executable (DEX).

Caractere Tipo Descrição
B byte Byte assinado
C char Ponto de código de caractere Unicode codificado em UTF-16
D double Valor de ponto flutuante de precisão dupla
F float Valor de ponto flutuante de precisão única
I int Número inteiro
J long Número inteiro longo
S short Número inteiro curto assinado
V void Vazio
Z boolean Verdadeiro ou falso
L (nome da classe) reference Uma instância de um nome de classe

Além disso, as bibliotecas podem definir regras que são empacotadas em artefatos do AAR. Ao criar um APK para incluir esses artefatos, as regras são mescladas (de maneira semelhante à mesclagem de manifestos) e compiladas em um perfil ART binário compacto específico para o APK.

O ART aproveita esse perfil quando o APK é usado em dispositivos para compilar antecipadamente um subconjunto específico do app durante a instalação no Android 9 (nível 28 da API) ou Android 7 (nível 24 da API) com o ProfileInstaller.

Coletar perfis de referência manualmente

Você pode gerar um perfil de referência manualmente sem configurar a biblioteca Macrobenchmark e criar automações de interface para as jornadas ideais do usuário. Embora o uso de Macrobenchmarks seja recomendado, isso nem sempre é possível. Por exemplo, se você estiver usando um sistema de build que não seja o Gradle, não será possível usar o plug-in do perfil de referência para Gradle. Nesses casos, é possível coletar manualmente as regras de perfil de referência. Isso será muito mais fácil se você usar um dispositivo ou emulador com a API 34 ou mais recente. É possível fazer isso com níveis de API mais baixos, mas exige acesso root, e você precisaria usar um emulador com uma imagem AOSP. Para coletar regras diretamente, siga estas etapas:

  1. Instale uma versão de lançamento do app em um dispositivo de teste. Para ter um perfil preciso, o tipo de build do app precisa ser otimizado para R8 e não pode ser depurável.
  2. Confira se os perfis já não estão compilados.

    API 34 e mais recentes

    adb shell cmd package compile -f -m verify $PACKAGE_NAME
    adb shell pm art clear-app-profiles $PACKAGE_NAME
    

    API 33 e anteriores

    adb root
    adb shell cmd package compile --reset $PACKAGE_NAME
    

    Se o APK tiver uma dependência na biblioteca Profile Installer do Jetpack, a biblioteca vai abrir um perfil na primeira inicialização do APK. Isso pode interferir na geração de perfil. Portanto, desative o processo com este comando:

    adb shell am broadcast -a androidx.profileinstaller.action.SKIP_FILE $PACKAGE_NAME/androidx.profileinstaller.ProfileInstallReceiver
    

  3. Execute o app e navegue manualmente pelas jornadas ideais do usuário para que você quer coletar um perfil.
  4. Execute o ART para despejar os perfis. Se o APK tiver uma dependência na biblioteca Profile Installer do Jetpack, use-a para despejar os perfis:

    adb shell am broadcast -a androidx.profileinstaller.action.SAVE_FILE $PACKAGE_NAME/androidx.profileinstaller.ProfileInstallReceiver
    adb shell am force-stop $PACKAGE_NAME
    
    Se você não estiver usando o Profile Installer, despeje os perfis manualmente em um emulador usando este comando:

    adb root
    adb shell killall -s SIGUSR1 $PACKAGE_NAME
    adb shell am force-stop $PACKAGE_NAME
    

  5. Aguarde pelo menos cinco segundos para que a geração do perfil seja concluída.
  6. Converta os perfis binários gerados para texto:

    API 34 e mais recentes

    adb shell pm dump-profiles --dump-classes-and-methods $PACKAGE_NAME
    

    API 33 e anteriores

    Determine se um perfil de referência ou um perfil atual foi criado. Um perfil de referência fica localizado neste local:

    /data/misc/profiles/ref/$$PACKAGE_NAME/primary.prof
    

    Um perfil atual fica localizado neste local:

    /data/misc/profiles/cur/0/$PACKAGE_NAME/primary.prof
    

    Determine a localização do APK:

    adb root
    adb shell pm path $PACKAGE_NAME
    

    Realize a conversão:

    adb root
    adb shell profman --dump-classes-and-methods --profile-file=$PROFILE_PATH --apk=$APK_PATH > /data/misc/profman/$PACKAGE_NAME-primary.prof.txt
    

  7. Use adb para extrair o perfil despejado no dispositivo:

    adb pull /data/misc/profman/$PACKAGE_NAME-primary.prof.txt PATH_TO_APP_MODULE/src/main/
    

Isso extrai as regras de perfil geradas e as instala no módulo do app. Na próxima vez que você criar o app, o perfil de referência será incluído. Para conferir, siga as etapas em Problemas de instalação.

Avaliar manualmente as melhorias do app

Recomendamos que você meça as melhorias no app com uma comparação. No entanto, se você quiser medir as melhorias manualmente, comece medindo a inicialização do app não otimizada para referência.

PACKAGE_NAME=com.example.app
# Force Stop App
adb shell am force-stop $PACKAGE_NAME
# Reset compiled state
adb shell cmd package compile --reset $PACKAGE_NAME
# Measure App startup
# This corresponds to `Time to initial display` metric.
adb shell am start-activity -W -n $PACKAGE_NAME/.ExampleActivity \
 | grep "TotalTime"

Em seguida, transfira o perfil de referência por sideload.

# Unzip the Release APK first.
unzip release.apk
# Create a ZIP archive.
# The name should match the name of the APK.
# Copy `baseline.prof{m}` and rename it `primary.prof{m}`.
cp assets/dexopt/baseline.prof primary.prof
cp assets/dexopt/baseline.profm primary.profm
# Create an archive.
zip -r release.dm primary.prof primary.profm
# Confirm that release.dm only contains the two profile files:
unzip -l release.dm
# Archive:  release.dm
#   Length      Date    Time    Name
# ---------  ---------- -----   ----
#      3885  1980-12-31 17:01   primary.prof
#      1024  1980-12-31 17:01   primary.profm
# ---------                     -------
#                               2 files
# Install APK + Profile together.
adb install-multiple release.apk release.dm

Para verificar se o pacote foi otimizado na instalação, execute o comando abaixo:

# Check dexopt state.
adb shell dumpsys package dexopt | grep -A 1 $PACKAGE_NAME

A saída precisa declarar que o pacote está compilado:

[com.example.app]
  path: /data/app/~~YvNxUxuP2e5xA6EGtM5i9A==/com.example.app-zQ0tkJN8tDrEZXTlrDUSBg==/base.apk
  arm64: [status=speed-profile] [reason=install-dm]

Agora, é possível medir a performance de inicialização do app como anteriormente, mas sem redefinir o estado compilado. Não redefina o estado compilado do pacote.

# Force stop app
adb shell am force-stop $PACKAGE_NAME
# Measure app startup
adb shell am start-activity -W -n $PACKAGE_NAME/.ExampleActivity \
 | grep "TotalTime"

Perfis de referência e profgen

Esta seção descreve o que a ferramenta profgen faz ao criar uma versão binária compacta de um perfil de referência.

O Profgen-cli (link em inglês) ajuda na compilação, introspecção e transpilação de perfis do ART. Assim, eles podem ser instalados em dispositivos com tecnologia Android, independentemente da versão do SDK de destino.

Profgen-cli é uma CLI que compila o HRF de um perfil de referência para o formato compilado. A CLI também é enviada no repositório cmdline-tools como parte do SDK do Android.

Esses recursos estão disponíveis na ramificação studio-main:

➜ ../cmdline-tools/latest/bin
apkanalyzer
avdmanager
lint
profgen
retrace
screenshot2
sdkmanager

Criar perfis binários compactos com o Profgen-cli

Os comandos disponíveis no Profgen-cli são bin, validate e dumpProfile. Para conferir os comandos disponíveis, use profgen --help:

profgen --help
Usage: profgen options_list
Subcommands:
    bin - Generate Binary Profile
    validate - Validate Profile
    dumpProfile - Dump a binary profile to a HRF

Options:
    --help, -h -> Usage info

Use o comando bin para gerar o perfil binário compacto. Confira a seguir um exemplo de invocação:

profgen bin ./baseline-prof.txt \
  --apk ./release.apk \
  --map ./obfuscation-map.txt \
  --profile-format v0_1_0_p \
  --output ./baseline.prof \

Para conferir as opções disponíveis, use profgen bin options_list:

Usage: profgen bin options_list
Arguments:
    profile -> File path to Human Readable profile { String }
Options:
    --apk, -a -> File path to apk (always required) { String }
    --output, -o -> File path to generated binary profile (always required)
    --map, -m -> File path to name obfuscation map { String }
    --output-meta, -om -> File path to generated metadata output { String }
    --profile-format, -pf [V0_1_0_P] -> The ART profile format version
      { Value should be one of [
         v0_1_5_s, v0_1_0_p, v0_0_9_omr1, v0_0_5_o, v0_0_1_n
        ]
      }
    --help, -h -> Usage info

O primeiro argumento representa o caminho para o HRF baseline-prof.txt.

O Profgen-cli também precisa do caminho para o build de lançamento do APK e de um mapa de ofuscação, que é usado para ofuscar o APK ao usar o R8 ou o Proguard. Dessa forma, profgen pode traduzir símbolos de origem no HRF para os nomes ofuscados correspondentes ao criar o perfil compilado.

Como os formatos de perfis do ART não são compatíveis com versões anteriores ou posteriores, forneça um formato para que profgen empacote os metadados do perfil (profm) que você pode usar para transcodificar um formato do perfil do ART para outro quando necessário.

Formatos de perfil e versões de plataforma

As seguintes opções estão disponíveis ao escolher um formato de perfil:

Formato do perfil Versão da plataforma Nível da API
v0_1_5_s Android S+ 31+
v0_1_0_p Android P, Q e R 28-30
v0_0_9_omr1 Android O MR1 27
v0_0_5_o Android O 26
v0_0_1_n Android N 24-25

Copie os arquivos de saída baseline.prof e baseline.profm na pasta assets ou dexopt no APK.

Mapas de ofuscação

Você só vai precisar fornecer o mapa de ofuscação se o HRF usar símbolos de origem. Se o HRF for gerado a partir de um build de lançamento que já está ofuscado e não houver necessidade de mapeamento, ignore essa opção e copie as saídas na pasta assets ou dexopt.

Instalação tradicional de perfis de referência

Tradicionalmente, os perfis de referência são fornecidos a um dispositivo de duas maneiras.

Usar install-multiple com DexMetadata

Em dispositivos com a API 28 e versões mais recentes, o cliente do Google Play faz o download do payload do APK e do DexMetadata (DM) para uma versão do APK que está sendo instalada. O DM contém as informações de perfil que são repassadas ao Gerenciador de pacotes no dispositivo.

O APK e o DM são instalados como parte de uma única sessão de instalação usando algo como:

adb install-multiple base.apk base.dm

Jetpack ProfileInstaller

Em dispositivos que executam a API de nível 29 e mais recentes, oJetpack ProfileInstaller fornece um mecanismo alternativo para instalar um perfil em pacote em assets ou dexopt depois que o APK for instalado no dispositivo. ProfileInstaller é invocado pela ProfileInstallReceiver ou pelo app diretamente.

A biblioteca ProfileInstaller transcodifica o perfil com base na versão do SDK do dispositivo de destino e copia o perfil para o diretório cur no dispositivo (um diretório de preparo específico do pacote para perfis do ART no dispositivo).

Quando o dispositivo está inativo, o perfil é selecionado por um processo chamado bg-dexopt no dispositivo.

Transferir um perfil de referência por sideload

Esta seção descreve como instalar um perfil de referência a partir de um APK.

Transmitir com androidx.profileinstaller

Em dispositivos com a API 24 e mais recentes, você pode transmitir um comando para instalar o perfil:

# Broadcast the install profile command - moves binary profile from assets
#     to a location where ART uses it for the next compile.
#     When successful, the following command prints "1":
adb shell am broadcast \
    -a androidx.profileinstaller.action.INSTALL_PROFILE \
    <pkg>/androidx.profileinstaller.ProfileInstallReceiver

# Kill the process
am force-stop <pkg>

# Compile the package based on profile
adb shell cmd package compile -f -m speed-profile <pkg>

O ProfileInstaller não está presente na maioria dos APKs com perfis de referência, que estão em cerca de 77 mil de 450 mil apps no Google Play, embora esteja presente em todos os APKs que usam o Compose. Isso ocorre porque as bibliotecas podem fornecer perfis sem declarar uma dependência no ProfileInstaller. A adição de uma dependência em cada biblioteca com um perfil se aplica a partir do Jetpack.

Usar install-multiple com profgen ou DexMetaData

Em dispositivos com a API 28 e versões mais recentes, você pode transferir um perfil de referência por sideload sem precisar ter a biblioteca ProfileInstaller no app.

Para isso, use o Profgen-cli:

profgen extractProfile \
        --apk app-release.apk \
        --output-dex-metadata app-release.dm \
        --profile-format V0_1_5_S # Select based on device and the preceding table.

# Install APK and the profile together
adb install-multiple appname-release.apk appname-release.dm

Para oferecer suporte a divisões de APK, execute as etapas de extração anteriores do perfil uma vez por APK. No momento da instalação, transmita cada APK e o arquivo .dm associado, garantindo que os nomes do APK e do .dm correspondam:

adb install-multiple appname-base.apk appname-base.dm \
appname-split1.apk appname-split1.dm

Verificação

Para verificar se o perfil está instalado corretamente, siga as etapas em Medir manualmente as melhorias no app.

Despejar o conteúdo de um perfil binário

Para introspecção do conteúdo de uma versão binária compacta de um perfil de referência, use a opção dumpProfile do Profgen-cli:

Usage: profgen dumpProfile options_list
Options:
    --profile, -p -> File path to the binary profile (always required)
    --apk, -a -> File path to apk (always required) { String }
    --map, -m -> File path to name obfuscation map { String }
    --strict, -s [true] -> Strict mode
    --output, -o -> File path for the HRF (always required) { String }
    --help, -h -> Usage info

O dumpProfile precisa do APK porque a representação binária compacta armazena apenas os deslocamentos DEX e, portanto, precisa que ele reconstrua nomes de classes e métodos.

O modo estrito está ativado por padrão e executa uma verificação de compatibilidade do perfil para os arquivos DEX no APK. Se você estiver tentando depurar perfis gerados por outra ferramenta, talvez ocorram falhas de compatibilidade que impedem o despejo para investigação. Nesses casos, é possível desativar o modo estrito com --strict false. No entanto, na maioria dos casos, mantenha o modo restrito ativado.

Um mapa de ofuscação é opcional. Quando fornecido, ele ajuda a remapear símbolos ofuscados em versões legíveis para facilitar o uso.