Utilizzo dell'ottimizzazione guidata dal profilo (PGO) (<=13)

Il sistema di build Android per Android 13 e versioni precedenti supporta l'utilizzo dell'ottimizzazione guidata dal profilo (PGO) di Clang su moduli Android nativi che dispongono di regole di build del progetto . Questa pagina descrive Clang PGO, come generare e aggiornare continuamente i profili utilizzati per PGO e come integrare PGO con il sistema di compilazione (con caso d'uso).

NB: Questo documento descrive l'uso di PGO nella piattaforma Android. Per informazioni sull'utilizzo di PGO da un'app Android, visitare questa pagina .

A proposito di Clang PGO

Clang può eseguire l'ottimizzazione guidata dal profilo utilizzando due tipi di profili:

  • I profili basati sulla strumentazione vengono generati da un programma target strumentato. Questi profili sono dettagliati e impongono un elevato sovraccarico di runtime.
  • I profili basati sul campionamento vengono generalmente prodotti campionando i contatori hardware. Impongono un basso sovraccarico di runtime e possono essere raccolti senza alcuna strumentazione o modifica al binario. Sono meno dettagliati dei profili basati sulla strumentazione.

Tutti i profili devono essere generati da un carico di lavoro rappresentativo che eserciti il ​​comportamento tipico dell'applicazione. Mentre Clang supporta sia basato su AST ( -fprofile-instr-generate ) che LLVM IR ( -fprofile-generate) , Android supporta solo LLVM IR per PGO basato su strumentazione.

Sono necessari i seguenti flag per creare la raccolta dei profili:

  • -fprofile-generate per strumentazione basata su IR. Con questa opzione, il backend utilizza un approccio di spanning tree minimo ponderato per ridurre il numero di punti di strumentazione e ottimizzare il loro posizionamento su bordi di peso ridotto (utilizzare questa opzione anche per la fase di collegamento). Il driver Clang passa automaticamente il runtime di profilazione ( libclang_rt.profile- arch -android.a ) al linker. Questa libreria contiene routine per scrivere i profili su disco all'uscita dal programma.
  • -gline-tables-only per la raccolta di profili basata su campionamento per generare informazioni di debug minime.

È possibile utilizzare un profilo per PGO utilizzando -fprofile-use= pathname o -fprofile-sample-use= pathname rispettivamente per i profili basati sulla strumentazione e basati sul campionamento.

Nota: quando vengono apportate modifiche al codice, se Clang non può più utilizzare i dati del profilo genera un avviso -Wprofile-instr-out-of-date .

Utilizzando PGO

L'utilizzo di PGO prevede i seguenti passaggi:

  1. Costruisci la libreria/eseguibile con la strumentazione passando -fprofile-generate al compilatore e al linker.
  2. Raccogli i profili eseguendo un carico di lavoro rappresentativo sul binario strumentato.
  3. Postelaborare i profili utilizzando l'utilità llvm-profdata (per i dettagli, vedere Gestione dei file di profilo LLVM ).
  4. Utilizzare i profili per applicare PGO passando -fprofile-use=<>.profdata al compilatore e al linker.

Per PGO in Android, i profili devono essere raccolti offline e archiviati insieme al codice per garantire build riproducibili. I profili possono essere utilizzati man mano che il codice si evolve, ma devono essere rigenerati periodicamente (o ogni volta che Clang avverte che i profili sono obsoleti).

Raccolta profili

Clang può utilizzare i profili raccolti eseguendo benchmark utilizzando una build strumentata della libreria o campionando i contatori hardware durante l'esecuzione del benchmark. Al momento, Android non supporta l'utilizzo della raccolta di profili basata su campionamento, pertanto è necessario raccogliere i profili utilizzando una build strumentata:

  1. Identificare un benchmark e l'insieme di biblioteche collettivamente esercitate da quel benchmark.
  2. Aggiungi le proprietà pgo al benchmark e alle librerie (dettagli di seguito).
  3. Produci una build Android con una copia strumentata di queste librerie utilizzando:
    make ANDROID_PGO_INSTRUMENT=benchmark

benchmark è un segnaposto che identifica la raccolta di librerie strumentate durante la compilazione. Gli input rappresentativi effettivi (e possibilmente un altro eseguibile che si collega a una libreria sottoposta a benchmark) non sono specifici di PGO e vanno oltre lo scopo di questo documento.

  1. Flash o sincronizza la build strumentata su un dispositivo.
  2. Esegui il benchmark per raccogliere i profili.
  3. Utilizzare lo strumento llvm-profdata (discusso di seguito) per post-elaborare i profili e renderli pronti per essere archiviati nell'albero di origine.

Utilizzo dei profili durante la compilazione

Controlla i profili in toolchain/pgo-profiles in un albero Android. Il nome deve corrispondere a quanto specificato nella sottoproprietà profile_file della proprietà pgo per la libreria. Il sistema di compilazione passa automaticamente il file del profilo a Clang durante la creazione della libreria. La variabile di ambiente ANDROID_PGO_DISABLE_PROFILE_USE può essere impostata su true per disabilitare temporaneamente PGO e misurarne il vantaggio in termini di prestazioni.

Per specificare ulteriori directory di profili specifici del prodotto, aggiungerle alla variabile make PGO_ADDITIONAL_PROFILE_DIRECTORIES in un BoardConfig.mk . Se vengono specificati percorsi aggiuntivi, i profili in questi percorsi sovrascrivono quelli in toolchain/pgo-profiles .

Quando si genera un'immagine di rilascio utilizzando la destinazione dist per make , il sistema di compilazione scrive i nomi dei file di profilo mancanti in $DIST_DIR/pgo_profile_file_missing.txt . Puoi controllare questo file per vedere quali file di profilo sono stati eliminati accidentalmente (il che disabilita silenziosamente PGO).

Abilitazione di PGO nei file Android.bp

Per abilitare PGO nei file Android.bp per i moduli nativi, è sufficiente specificare la proprietà pgo . Questa proprietà ha le seguenti sottoproprietà:

Proprietà Descrizione
instrumentation Impostato su true per PGO che utilizza la strumentazione. L'impostazione predefinita è false .
sampling Impostato su true per PGO che utilizza il campionamento. L'impostazione predefinita è false .
benchmarks Elenco delle stringhe. Questo modulo è creato per la profilazione se qualsiasi benchmark nell'elenco è specificato nell'opzione di compilazione ANDROID_PGO_INSTRUMENT .
profile_file File di profilo (relativo a toolchain/pgo-profile ) da utilizzare con PGO. La build avvisa che questo file non esiste aggiungendo questo file a $DIST_DIR/pgo_profile_file_missing.txt a meno che la proprietà enable_profile_use non sia impostata su false OPPURE la variabile di build ANDROID_PGO_NO_PROFILE_USE sia impostata su true .
enable_profile_use Impostato su false se i profili non devono essere utilizzati durante la compilazione. Può essere utilizzato durante il bootstrap per abilitare la raccolta dei profili o per disabilitare temporaneamente PGO. L'impostazione predefinita è true .
cflags Elenco di flag aggiuntivi da utilizzare durante una compilazione strumentata.

Esempio di modulo 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",
    }
}

