Usar a otimização guiada por perfil

O sistema de build do Android 13 e versões anteriores oferece suporte ao uso da otimização guiada por perfil (PGO, na sigla em inglês) do Clang em módulos Android nativos que têm regras de build de diagrama. Esta página descreve a PGO do Clang, como gerar e atualizar continuamente os perfis usados para a PGO e como integrar a PGO ao sistema de build (com caso de uso).

Observação: este documento descreve o uso da PGO na plataforma Android. Para saber como usar a PGO em um app Android, acesse esta página.

Sobre a PGO do Clang

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

  • 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 sobrecarga de execução alta.
  • Os perfis baseados em amostragem geralmente são produzidos por amostragem de contadores de hardware. Eles impõem uma sobrecarga de execução baixa e podem ser coletados sem nenhuma instrumentação ou modificação no binário. Eles são menos detalhados do que os perfis baseados em instrumentação.

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

As flags a seguir são necessárias para criar a coleta de perfis:

  • -fprofile-generate para instrumentação baseada em IR. Com essa opção, o back-end usa uma abordagem de árvore mínima ponderada para reduzir o número de pontos de instrumentação e otimizar a posição deles para bordas de baixo peso. Use essa opção também para a etapa de vinculação. O driver Clang transmite automaticamente o ambiente de execução de perfil (libclang_rt.profile-arch-android.a) ao vinculador. Essa biblioteca contém rotinas para gravar os perfis no disco ao sair do programa.
  • -gline-tables-only para a coleta de perfis baseada em amostragem para gerar informações mínimas de depuração.

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

Observação:à medida que as mudanças são feitas 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 o PGO

O uso da PGO envolve as seguintes etapas:

  1. Crie a biblioteca/executável com instrumentação transmitindo -fprofile-generate ao compilador e ao vinculador.
  2. Colete perfis executando uma carga de trabalho representativa no binário instrumentado.
  3. Faça o pós-processamento dos perfis usando o utilitário llvm-profdata. Para mais detalhes, consulte Como lidar com arquivos de perfil LLVM.
  4. Use os perfis para aplicar a PGO transmitindo -fprofile-use=<>.profdata para o compilador e o vinculador.

Para o PGO no Android, os perfis precisam ser coletados off-line e verificados junto com o código para garantir builds reproduzíveis. Os perfis podem ser usados à medida que o código evolui, mas precisam ser regenerados periodicamente (ou sempre que o Clang avisar que os perfis estão desatualizados).

Coletar perfis

O Clang pode usar perfis coletados executando comparativos de mercado usando um build instrumentado da biblioteca ou amostrando contadores de hardware quando o comparativo de mercado é executado. No momento, o Android não oferece suporte à coleta de perfis baseada em amostragem. Portanto, é necessário coletar perfis usando um build instrumentado:

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

benchmark é um marcador de posição que identifica a coleção de bibliotecas instrumentadas durante o build. As entradas de exemplo reais (e possivelmente outro executável vinculado a uma biblioteca sendo comparada) não são específicas para a PGO e estão fora do escopo deste documento.

  1. Faça a atualização ou a sincronização do build instrumentado em um dispositivo.
  2. Execute o comparativo para coletar perfis.
  3. Use a ferramenta llvm-profdata (discutida abaixo) para processar os perfis e deixá-los prontos para serem verificados na árvore de origem.

Usar perfis durante o build

Verifique os perfis em toolchain/pgo-profiles em uma árvore do Android. O nome precisa corresponder ao especificado na subpropriedade profile_file da propriedade pgo da biblioteca. O sistema de build transmite automaticamente o arquivo de perfil para o Clang ao criar a biblioteca. A variável de ambiente ANDROID_PGO_DISABLE_PROFILE_USE pode ser definida como true para desativar temporariamente o PGO e medir o benefício de desempenho.

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

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

Ativar o PGO em arquivos Android.bp

Para ativar a PGO em arquivos Android.bp para módulos nativos, basta especificar a propriedade pgo. Essa propriedade tem as seguintes subatributos:

Propriedade Descrição
instrumentation Definido 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 strings. Esse módulo é criado para criar perfis se qualquer comparativo na lista for especificado na opção de build ANDROID_PGO_INSTRUMENT.
profile_file Arquivo de perfil (relativo a toolchain/pgo-profile) para usar com a PGO. O build avisa que esse arquivo não existe adicionando-o a $DIST_DIR/pgo_profile_file_missing.txt a menos que a propriedade enable_profile_use seja definida como false OU a variável de build ANDROID_PGO_NO_PROFILE_USE seja definida como true.
enable_profile_use Defina como false se os perfis não forem usados durante o build. Pode ser usado durante o bootstrap para ativar a coleta de perfil ou desativar temporariamente o PGO. O padrão é true.
cflags Lista de flags adicionais a serem usadas durante um build instrumentado.

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 comparativos de mercado benchmark1 e benchmark2 exercerem um comportamento representativo para as bibliotecas libstatic1, libstatic2 ou libshared1, a propriedade pgo dessas bibliotecas também poderá incluir os comparativos de mercado. 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 build para vários módulos.

Para selecionar diferentes arquivos de perfil ou desativar seletivamente a PGO para uma arquitetura, especifique as propriedades profile_file, enable_profile_use e cflags por arquitetura. Exemplo (com o destino da arquitetura 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 execução de criação de perfil durante a criação de perfil baseada em instrumentação, transmita a flag de build -fprofile-generate ao vinculador. As bibliotecas estáticas instrumentadas com PGO, todas as bibliotecas compartilhadas e qualquer binário que dependa diretamente da biblioteca estática também precisam ser instrumentadas para PGO. No entanto, essas bibliotecas compartilhadas ou executáveis não precisam usar perfis de PGO, e a propriedade enable_profile_use delas pode ser definida como false. Fora dessa restrição, é possível aplicar a PGO a qualquer biblioteca estática, compartilhada ou executável.

Processar arquivos de perfil LLVM

A execução de uma biblioteca ou um executável instrumentado produz um arquivo de perfil chamado default_unique_id_0.profraw em /data/local/tmp, em que unique_id é um hash numérico exclusivo para essa biblioteca. Se esse arquivo já existir, o ambiente de execução de criação de perfil vai mesclar o novo perfil com o antigo ao gravar os perfis. /data/local/tmp não é acessível para desenvolvedores de apps. Eles precisam usar algo como /storage/emulated/0/Android/data/packagename/files. Para mudar o local do arquivo de perfil, defina a variável de ambiente LLVM_PROFILE_FILE no momento da execução.

O utilitário llvm-profdata é 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>

O profile.profdata pode ser verificado na árvore de origem para uso durante o build.

Se vários binários/bibliotecas instrumentados forem carregados durante um comparativo de mercado, cada biblioteca vai gerar um arquivo .profraw separado com um ID exclusivo. Normalmente, todos esses arquivos podem ser mesclados em um único arquivo .profdata e usados para o build de PGO. Nos casos em que uma biblioteca é usada por outro comparativo de mercado, ela precisa ser otimizada usando perfis de ambos os comparativos. Nessa 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_ids para bibliotecas individuais, pesquise a saída show para cada unique_id para um nome de função que seja exclusivo da biblioteca.

Estudo de caso: PGO para ART

O estudo de caso apresenta o ART como um exemplo relacionado, mas não é uma descrição precisa do conjunto real de bibliotecas com perfil para o ART ou suas interdependências.

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

Benchmark 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 a libart.so:
        pgo: {
            instrumentation: true,
            benchmarks: ["art_runtime", "dex2oat",],
            profile_file: "libart.profdata",
        }
  3. Crie builds instrumentados para os comparativos de mercado 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 ativados para PGO para criação de perfil.

  5. Execute as comparações usando dex2oat e art_runtime para receber:
    • Três arquivos .profraw de dex2oat (dex2oat_exe.profdata, dex2oat_libart-compiler.profdata e dexeoat_libart.profdata), identificados usando o método descrito em Como gerenciar 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. Para extrair o perfil de libart.so, mescle os perfis das duas comparações:
    llvm-profdata merge -output=libart.profdata \
        dex2oat_libart.profdata art_runtime_libart.profdata

    As contagens brutas de libart.so dos dois perfis podem ser diferentes porque os comparativos variam no número de casos de teste e na duração de execução. Nesse caso, é possível 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 precisa ser determinado com base no conhecimento de domínio ou na experimentação.

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