Utilizzare l'ottimizzazione basata su profilo

Il sistema di compilazione di Android per Android 13 e versioni precedenti supporta l'utilizzo dell'ottimizzazione basata su profilo (PGO) di Clang sui moduli Android nativi che hanno regole di compilazione blueprint. Questa pagina descrive la PGO di Clang, come generare e aggiornare continuamente i profili utilizzati per la PGO e come integrare la PGO con il sistema di compilazione (con use case).

Nota: questo documento descrive l'utilizzo di PGO nella piattaforma Android. Per scoprire di più sull'utilizzo di giochi di gru a pesca verticale online da un'app per Android, visita questa pagina.

Informazioni su Clang PGO

Clang può eseguire l'ottimizzazione basata su profilo utilizzando due tipi di profili:

  • I profili basati sull'instrumentazione vengono generati da un programma di destinazione sottoposto a instrumentazione. Questi profili sono dettagliati e comportano un elevato costo aggiuntivo in fase di esecuzione.
  • I profili basati sul campionamento vengono in genere generati da contatori hardware di campionamento. Impongono un basso sovraccarico di runtime e possono essere raccolti senza alcuna strumentazione o modifica del file binario. Sono meno dettagliati dei profili basati sulla misurazione.

Tutti i profili devono essere generati da un carico di lavoro rappresentativo che eserciti il comportamento tipico dell'app. Sebbene Clang supporti sia il profilo basato su AST (-fprofile-instr-generate) sia quello basato su LLVM IR (-fprofile-generate)), Android supporta solo il profilo basato su LLVM IR per il PGO basato sull'instrumentazione.

Per la compilazione per la raccolta dei profili sono necessari i seguenti flag:

  • -fprofile-generate per la strumentazione basata su IR. Con questa opzione, il backend utilizza un approccio di albero ricoprente minimo ponderato per ridurre il numero di punti di misurazione e ottimizzare il loro posizionamento in base ai bordi a basso peso (utilizza questa opzione anche per il passaggio 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 sul disco al termine del programma.
  • -gline-tables-only per la raccolta dei profili basata su campionamento per generare informazioni di debug minime.

Un profilo può essere utilizzato per la PGO utilizzando -fprofile-use=pathname o -fprofile-sample-use=pathname rispettivamente per i profili basati su strumentazione e su campionamento.

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

Utilizzare PGO

L'utilizzo di PGO prevede i seguenti passaggi:

  1. Compila la libreria/l'eseguibile con la misurazione passando -fprofile-generate al compilatore e al linker.
  2. Raccogli i profili eseguendo un carico di lavoro rappresentativo sul codice binario instrumentato.
  3. Esegui il post-trattamento dei profili utilizzando l'utilità llvm-profdata (per maggiori dettagli, vedi Gestire i file di profilo LLVM).
  4. Utilizza i profili per applicare la PGO passando -fprofile-use=<>.profdata al compilatore e al linker.

Per la PGO in Android, i profili devono essere raccolti offline e controllati 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 avvisa che i profili non sono aggiornati).

Raccogliere i profili

Clang può utilizzare i profili raccolti eseguendo benchmark con una compilazione instrumentata della libreria o campionando i contatori hardware durante l'esecuzione del benchmark. Al momento, Android non supporta l'utilizzo della raccolta dei profili basata sul campionamento, pertanto devi raccogliere i profili utilizzando una build instrumentata:

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

benchmark è un segnaposto che identifica la raccolta di librerie sottoposte a ispezione durante la compilazione. Gli input rappresentativi effettivi (e possibilmente un altro file eseguibile che esegue il collegamento a una libreria di cui viene eseguito il benchmark) non sono specifici per la PGO e non rientrano nell'ambito di questo documento.

  1. Esegui il flashing o la sincronizzazione della build con strumenti su un dispositivo.
  2. Esegui il benchmark per raccogliere i profili.
  3. Utilizza lo strumento llvm-profdata (descritto di seguito) per eseguire il post-trattamento dei profili e prepararli per il check-in nell'albero di origine.

Utilizzare i profili durante la compilazione

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

Per specificare altre directory di profili specifici per prodotto, aggiungile alla variabile PGO_ADDITIONAL_PROFILE_DIRECTORIES in un BoardConfig.mk. Se vengono specificati percorsi aggiuntivi, i profili in questi percorsi sostituiscono quelli in toolchain/pgo-profiles.