Se i benchmark benchmark1 e benchmark2 esercitano un comportamento rappresentativo per le librerie libstatic1 , libstatic2 o libshared1 , la proprietà pgo di queste librerie può includere anche i benchmark. Il modulo defaults in Android.bp può includere una specifica pgo comune per un set di librerie per evitare di ripetere le stesse regole di compilazione per diversi moduli.

Per selezionare file di profilo diversi o disabilitare selettivamente PGO per un'architettura, specificare le proprietà profile_file , enable_profile_use e cflags per architettura. Esempio (con l'obiettivo dell'architettura in grassetto ):

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",
              }
         }
    }
}

Per risolvere i riferimenti alla libreria runtime di profilazione durante la profilatura basata sulla strumentazione, passare il flag di build -fprofile-generate al linker. Anche le librerie statiche dotate di PGO, tutte le librerie condivise e qualsiasi file binario che dipende direttamente dalla libreria statica devono essere dotate di PGO. Tuttavia, tali librerie condivise o eseguibili non necessitano di utilizzare profili PGO e la loro proprietà enable_profile_use può essere impostata su false . Al di fuori di questa restrizione, è possibile applicare PGO a qualsiasi libreria statica, libreria condivisa o eseguibile.

Gestione dei file di profilo LLVM

L'esecuzione di una libreria o di un eseguibile dotato di strumenti produce un file di profilo denominato default_ unique_id _0.profraw in /data/local/tmp (dove unique_id è un hash numerico univoco per questa libreria). Se questo file esiste già, il runtime di profilazione unisce il nuovo profilo con quello vecchio durante la scrittura dei profili. Tieni presente che /data/local/tmp non è accessibile agli sviluppatori di app; dovrebbero invece usare da qualche parte come /storage/emulated/0/Android/data/ packagename /files . Per modificare la posizione del file di profilo, impostare la variabile di ambiente LLVM_PROFILE_FILE in fase di runtime.

