Cómo crear y medir perfiles de Baseline manualmente

Te recomendamos que automatices la generación de reglas de perfil con la biblioteca de Jetpack Macrobenchmark para reducir el esfuerzo manual y aumentar la escalabilidad general. Sin embargo, es posible crear y medir de forma manual las reglas de perfil en tu app.

Cómo definir manualmente reglas de perfil

Puedes definir manualmente reglas de perfil en una app o un módulo de biblioteca. Para ello, crea un archivo llamado baseline-prof.txt ubicado en el directorio src/main. Esta es la misma carpeta que contiene el archivo AndroidManifest.xml.

El archivo especifica una regla por línea. Cada regla representa un patrón para buscar coincidencias de métodos o clases en la app o biblioteca que se debe optimizar.

La sintaxis de estas reglas es un superconjunto del formato de perfil de ART legible por humanos (HRF) cuando se usa adb shell profman --dump-classes-and-methods. La sintaxis es similar a la sintaxis para descriptores y firmas, pero permite usar comodines para simplificar el proceso de escritura de reglas.

En los siguientes ejemplos, se muestran algunas reglas del Perfil de Baseline que se incluyen en la biblioteca de 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;

Puedes intentar modificar las reglas de perfil en este proyecto de ejemplo de Compiler Explorer. Ten en cuenta que Compiler Explorer solo admite el formato de perfil de ART legible por humanos (HRF), por lo que no se admiten comodines.

Sintaxis de reglas

Estas reglas adoptan una de dos formas para referirse a métodos o clases.

[FLAGS][CLASS_DESCRIPTOR]->[METHOD_SIGNATURE]

Una regla de clase usa el siguiente patrón:

[CLASS_DESCRIPTOR]

Consulta la siguiente tabla para obtener una descripción detallada:

Sintaxis Descripción
FLAGS Representa uno o más de los caracteres H, S y P para indicar si este método debe marcarse como Hot, Startup o Post Startup en relación con el tipo de inicio.

Un método con la marca H indica que es un método "caliente", lo que significa que se lo llama muchas veces durante la vida útil de la app.

Un método con la marca S indica que es un método al que se llama durante el inicio.

Un método con la marca P indica que es un método al que se llama después del inicio.

Una clase presente en este archivo indica que se usa durante el inicio y debe asignarse previamente en el montón para evitar el costo de cargarla. El compilador de ART utiliza varias estrategias de optimización, como la compilación AOT de estos métodos y la optimización de diseños en el archivo AOT generado.
CLASS_DESCRIPTOR Es el descriptor de la clase del método al que se hace referencia. Por ejemplo, androidx.compose.runtime.SlotTable tiene un descriptor de Landroidx/compose/runtime/SlotTable;. L está precedido por el formato ejecutable Dalvik (DEX).
METHOD_SIGNATURE Firma del método, incluidos el nombre, los tipos de parámetros y los tipos de datos que se devuelven del método. Por ejemplo:

// LayoutNode.kt

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

en LayoutNode tiene la firma isPlaced()Z.

Estos patrones pueden tener comodines para tener una sola regla que abarque varios métodos o clases. Si deseas obtener asistencia guiada cuando escribas con la sintaxis de reglas en Android Studio, consulta el complemento de Perfiles de Baseline para Android.

Un ejemplo de una regla de comodín podría verse de la siguiente manera:

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

Tipos admitidos en reglas de Perfil de Baseline

Las reglas del Perfil de Baseline admiten los siguientes tipos. Si deseas obtener información detallada sobre estos tipos, consulta el formato ejecutable Dalvik (DEX).

Carácter Tipo Descripción
B byte Byte firmado
C char Punto de código de caracteres Unicode codificado en UTF-16
D double Valor de punto flotante de doble precisión
F float Valor de punto flotante de precisión simple
I int Entero
J long Entero largo
S short Entero corto firmado
V void Nulo
Z boolean Verdadero o falso
L (nombre de clase) reference Una instancia de un nombre de clase

Además, las bibliotecas pueden definir reglas que se empaquetan en artefactos de AAR. Cuando compilas un APK para incluir estos artefactos, las reglas se combinan (de manera similar a como se realiza la combinación de manifiestos) y se compilan en un perfil de ART binario compacto que es específico del APK.

ART aprovecha este perfil cuando se usa el APK en los dispositivos para compilar a través de AOT un subconjunto específico de la aplicación al momento de la instalación en Android 9 (nivel de API 28) o Android 7 (nivel de API 24) cuando se usa ProfileInstaller.

Cómo recopilar perfiles de Baseline de forma manual

