Usa la optimización guiada por perfil

El sistema de compilación de Android para Android 13 y versiones anteriores admite el uso de la optimización guiada por perfil (PGO) de Clang en módulos de Android nativos que tienen reglas de compilación de diseño. En esta página, se describe la PGO de Clang, cómo generar y actualizar continuamente los perfiles que se usan para la PGO y cómo integrar la PGO con el sistema de compilación (con un caso de uso).

Nota: En este documento, se describe el uso de la PGO en la plataforma de Android. Para obtener información sobre el uso de la PGO desde una app para Android, visita esta página.

Información acerca de la PGO de Clang

Clang puede realizar una optimización guiada por perfil con dos tipos de perfiles:

  • Los perfiles basados en la instrumentación se generan a partir de un programa de destino instrumentado. Estos perfiles son detallados y generan una sobrecarga de tiempo de ejecución alta.
  • Por lo general, los perfiles basados en muestras se producen a partir de contadores de hardware de muestreo. Imponen una sobrecarga de tiempo de ejecución baja y se pueden recopilar sin ninguna instrumentación ni modificación del objeto binario. Son menos detallados que los perfiles basados en la instrumentación.

Todos los perfiles deben generarse a partir de una carga de trabajo representativa que ejercite el comportamiento típico de la app. Si bien Clang admite perfiles basados en AST (-fprofile-instr-generate) y en IR de LLVM (-fprofile-generate)), Android solo admite perfiles basados en IR de LLVM para la PGO basada en instrumentación.

Se necesitan las siguientes marcas para compilar para la recopilación de perfiles:

  • -fprofile-generate para la instrumentación basada en IR. Con esta opción, el backend usa un enfoque de árbol de expansión mínima ponderado para reducir la cantidad de puntos de instrumentación y optimizar su ubicación en los bordes de bajo peso (también usa esta opción para el paso de vinculación). El controlador de Clang pasa automáticamente el tiempo de ejecución de generación de perfiles (libclang_rt.profile-arch-android.a) al vinculador. Esta biblioteca contiene rutinas para escribir los perfiles en el disco cuando se cierra el programa.
  • -gline-tables-only para la recopilación de perfiles basada en muestreos para generar información de depuración mínima.

Se puede usar un perfil para la PGO con -fprofile-use=pathname o -fprofile-sample-use=pathname para perfiles basados en la instrumentación y en el muestreo, respectivamente.

Nota: A medida que se realizan cambios en el código, si Clang ya no puede usar los datos de perfil, genera una advertencia -Wprofile-instr-out-of-date.

Usa la PGO

El uso de la PGO implica los siguientes pasos:

  1. Para compilar la biblioteca o el ejecutable con instrumentación, pasa -fprofile-generate al compilador y al vinculador.
  2. Para recopilar perfiles, ejecuta una carga de trabajo representativa en el objeto binario instrumentado.
  3. Procesa posteriormente los perfiles con la utilidad llvm-profdata (para obtener más información, consulta Cómo manejar archivos de perfil de LLVM).
  4. Usa los perfiles para aplicar la PGO pasando -fprofile-use=<>.profdata al compilador y al vinculador.

Para la PGO en Android, los perfiles deben recopilarse sin conexión y confirmarse junto con el código para garantizar compilaciones reproducibles. Los perfiles se pueden usar a medida que evoluciona el código, pero deben regenerarse periódicamente (o cada vez que Clang advierte que los perfiles están inactivos).

Recopila perfiles

Clang puede usar perfiles recopilados mediante la ejecución de comparativas con una compilación instrumentada de la biblioteca o mediante el muestreo de contadores de hardware cuando se ejecuta la comparativa. En este momento, Android no admite el uso de la recopilación de perfiles basada en el muestreo, por lo que debes recopilar perfiles con una compilación instrumentada:

  1. Identifica una comparativa y el conjunto de bibliotecas que esa comparativa ejecuta de forma colectiva.
  2. Agrega propiedades pgo a las comparativas y bibliotecas (detalles a continuación).
  3. Produce una compilación de Android con una copia instrumentada de estas bibliotecas con lo siguiente:
    make ANDROID_PGO_INSTRUMENT=benchmark

benchmark es un marcador de posición que identifica la recopilación de bibliotecas instrumentadas durante la compilación. Las entradas representativas reales (y, posiblemente, otro ejecutable que se vincula a una biblioteca a la que se le realizan comparativas) no son específicas de la PGO y están fuera del alcance de este documento.

  1. Instala o sincroniza la compilación instrumentada en un dispositivo.
  2. Ejecuta la comparativa para recopilar perfiles.
  3. Usa la herramienta llvm-profdata (que se explica a continuación) para postprocesar los perfiles y prepararlos para que se verifiquen en el árbol fuente.

Usa perfiles durante la compilación

