Use otimização guiada por perfil

O sistema de compilação do Android para Android 13 e versões anteriores oferece suporte ao uso da otimização guiada por perfil (PGO) do Clang em módulos Android nativos que possuem regras de compilação de blueprint . Esta página descreve o Clang PGO, como gerar e atualizar continuamente perfis usados ​​para PGO e como integrar o PGO ao sistema de compilação (com caso de uso).

NB: Este documento descreve o uso do PGO na plataforma Android. Para saber mais sobre como usar o PGO em um aplicativo Android, visite esta página .

Sobre Clang PGO

Clang pode realizar otimização guiada por perfil usando dois tipos de perfis:

  • Perfis baseados em instrumentação são gerados a partir de um programa de destino instrumentado. Esses perfis são detalhados e impõem uma alta sobrecarga de tempo de execução.
  • Perfis baseados em amostragem são normalmente produzidos por contadores de hardware de amostragem. Eles impõem uma baixa sobrecarga de tempo de execução e podem ser coletados sem qualquer instrumentação ou modificação no binário. Eles são menos detalhados que os perfis baseados em instrumentação.

Todos os perfis devem ser gerados a partir de uma carga de trabalho representativa que exercite o comportamento típico do aplicativo. Embora o Clang ofereça suporte baseado em AST ( -fprofile-instr-generate ) e baseado em LLVM IR ( -fprofile-generate) , o Android oferece suporte apenas baseado em LLVM IR para PGO baseado em instrumentação.

Os seguintes sinalizadores são necessários para construir a coleção de perfis:

  • -fprofile-generate para instrumentação baseada em IR. Com esta opção, o back-end usa uma abordagem de árvore geradora mínima ponderada para reduzir o número de pontos de instrumentação e otimizar seu posicionamento em arestas de baixo peso (use esta opção também para a etapa de link). O driver Clang passa automaticamente o tempo de execução de criação de perfil ( libclang_rt.profile- arch -android.a ) para o vinculador. Esta biblioteca contém rotinas para gravar os perfis no disco ao sair do programa.
  • -gline-tables-only para coleta de perfis baseada em amostragem para gerar informações mínimas de depuração.

Um perfil pode ser usado para PGO usando -fprofile-use= pathname ou -fprofile-sample-use= pathname para perfis baseados em instrumentação e amostragem, respectivamente.

Nota: À medida que são feitas alterações no código, se o Clang não puder mais usar os dados do perfil, ele gerará um aviso -Wprofile-instr-out-of-date .

Usar PGO

O uso do PGO envolve as seguintes etapas:

  1. Crie a biblioteca/executável com instrumentação passando -fprofile-generate para o compilador e vinculador.
  2. Colete perfis executando uma carga de trabalho representativa no binário instrumentado.
  3. Pós-processe os perfis usando o utilitário llvm-profdata (para obter detalhes, consulte Manipulando arquivos de perfil LLVM ).
  4. Use os perfis para aplicar o PGO passando -fprofile-use=<>.profdata para o compilador e vinculador.

Para PGO no Android, os perfis devem ser coletados off-line e verificados junto com o código para garantir compilações reproduzíveis. Os perfis podem ser usados ​​conforme o código evolui, mas devem ser regenerados periodicamente (ou sempre que o Clang avisar que os perfis estão obsoletos).

Colete perfis

Clang pode usar perfis coletados executando benchmarks usando uma compilação instrumentada da biblioteca ou amostrando contadores de hardware quando o benchmark é executado. No momento, o Android não oferece suporte ao uso de coleta de perfis com base em amostragem. Portanto, você deve coletar perfis usando um build instrumentado:

  1. Identifique um benchmark e o conjunto de bibliotecas exercidas coletivamente por esse benchmark.
  2. Adicione propriedades pgo ao benchmark e às bibliotecas (detalhes abaixo).
  3. Produza uma versão Android com uma cópia instrumentada dessas bibliotecas usando:
    make ANDROID_PGO_INSTRUMENT=benchmark