Puedes generar un Perfil de Baseline de forma manual sin configurar la biblioteca de Macrobenchmark ni crear automatizaciones de la IU de tus recorridos críticos del usuario. Si bien recomendamos el uso de macrocomparativas, es posible que no siempre sea posible. Por ejemplo, si usas un sistema de compilación que no es de Gradle, no puedes usar el complemento de Gradle para perfiles de Baseline. En esos casos, puedes recopilar de forma manual las reglas del Perfil de Baseline. Esto es mucho más fácil si usas un dispositivo o emulador con el nivel de API 34 o superior. Si bien es posible con niveles de API inferiores, se requiere acceso raíz y debes usar un emulador que ejecute una imagen del AOSP. Puedes recopilar reglas directamente si haces lo siguiente:

  1. Instala una versión de actualización de tu app en un dispositivo de prueba. El tipo de compilación de la app debe estar optimizado para R8 y no debe ser depurable para lograr un perfil preciso.
  2. Asegúrate de que los perfiles no estén ya compilados.

    Nivel de API 34 y niveles superiores

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

    Nivel de API 33 y niveles inferiores

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

    Si tu APK tiene una dependencia en la biblioteca de Profile Installer de Jetpack, esta biblioteca inicia un perfil en el primer inicio del APK. Esto puede interferir en el proceso de generación de perfiles, por lo que debes inhabilitarlo con el siguiente comando:

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

  3. Ejecuta la app y navega de forma manual por los recorridos críticos del usuario para los que quieres recopilar un perfil.
  4. Indica a ART que vuelque los perfiles. Si tu APK tiene una dependencia en la biblioteca de Profile Installer de Jetpack, úsala para volcar los perfiles:

    adb shell am broadcast -a androidx.profileinstaller.action.SAVE_FILE $PACKAGE_NAME/androidx.profileinstaller.ProfileInstallReceiver
    adb shell am force-stop $PACKAGE_NAME
    
    Si no usas Profile Installer, vuelca los perfiles manualmente en un emulador con el siguiente comando:

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

  5. Espera al menos cinco segundos para permitir que se complete la generación de perfiles.
  6. Convierte en texto los perfiles binarios que se generan:

    Nivel de API 34 y niveles superiores

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

    Nivel de API 33 y niveles inferiores

    Determina si se creó un perfil de referencia o un perfil actual. Un perfil de referencia se encuentra en la siguiente ubicación:

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

    Un perfil actual se encuentra en la siguiente ubicación:

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

    Determina la ubicación del APK:

    adb root
    adb shell pm path $PACKAGE_NAME
    

    Realiza la conversión:

    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. Usa adb para recuperar el perfil volcado del dispositivo:

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

Esto extrae las reglas de perfil generadas y las instala en el módulo de tu app. La próxima vez que compiles la app, se incluirá el Perfil de Baseline. Para verificar esto, sigue los pasos que se indican en Problemas de instalación.

Cómo medir manualmente las mejoras de la app

Te recomendamos que uses comparativas para medir las mejoras de la app. Sin embargo, si deseas medir las mejoras de forma manual, puedes comenzar por medir el inicio de la app no optimizado como referencia.

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"

A continuación, transfiere el Perfil de Baseline.

# 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 si el paquete se optimizó durante la instalación, ejecuta el siguiente comando:

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

El resultado debe indicar que el paquete está compilado:

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

Ahora, puedes medir el rendimiento del inicio de la app como antes, pero sin restablecer el estado compilado. Asegúrate de no restablecer el estado compilado del paquete.

# 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"

Perfiles de Baseline y profgen

En esta sección, se describe lo que hace la herramienta profgen cuando se compila una versión binaria compacta de un perfil de Baseline.

Profgen-cli ayuda con la compilación de perfiles, la introspección y la transpilación de perfiles de ART, por lo que se pueden instalar en dispositivos con Android, independientemente de la versión del SDK de destino.

Profgen-cli es una CLI que compila el HRF de un Perfil de Baseline a su formato compilado. La CLI también se envía al repositorio cmdline-tools como parte del SDK de Android.

Estas funciones están disponibles en la rama studio-main:

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

Cómo crear perfiles binarios compactos con Profgen-cli

Los comandos disponibles con Profgen-cli son bin, validate y dumpProfile. Para ver los comandos disponibles, usa 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

Usa el comando bin para generar el perfil binario compacto. A continuación, se muestra un ejemplo de invocación:

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

Para ver las opciones disponibles, usa 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

El primer argumento representa la ruta de acceso al HRF baseline-prof.txt.

Profgen-cli también necesita la ruta de acceso a la compilación de lanzamiento del APK y un mapa de ofuscación que se usa para ofuscar el APK cuando se usa R8 o ProGuard. De esta manera, profgen puede traducir símbolos de origen en el HRF a sus nombres ofuscados correspondientes cuando se compila el perfil compilado.