Quando generi un'immagine di release utilizzando il target 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 del profilo sono stati eliminati accidentalmente (il che disattiva silenziosamente PGO).

Attivare PGO nei file Android.bp

Per attivare la PGO nei file Android.bp per i moduli nativi, basta specificare la proprietà pgo. Questa proprietà ha le seguenti proprietà secondarie:

Proprietà Descrizione
instrumentation Imposta su true per la PGO utilizzando la misurazione. Il valore predefinito è false.
sampling Impostato su true per la PGO con il campionamento. Il valore predefinito è false.
benchmarks Elenco di stringhe. Questo modulo è progettato per il profiling se un benchmark nell'elenco è specificato nell'opzione ANDROID_PGO_INSTRUMENT build.
profile_file File del profilo (relativo a toolchain/pgo-profile) da utilizzare con PGO. La compilazione avvisa che questo file non esiste aggiungendolo 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 compilazione ANDROID_PGO_NO_PROFILE_USE non sia impostata su true.
enable_profile_use Imposta su false se i profili non devono essere utilizzati durante la compilazione. Può essere utilizzato durante il bootstrap per attivare la raccolta dei profili o per disattivare temporaneamente la PGO. Il valore predefinito è true.
cflags Elenco di flag aggiuntivi da utilizzare durante una compilazione con strumenti.

Esempio di un 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 adottano 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 insieme di librerie per evitare di ripetere le stesse regole di compilazione per più moduli.

Per selezionare file di profilo diversi o disattivare selettivamente la PGO per un'architettura, specifica le proprietà profile_file, enable_profile_use e cflags per l'architettura. Esempio (con il target 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 di runtime di profilazione durante la profilazione basata su strumenti, passa il flag di compilazione -fprofile-generate al linker. Anche le librerie statiche instrumentate con PGO, tutte le librerie condivise e qualsiasi programma binario che dipende direttamente dalla libreria statica devono essere instrumentate per PGO. Tuttavia, queste librerie o eseguibili condivisi non devono utilizzare profili PGO e la loro proprietà enable_profile_use può essere impostata su false. Al di fuori di questa limitazione, puoi applicare la PGO a qualsiasi libreria statica, libreria condivisa o eseguibile.

Gestire i file di profilo LLVM

L'esecuzione di una libreria o di un file eseguibile sottoposto a ispezione 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 precedente durante la scrittura dei profili. Tieni presente che /data/local/tmp non è accessibile agli sviluppatori di app, che devono utilizzare un sito come /storage/emulated/0/Android/data/packagename/files. Per modificare la posizione del file del profilo, imposta la variabile di ambiente LLVM_PROFILE_FILE in fase di runtime.

L'utilità llvm-profdata viene poi utilizzata per convertire il file .profraw (e possibilmente unire più file .profraw) in un file .profdata:

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

profile.profdata può quindi essere sottoposto a check-in nell'albero di origine per essere utilizzato durante la compilazione.

Se durante un benchmark vengono caricati più binari/librerie con strumenti di misurazione, ogni libreria genera un file .profraw separato con un ID univoco distinto. In genere, tutti questi file possono essere uniti in un unico .profdata file e utilizzati per la compilazione PGO. Se una libreria viene utilizzata da un altro benchmark, 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 gli unique_id alle singole librerie, cerca nell'output di show per ogni unique_id un nome funzione univoco per la libreria.

Case study: giochi di realtà aumentata per ART

Il caso studio presenta ART come un esempio pertinente, ma non è una descrizione accurata dell'insieme effettivo di librerie profilate per ART o delle relative interdipendenze.

Il compilatore dex2oat ahead-of-time 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:

Benchmark Librerie 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 con strumenti 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 con tutte le librerie instrumentate utilizzando:

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

    Il secondo comando compila tutti i moduli PGO abilitati per il profiling.

  5. Esegui i benchmark con dex2oat e art_runtime per ottenere:
    • Tre file .profraw di 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. Genera 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 di libart.so unendo i profili dei due benchmark:
    llvm-profdata merge -output=libart.profdata \
        dex2oat_libart.profdata art_runtime_libart.profdata

    I conteggi non elaborati per libart.so dei due profili potrebbero essere diversi perché i benchmark differiscono per il numero di casi di test e per la durata per cui vengono eseguiti. In questo caso, puoi utilizzare un'unione ponderata:

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

    Il comando riportato sopra assegna il doppio del peso al profilo dadex2oat. Il peso effettivo deve essere determinato in base alle conoscenze o alla sperimentazione nel dominio.

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