benchmark é um espaço reservado que identifica a coleção de bibliotecas instrumentadas durante a construção. As entradas representativas reais (e possivelmente outro executável vinculado a uma biblioteca que está sendo avaliada) não são específicas do PGO e estão além do escopo deste documento.

  1. Atualize ou sincronize a compilação instrumentada em um dispositivo.
  2. Execute o benchmark para coletar perfis.
  3. Use a ferramenta llvm-profdata (discutida abaixo) para pós-processar os perfis e prepará-los para serem verificados na árvore de origem.

Use perfis durante a construção

Verifique os perfis em toolchain/pgo-profiles em uma árvore Android. O nome deve corresponder ao especificado na subpropriedade profile_file da propriedade pgo da biblioteca. O sistema de compilação passa automaticamente o arquivo de perfil para o Clang ao construir a biblioteca. A variável de ambiente ANDROID_PGO_DISABLE_PROFILE_USE pode ser definida como true para desabilitar temporariamente o PGO e medir seu benefício de desempenho.

Para especificar diretórios de perfil adicionais específicos do produto, anexe-os à variável make PGO_ADDITIONAL_PROFILE_DIRECTORIES em um BoardConfig.mk . Se caminhos adicionais forem especificados, os perfis nesses caminhos substituirão aqueles em toolchain/pgo-profiles .

Ao gerar uma imagem de lançamento usando o dist target para make , o sistema de compilação grava os nomes dos arquivos de perfil ausentes em $DIST_DIR/pgo_profile_file_missing.txt . Você pode verificar este arquivo para ver quais arquivos de perfil foram descartados acidentalmente (o que desativa silenciosamente o PGO).

Habilite PGO em arquivos Android.bp

Para ativar o PGO em arquivos Android.bp para módulos nativos, basta especificar a propriedade pgo . Esta propriedade possui as seguintes subpropriedades:

Propriedade Descrição
instrumentation Definido como true para PGO usando instrumentação. O padrão é false .
sampling Definido como true para PGO usando amostragem. O padrão é false .
benchmarks Lista de strings. Este módulo é construído para criação de perfil se qualquer benchmark na lista for especificado na opção de construção ANDROID_PGO_INSTRUMENT .
profile_file Arquivo de perfil (relativo a toolchain/pgo-profile ) para usar com PGO. A compilação avisa que este arquivo não existe adicionando-o a $DIST_DIR/pgo_profile_file_missing.txt a menos que a propriedade enable_profile_use esteja definida como false OU a variável de compilação ANDROID_PGO_NO_PROFILE_USE esteja definida como true .
enable_profile_use Defina como false se os perfis não devem ser usados ​​durante a construção. Pode ser usado durante a inicialização para ativar a coleta de perfis ou desativar temporariamente o PGO. O padrão é true .
cflags Lista de sinalizadores adicionais a serem usados ​​durante um build instrumentado.

Exemplo de módulo com 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",
    }
}

Se os benchmarks benchmark1 e benchmark2 exercerem um comportamento representativo para as bibliotecas libstatic1 , libstatic2 ou libshared1 , a propriedade pgo dessas bibliotecas também poderá incluir os benchmarks. O módulo defaults em Android.bp pode incluir uma especificação pgo comum para um conjunto de bibliotecas para evitar a repetição das mesmas regras de construção para vários módulos.

Para selecionar diferentes arquivos de perfil ou desabilitar seletivamente o PGO para uma arquitetura, especifique as propriedades profile_file , enable_profile_use e cflags por arquitetura. Exemplo (com arquitetura alvo em negrito ):

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 referências à biblioteca de tempo de execução de criação de perfil durante a criação de perfil baseada em instrumentação, passe o sinalizador de compilação -fprofile-generate para o vinculador. Bibliotecas estáticas instrumentadas com PGO, todas as bibliotecas compartilhadas e qualquer binário que dependa diretamente da biblioteca estática também devem ser instrumentadas para PGO. No entanto, essas bibliotecas ou executáveis ​​compartilhados não precisam usar perfis PGO e sua propriedade enable_profile_use pode ser definida como false . Fora desta restrição, você pode aplicar PGO a qualquer biblioteca estática, biblioteca compartilhada ou executável.

Lidar com arquivos de perfil LLVM