Verifica los perfiles en toolchain/pgo-profiles en un árbol de Android. El nombre debe coincidir con lo que se especifica en la subpropiedad profile_file de la propiedad pgo de la biblioteca. El sistema de compilación pasa automáticamente el archivo de perfil a Clang cuando compila la biblioteca. La variable de entorno ANDROID_PGO_DISABLE_PROFILE_USE se puede establecer en true para inhabilitar temporalmente la PGO y medir su beneficio de rendimiento.

Para especificar directorios de perfil adicionales específicos del producto, agrégalos a la variable de compilación PGO_ADDITIONAL_PROFILE_DIRECTORIES en un BoardConfig.mk. Si se especifican rutas de acceso adicionales, los perfiles de estas rutas de acceso anulan los de toolchain/pgo-profiles.

Cuando se genera una imagen de lanzamiento con el objetivo dist para make, el sistema de compilación escribe los nombres de los archivos de perfil faltantes en $DIST_DIR/pgo_profile_file_missing.txt. Puedes verificar este archivo para ver qué archivos de perfil se descartaron por accidente (lo que inhabilita la PGO de forma silenciosa).

Habilita la PGO en los archivos Android.bp

Para habilitar la PGO en los archivos Android.bp de los módulos nativos, simplemente especifica la propiedad pgo. Esta propiedad tiene las siguientes subpropiedades:

Propiedad Descripción
instrumentation Se establece en true para la PGO con instrumentación. El valor predeterminado es false.
sampling Se establece en true para la PGO con muestreo. El valor predeterminado es false.
benchmarks Es una lista de cadenas. Este módulo se compila para generar perfiles si se especifica cualquier comparativa de la lista en la opción de compilación ANDROID_PGO_INSTRUMENT.
profile_file Es el archivo de perfil (en relación con toolchain/pgo-profile) que se usará con la PGO. La compilación advierte que este archivo no existe cuando lo agrega a $DIST_DIR/pgo_profile_file_missing.txt a menos que la propiedad enable_profile_use esté configurada como false O la variable de compilación ANDROID_PGO_NO_PROFILE_USE esté configurada como true.
enable_profile_use Establece en false si los perfiles no deben usarse durante la compilación. Se puede usar durante el inicio para habilitar la recopilación de perfiles o para inhabilitar temporalmente la PGO. El valor predeterminado es true.
cflags Es una lista de marcas adicionales que se usarán durante una compilación instrumentada.

Ejemplo de un módulo con PGO:

cc_library {
    name: "libexample",
    srcs: [
        "src1.cpp",
        "src2.cpp",
    ],
    static: [
        "libstatic1",
        "libstatic2",
    ],
    shared: [
        "libshared1",
    ]
    pgo: {
        instrumentation: true,
        benchmarks: [
            "benchmark1",
            "benchmark2",
        ],
        profile_file: "example.profdata",
    }
}

Si las comparativas benchmark1 y benchmark2 ejercen un comportamiento representativo para las bibliotecas libstatic1, libstatic2 o libshared1, la propiedad pgo de estas bibliotecas también puede incluir las comparativas. El módulo defaults en Android.bp puede incluir una especificación pgo común para un conjunto de bibliotecas para evitar repetir las mismas reglas de compilación para varios módulos.

Para seleccionar diferentes archivos de perfil o inhabilitar de forma selectiva la PGO para una arquitectura, especifica las propiedades profile_file, enable_profile_use y cflags por arquitectura. Ejemplo (con el objetivo de arquitectura en negrita):

cc_library {
    name: "libexample",
    srcs: [
          "src1.cpp",
          "src2.cpp",
    ],
    static: [
          "libstatic1",
          "libstatic2",
    ],
    shared: [
          "libshared1",
    ],
    pgo: {
         instrumentation: true,
         benchmarks: [
              "benchmark1",
              "benchmark2",
         ],
    }

    target: {
         android_arm: {
              pgo: {
                   profile_file: "example_arm.profdata",
              }
         },
         android_arm64: {
              pgo: {
                   profile_file: "example_arm64.profdata",
              }
         }
    }
}

Para resolver referencias a la biblioteca del tiempo de ejecución de generación de perfiles durante la generación de perfiles basada en la instrumentación, pasa la marca de compilación -fprofile-generate al vinculador. Las bibliotecas estáticas instrumentadas con la PGO, todas las bibliotecas compartidas y cualquier objeto binario que dependa directamente de la biblioteca estática también deben instrumentarse para la PGO. Sin embargo, esas bibliotecas o ejecutables compartidos no necesitan usar perfiles de PGO, y su propiedad enable_profile_use se puede establecer en false. Fuera de esta restricción, puedes aplicar la PGO a cualquier biblioteca estática, biblioteca compartida o ejecutable.

Cómo controlar archivos de perfil de LLVM