L'utilità llvm-profdata viene quindi utilizzata per convertire il file .profraw (ed eventualmente unire più file .profraw ) in un file .profdata :

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

profile.profdata può quindi essere archiviato nell'albero dei sorgenti per essere utilizzato durante la compilazione.

Se durante un benchmark vengono caricati più file binari/librerie strumentate, ciascuna libreria genera un file .profraw separato con un ID univoco separato. In genere, tutti questi file possono essere uniti in un singolo file .profdata e utilizzati per la compilazione PGO. Nei casi in cui una libreria viene utilizzata da un altro benchmark, tale libreria deve essere ottimizzata utilizzando i profili di entrambi i benchmark. In questa situazione, l'opzione show di llvm-profdata è utile:

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

Per mappare unique_id a singole librerie, cercare nell'output show per ogni unique_id un nome di funzione univoco per la libreria.

Caso di studio: PGO per l'ARTE

Il caso di studio presenta l'ART come un esempio riconoscibile; tuttavia, non è una descrizione accurata dell'effettivo insieme di librerie profilate per ART o delle loro interdipendenze.

Il compilatore anticipato dex2oat in ART dipende da libart-compiler.so , che a sua volta dipende da libart.so . Il runtime ART è implementato principalmente in libart.so . I benchmark per il compilatore e il runtime saranno diversi:

Segno di riferimento Biblioteche profilate
dex2oat dex2oat (eseguibile), libart-compiler.so , libart.so
art_runtime libart.so
  1. Aggiungi la seguente proprietà pgo a dex2oat , libart-compiler.so :
        pgo: {
            instrumentation: true,
            benchmarks: ["dex2oat",],
            profile_file: "dex2oat.profdata",
        }
  2. Aggiungi la seguente proprietà pgo a libart.so :
        pgo: {
            instrumentation: true,
            benchmarks: ["art_runtime", "dex2oat",],
            profile_file: "libart.profdata",
        }
  3. Crea build strumentate per i benchmark dex2oat e art_runtime utilizzando:
        make ANDROID_PGO_INSTRUMENT=dex2oat
        make ANDROID_PGO_INSTRUMENT=art_runtime
  4. In alternativa, crea una singola build strumentata con tutte le librerie strumentate utilizzando:

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

    Il secondo comando crea tutti i moduli abilitati per PGO per la profilazione.

  5. Eseguire i benchmark esercitando dex2oat e art_runtime per ottenere:
    • Tre file .profraw da dex2oat ( dex2oat_exe.profdata , dex2oat_libart-compiler.profdata e dexeoat_libart.profdata ), identificati utilizzando il metodo descritto in Gestione dei file di profilo LLVM .
    • Un singolo art_runtime_libart.profdata .
  6. Produci un file profdata comune per l'eseguibile dex2oat e libart-compiler.so utilizzando:
    llvm-profdata merge -output=dex2oat.profdata \
        dex2oat_exe.profdata dex2oat_libart-compiler.profdata
  7. Ottieni il profilo per libart.so unendo i profili dai due benchmark:
    llvm-profdata merge -output=libart.profdata \
        dex2oat_libart.profdata art_runtime_libart.profdata

    I conteggi grezzi per libart.so dai due profili potrebbero essere disparati perché i benchmark differiscono nel numero di casi di test e nella durata per cui vengono eseguiti. In questo caso, puoi utilizzare una fusione ponderata:

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

    Il comando precedente assegna il doppio del peso al profilo da dex2oat . Il peso effettivo dovrebbe essere determinato in base alla conoscenza del settore o alla sperimentazione.

  8. Controlla i file del profilo dex2oat.profdata e libart.profdata in toolchain/pgo-profiles da utilizzare durante la compilazione.