A execução de uma biblioteca instrumentada ou executável produz um arquivo de perfil denominado default_ unique_id _0.profraw em /data/local/tmp (onde unique_id é um hash numérico exclusivo desta biblioteca). Se esse arquivo já existir, o tempo de execução de criação de perfil mesclará o novo perfil com o antigo enquanto grava os perfis. Observe que /data/local/tmp não está acessível para desenvolvedores de aplicativos; eles deveriam usar algum lugar como /storage/emulated/0/Android/data/ packagename /files . Para alterar o local do arquivo de perfil, configure a variável de ambiente LLVM_PROFILE_FILE no tempo de execução.

O utilitário llvm-profdata é então usado para converter o arquivo .profraw (e possivelmente mesclar vários arquivos .profraw ) em um arquivo .profdata :

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

profile.profdata pode então ser verificado na árvore de origem para uso durante a construção.

Se vários binários/bibliotecas instrumentados forem carregados durante um benchmark, cada biblioteca gerará um arquivo .profraw separado com um ID exclusivo separado. Normalmente, todos esses arquivos podem ser mesclados em um único arquivo .profdata e usados ​​para construção do PGO. Nos casos em que uma biblioteca é exercida por outro benchmark, essa biblioteca deve ser otimizada utilizando perfis de ambos os benchmarks. Nesta situação, a opção show do llvm-profdata é útil:

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

Para mapear unique_id s para bibliotecas individuais, pesquise na saída show para cada unique_id um nome de função que seja exclusivo da biblioteca.

Estudo de caso: PGO para ART

O estudo de caso apresenta a ART como um exemplo identificável; no entanto, não é uma descrição precisa do conjunto real de bibliotecas perfiladas para ART ou de suas interdependências.

O compilador antecipado dex2oat no ART depende de libart-compiler.so , que por sua vez depende de libart.so . O tempo de execução do ART é implementado principalmente em libart.so . Os benchmarks para o compilador e o tempo de execução serão diferentes:

Referência Bibliotecas perfiladas
dex2oat dex2oat (executável), libart-compiler.so , libart.so
art_runtime libart.so
  1. Adicione a seguinte propriedade pgo a dex2oat , libart-compiler.so :
        pgo: {
            instrumentation: true,
            benchmarks: ["dex2oat",],
            profile_file: "dex2oat.profdata",
        }
  2. Adicione a seguinte propriedade pgo a libart.so :
        pgo: {
            instrumentation: true,
            benchmarks: ["art_runtime", "dex2oat",],
            profile_file: "libart.profdata",
        }
  3. Crie compilações instrumentadas para os benchmarks dex2oat e art_runtime usando:
        make ANDROID_PGO_INSTRUMENT=dex2oat
        make ANDROID_PGO_INSTRUMENT=art_runtime
  4. Como alternativa, crie um único build instrumentado com todas as bibliotecas instrumentadas usando:

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

    O segundo comando cria todos os módulos habilitados para PGO para criação de perfil.

  5. Execute os benchmarks exercitando dex2oat e art_runtime para obter:
    • Três arquivos .profraw de dex2oat ( dex2oat_exe.profdata , dex2oat_libart-compiler.profdata e dexeoat_libart.profdata ), identificados usando o método descrito em Manipulando arquivos de perfil LLVM .
    • Um único art_runtime_libart.profdata .
  6. Produza um arquivo profdata comum para o executável dex2oat e libart-compiler.so usando:
    llvm-profdata merge -output=dex2oat.profdata \
        dex2oat_exe.profdata dex2oat_libart-compiler.profdata
  7. Obtenha o perfil para libart.so mesclando os perfis dos dois benchmarks:
    llvm-profdata merge -output=libart.profdata \
        dex2oat_libart.profdata art_runtime_libart.profdata

    As contagens brutas para libart.so dos dois perfis podem ser diferentes porque os benchmarks diferem no número de casos de teste e na duração em que são executados. Nesse caso, você pode usar uma mesclagem ponderada:

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

    O comando acima atribui o dobro do peso ao perfil de dex2oat . O peso real deve ser determinado com base no conhecimento do domínio ou na experimentação.

  8. Verifique os arquivos de perfil dex2oat.profdata e libart.profdata em toolchain/pgo-profiles para uso durante a construção.