Como usar a otimização guiada por perfil (PGO)

O sistema de compilação do Android é compatível com o uso da otimização guiada por perfil (PGO) do Clang em módulos nativos do Android que possuem regras de compilação de blueprint . Esta página descreve o Clang PGO, como gerar e atualizar continuamente os perfis usados ​​para o PGO e como integrar o PGO com o sistema de compilação (com caso de uso).

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

Sobre Clang PGO

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

  • Os 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.
  • Os perfis baseados em amostragem geralmente são 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 do que os perfis baseados em instrumentação.

Todos os perfis devem ser gerados a partir de uma carga de trabalho representativa que exerça o comportamento típico da aplicação. Embora o Clang seja compatível com AST ( -fprofile-instr-generate ) e LLVM IR ( -fprofile-generate) , o Android é compatível apenas com 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 essa opção, o back-end usa uma abordagem de árvore de abrangência mínima ponderada para reduzir o número de pontos de instrumentação e otimizar seu posicionamento em arestas de baixo peso (use essa 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 na saída 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-instr-use= pathname ou -fprofile-sample-use= pathname para perfis baseados em instrumentação e baseados em amostragem, respectivamente.

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

Usando PGO

O uso do PGO envolve as seguintes etapas:

  1. Compile a biblioteca/executável com instrumentação passando -fprofile-generate para o compilador e o 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 offline e verificados juntamente com o código para garantir compilações reproduzíveis. Os perfis podem ser usados ​​à medida que o código evolui, mas devem ser regenerados periodicamente (ou sempre que o Clang avisar que os perfis estão obsoletos).

Coletando perfis

O Clang pode usar perfis coletados pela execução de benchmarks usando uma compilação instrumentada da biblioteca ou pela amostragem de contadores de hardware quando o benchmark é executado. No momento, o Android não oferece suporte ao uso de coleta de perfis baseada em amostragem, portanto, você deve coletar perfis usando uma compilação instrumentada:

  1. Identifique um benchmark e o conjunto de bibliotecas exercido coletivamente por esse benchmark.
  2. Adicione as propriedades pgo ao benchmark e às bibliotecas (detalhes abaixo).
  3. Produza uma versão do 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 compilaçã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 deixá-los prontos para serem verificados na árvore de origem.

Usando perfis durante a compilaçã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 para a biblioteca. O sistema de compilação passa automaticamente o arquivo de perfil para o Clang ao compilar 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 perfis 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 destino dist 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 acidentalmente descartados (o que desativa silenciosamente o PGO).

Habilitando PGO em arquivos Android.bp

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

Propriedade Descrição
instrumentation Defina como true para PGO usando instrumentação. O padrão é false .
sampling Defina como true para PGO usando amostragem. O padrão é false .
benchmarks Lista de cordas. Este módulo é construído para criação de perfil se qualquer referência na lista for especificada 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 este arquivo 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 construçã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 compilação. Pode ser usado durante o bootstrap para habilitar a coleta de perfis ou para desabilitar temporariamente o PGO. O padrão é true .
cflags Lista de sinalizadores adicionais a serem usados ​​durante uma compilação instrumentada.

Exemplo de um 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 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 compilação para vários módulos.

Para selecionar diferentes arquivos de perfil ou desativar 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 dessa restrição, você pode aplicar o PGO a qualquer biblioteca estática, biblioteca compartilhada ou executável.

Manipulando 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 (em que unique_id é um hash numérico exclusivo para esta biblioteca). Se esse arquivo já existir, o runtime de criação de perfil mescla o novo perfil com o antigo enquanto grava os perfis. Observe que /data/local/tmp não é acessível a desenvolvedores de aplicativos; eles devem 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 compilaçã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 a construção do PGO. Nos casos em que uma biblioteca é exercida por outro benchmark, essa biblioteca deve ser otimizada usando perfis de ambos os benchmarks. Nesta situação, a opção show de 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, procure 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 relacionável; no entanto, não é uma descrição precisa do conjunto real de bibliotecas perfiladas para ART ou 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 ART é implementado principalmente em libart.so . Os benchmarks para o compilador e o tempo de execução serão diferentes:

Referência Bibliotecas com perfil
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 ao 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 uma única compilação instrumentada 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 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 díspares porque os benchmarks diferem no número de casos de teste e na duração pela qual eles 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 compilação.