Debido a que los formatos de perfiles de ART no son compatibles con versiones anteriores ni posteriores, proporciona un formato de perfil para que profgen empaquete los metadatos del perfil (profm) que puedes usar para transcodificar un formato de perfil de ART a otro cuando se requiera.

Formatos de perfil y versiones de plataforma

Las siguientes opciones están disponibles cuando eliges un formato de perfil:

Formato del perfil Versión de plataforma Nivel de API
v0_1_5_s Android S+ 31+
v0_1_0_p Android P, Q y 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

Copia los archivos de salida baseline.prof y baseline.profm en la carpeta assets o dexopt en el APK.

Mapas de ofuscación

Solo debes proporcionar el mapa de ofuscación si el HRF utiliza símbolos de origen. Si el HRF se genera a partir de una compilación de lanzamiento ya ofuscada y no se requiere asignación, puedes ignorar esa opción y copiar los resultados en la carpeta assets o dexopt.

Instalación tradicional de perfiles de Baseline

Tradicionalmente, los perfiles de Baseline se entregan a un dispositivo de una de dos maneras.

Usa install-multiple con DexMetadata

En los dispositivos que ejecutan la API 28 y versiones posteriores, el cliente de Play descarga el APK y la carga útil de DexMetadata (DM) para la instalación de una versión de APK. El DM contiene la información de perfil que se pasa al Administrador de paquetes del dispositivo.

El APK y el DM se instalan como parte de una sola sesión de instalación usando algo como lo siguiente:

adb install-multiple base.apk base.dm

Jetpack ProfileInstaller

En los dispositivos que ejecutan el nivel de API 29 y versiones posteriores, la biblioteca de Jetpack ProfileInstaller proporciona un mecanismo alternativo para instalar un perfil empaquetado en assets o dexopt después de instalar el APK en el dispositivo. ProfileInstallReceiver o la app invocan directamente a ProfileInstaller.

La biblioteca de ProfileInstaller transcodifica el perfil según la versión del SDK del dispositivo de destino y lo copia en el directorio cur del dispositivo (un directorio de etapa de pruebas específico del paquete para los perfiles de ART en el dispositivo).

Una vez que el dispositivo está inactivo, se recoge el perfil a través de un proceso llamado bg-dexopt en el dispositivo.

Transferencia de un perfil de Baseline

En esta sección, se describe cómo instalar un perfil de Baseline dado un APK.

Transmite con androidx.profileinstaller

En dispositivos que ejecutan el nivel de API 24 y versiones posteriores, puedes transmitir un comando para instalar el 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>

ProfileInstaller no está presente en la mayoría de los APKs con perfiles de Baseline, lo que se cumple en alrededor de 77,000 de 450,000 apps en Play, aunque está presente de forma efectiva en todos los APKs que usan Compose. Esto se debe a que las bibliotecas pueden proporcionar perfiles sin declarar una dependencia en ProfileInstaller. Agregar una dependencia en cada biblioteca con un perfil se aplica a partir de Jetpack.

Usa install-multiple con profgen o DexMetaData

En dispositivos que ejecutan la API 28 y versiones posteriores, puedes transferir un perfil de Baseline sin tener que tener la biblioteca de ProfileInstaller en la app.

Para hacerlo, usa 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 admitir las divisiones de APK, ejecuta los pasos de extracción de perfiles anteriores una vez por APK. En el momento de la instalación, pasa cada APK y el archivo .dm asociado para asegurarte de que coincidan el APK y los nombres .dm:

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

Verificación

Para verificar que el perfil esté instalado correctamente, puedes usar los pasos que se indican en Cómo medir de forma manual las mejoras de la app.

Cómo volcar el contenido de un perfil binario

Para examinar el contenido de una versión binaria compacta de un perfil de Baseline, usa la opción dumpProfile de 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

dumpProfile necesita el APK, ya que la representación binaria compacta solo almacena desplazamientos DEX y, por lo tanto, los necesita para reconstruir nombres de clases y métodos.

El modo estricto está habilitado de forma predeterminada, lo que realiza una verificación de compatibilidad del perfil de los archivos DEX en el APK. Si intentas depurar perfiles que generó otra herramienta, es posible que obtengas fallas de compatibilidad que te impidan volcar para la investigación. En esos casos, puedes inhabilitar el modo estricto con --strict false. Sin embargo, en la mayoría de los casos, mantén habilitado el modo estricto.

El mapa de ofuscación es opcional. Cuando se proporciona, ayuda a reasignar los símbolos ofuscados a sus versiones legibles por humanos para facilitar su uso.