La ejecución de una biblioteca o un ejecutable instrumentados produce un archivo de perfil llamado default_unique_id_0.profraw en /data/local/tmp (donde unique_id es un hash numérico único para esta biblioteca). Si este archivo ya existe, el entorno de ejecución de generación de perfiles combina el perfil nuevo con el anterior mientras escribe los perfiles. Ten en cuenta que los desarrolladores de apps no pueden acceder a /data/local/tmp. En su lugar, deben usar /storage/emulated/0/Android/data/packagename/files. Para cambiar la ubicación del archivo de perfil, establece la variable de entorno LLVM_PROFILE_FILE durante el tiempo de ejecución.

Luego, se usa la utilidad llvm-profdata para convertir el archivo .profraw (y, posiblemente, combinar varios archivos .profraw) en un archivo .profdata:

  llvm-profdata merge -output=profile.profdata <.profraw and/or .profdata files>

Luego, profile.profdata se puede verificar en el árbol fuente para usarlo durante la compilación.

Si se cargan varios objetos binarios o bibliotecas instrumentados durante una comparativa, cada biblioteca genera un archivo .profraw independiente con un ID único independiente. Por lo general, todos estos archivos se pueden combinar en un solo archivo .profdata y usarse para la compilación de la PGO. En los casos en que otra comparativa ejecuta una biblioteca, esta se debe optimizar con los perfiles de ambas comparativas. En esta situación, la opción show de llvm-profdata es útil:

  llvm-profdata merge -output=default_unique_id.profdata default_unique_id_0.profraw
llvm-profdata show -all-functions default_unique_id.profdata

Para asignar unique_id a bibliotecas individuales, busca en el resultado de show para cada unique_id un nombre de función que sea único para la biblioteca.

Caso de éxito: PGO para ART

En el caso de éxito, se presenta ART como un ejemplo relacionado. Sin embargo, no es una descripción precisa del conjunto real de bibliotecas perfiladas para ART ni de sus interdependencias.

El compilador anticipado dex2oat en ART depende de libart-compiler.so, que a su vez depende de libart.so. El entorno de ejecución de ART se implementa principalmente en libart.so. Las comparativas del compilador y del entorno de ejecución serán diferentes:

Benchmark Bibliotecas con perfil
dex2oat dex2oat (ejecutable), libart-compiler.so, libart.so
art_runtime libart.so
  1. Agrega la siguiente propiedad pgo a dex2oat, libart-compiler.so:
        pgo: {
            instrumentation: true,
            benchmarks: ["dex2oat",],
            profile_file: "dex2oat.profdata",
        }
  2. Agrega la siguiente propiedad pgo a libart.so:
        pgo: {
            instrumentation: true,
            benchmarks: ["art_runtime", "dex2oat",],
            profile_file: "libart.profdata",
        }
  3. Crea compilaciones instrumentadas para las comparativas de dex2oat y art_runtime con lo siguiente:
        make ANDROID_PGO_INSTRUMENT=dex2oat
        make ANDROID_PGO_INSTRUMENT=art_runtime
  4. Como alternativa, crea una sola compilación instrumentada con todas las bibliotecas instrumentadas con lo siguiente:

        make ANDROID_PGO_INSTRUMENT=dex2oat,art_runtime
        (or)
        make ANDROID_PGO_INSTRUMENT=ALL

    El segundo comando compila todos los módulos habilitados para la PGO para la generación de perfiles.

  5. Ejecuta las comparativas que ejercitan dex2oat y art_runtime para obtener lo siguiente:
    • Tres archivos .profraw de dex2oat (dex2oat_exe.profdata, dex2oat_libart-compiler.profdata y dexeoat_libart.profdata), identificados con el método que se describe en Cómo controlar los archivos de perfil de LLVM
    • Un solo art_runtime_libart.profdata.
  6. Genera un archivo profdata común para el ejecutable dex2oat y libart-compiler.so con lo siguiente:
    llvm-profdata merge -output=dex2oat.profdata \
        dex2oat_exe.profdata dex2oat_libart-compiler.profdata
  7. Para obtener el perfil de libart.so, combina los perfiles de las dos comparativas:
    llvm-profdata merge -output=libart.profdata \
        dex2oat_libart.profdata art_runtime_libart.profdata

    Los recuentos sin procesar de libart.so de los dos perfiles pueden ser dispares porque las comparativas difieren en la cantidad de casos de prueba y la duración durante la que se ejecutan. En este caso, puedes usar una combinación ponderada:

    llvm-profdata merge -output=libart.profdata \
        -weighted-input=2,dex2oat_libart.profdata \
        -weighted-input=1,art_runtime_libart.profdata

    El comando anterior asigna el doble de peso al perfil de dex2oat. El peso real se debe determinar en función del conocimiento o la experimentación del dominio.

  8. Verifica los archivos de perfil dex2oat.profdata y libart.profdata en toolchain/pgo-profiles para usarlos durante la compilación.