Utiliser l'optimisation guidée par le profil

Le système de compilation Android pour Android 13 et versions antérieures prend en charge l'optimisation guidée par le profil (PGO) de Clang sur les modules Android natifs qui disposent de règles de compilation blueprint. Cette page décrit le PGO Clang, comment générer et mettre à jour en continu les profils utilisés pour le PGO, et comment intégrer le PGO au système de compilation (avec un cas d'utilisation).

Remarque: Ce document décrit l'utilisation de PGO sur la plate-forme Android. Pour en savoir plus sur l'utilisation de PGO à partir d'une application Android, consultez cette page.

À propos de PGO Clang

Clang peut effectuer une optimisation guidée par le profil à l'aide de deux types de profils:

  • Les profils basés sur l'instrumentation sont générés à partir d'un programme cible instrumenté. Ces profils sont détaillés et imposent un coût d'exécution élevé.
  • Les profils basés sur l'échantillonnage sont généralement produits en échantillonnant les compteurs matériels. Ils imposent un faible coût d'exécution et peuvent être collectés sans instrumentation ni modification du binaire. Ils sont moins détaillés que les profils basés sur l'instrumentation.

Tous les profils doivent être générés à partir d'une charge de travail représentative qui exerce le comportement typique de l'application. Bien que Clang prenne en charge à la fois les profils basés sur l'AST (-fprofile-instr-generate) et ceux basés sur l'IR LLVM (-fprofile-generate)), Android n'accepte que les profils basés sur l'IR LLVM pour le PGO basé sur l'instrumentation.

Les indicateurs suivants sont nécessaires pour créer la collecte de profils:

  • -fprofile-generate pour l'instrumentation basée sur l'IR Avec cette option, le backend utilise une approche d'arborescence minimale pondérée pour réduire le nombre de points d'instrumentation et optimiser leur emplacement sur les arêtes à faible poids (utilisez également cette option pour l'étape d'association). Le pilote Clang transmet automatiquement l'environnement d'exécution de profilage (libclang_rt.profile-arch-android.a) au linker. Cette bibliothèque contient des routines permettant d'écrire les profils sur disque à la sortie du programme.
  • -gline-tables-only pour la collecte de profils basée sur l'échantillonnage afin de générer des informations de débogage minimales.

Un profil peut être utilisé pour PGO à l'aide de -fprofile-use=pathname ou de -fprofile-sample-use=pathname pour les profils basés sur l'instrumentation et l'échantillonnage, respectivement.

Remarque:Lorsque des modifications sont apportées au code, si Clang ne peut plus utiliser les données de profil, il génère un avertissement -Wprofile-instr-out-of-date.

Utiliser PGO

Pour utiliser PGO, procédez comme suit:

  1. Créez la bibliothèque/l'exécutable avec instrumentation en transmettant -fprofile-generate au compilateur et à l'éditeur de liens.
  2. Collectez des profils en exécutant une charge de travail représentative sur le binaire instrumenté.
  3. Post-traitez les profils à l'aide de l'utilitaire llvm-profdata (pour en savoir plus, consultez la section Gérer les fichiers de profil LLVM).
  4. Utilisez les profils pour appliquer PGO en transmettant -fprofile-use=<>.profdata au compilateur et à l'outil d'association.

Pour PGO sur Android, les profils doivent être collectés hors connexion et enregistrés avec le code pour garantir la reproductibilité des builds. Les profils peuvent être utilisés à mesure que le code évolue, mais doivent être régénérés régulièrement (ou chaque fois que Clang avertit que les profils sont obsolètes).

Collecter des profils

Clang peut utiliser les profils collectés en exécutant des benchmarks à l'aide d'une version instrumentée de la bibliothèque ou en échantillonnant des compteurs matériels lors de l'exécution du benchmark. Pour le moment, Android n'est pas compatible avec la collecte de profils basée sur l'échantillonnage. Vous devez donc collecter des profils à l'aide d'un build instrumenté:

  1. Identifiez un benchmark et l'ensemble des bibliothèques exercées collectivement par ce benchmark.
  2. Ajoutez des propriétés pgo au benchmark et aux bibliothèques (détails ci-dessous).
  3. Créez un build Android avec une copie instrumentée de ces bibliothèques à l'aide de:
    make ANDROID_PGO_INSTRUMENT=benchmark

benchmark est un espace réservé qui identifie la collection de bibliothèques instrumentées lors de la compilation. Les entrées représentatives réelles (et éventuellement un autre exécutable qui établit un lien avec une bibliothèque en cours de benchmarking) ne sont pas spécifiques à PGO et sortent du cadre de ce document.

  1. Flashez ou synchronisez le build instrumenté sur un appareil.
  2. Exécutez le benchmark pour collecter les profils.
  3. Utilisez l'outil llvm-profdata (décrit ci-dessous) pour post-traiter les profils et les préparer à être intégrés à l'arborescence source.

Utiliser des profils lors de la compilation

Vérifiez les profils dans toolchain/pgo-profiles dans un arbre Android. Le nom doit correspondre à celui spécifié dans la sous-propriété profile_file de la propriété pgo pour la bibliothèque. Le système de compilation transmet automatiquement le fichier de profil à Clang lors de la compilation de la bibliothèque. La variable d'environnement ANDROID_PGO_DISABLE_PROFILE_USE peut être définie sur true pour désactiver temporairement PGO et mesurer ses avantages en termes de performances.

Pour spécifier des répertoires de profils spécifiques au produit supplémentaires, ajoutez-les à la variable de création PGO_ADDITIONAL_PROFILE_DIRECTORIES dans un fichier BoardConfig.mk. Si des chemins d'accès supplémentaires sont spécifiés, les profils de ces chemins remplacent ceux de toolchain/pgo-profiles.

Lors de la génération d'une image de version à l'aide de la cible dist pour make, le système de compilation écrit les noms des fichiers de profil manquants dans $DIST_DIR/pgo_profile_file_missing.txt. Vous pouvez consulter ce fichier pour voir quels fichiers de profil ont été supprimés par erreur (ce qui désactive PGO de manière silencieuse).

Activer PGO dans les fichiers Android.bp

Pour activer PGO dans les fichiers Android.bp pour les modules natifs, spécifiez simplement la propriété pgo. Cette propriété comporte les sous-propriétés suivantes:

Propriété Description
instrumentation Défini sur true pour PGO à l'aide de l'instrumentation. La valeur par défaut est false.
sampling Défini sur true pour l'optimisation PGO à l'aide de l'échantillonnage. La valeur par défaut est false.
benchmarks Liste de chaînes. Ce module est conçu pour le profilage si un benchmark de la liste est spécifié dans l'option de compilation ANDROID_PGO_INSTRUMENT.
profile_file Fichier de profil (par rapport à toolchain/pgo-profile) à utiliser avec PGO. La compilation avertit que ce fichier n'existe pas en l'ajoutant à $DIST_DIR/pgo_profile_file_missing.txt sauf si la propriété enable_profile_use est définie sur false OU la variable de compilation ANDROID_PGO_NO_PROFILE_USE est définie sur true.
enable_profile_use Définissez la valeur sur false si les profils ne doivent pas être utilisés lors de la compilation. Peut être utilisé lors du démarrage pour activer la collecte de profils ou pour désactiver temporairement PGO. La valeur par défaut est true.
cflags Liste d'options supplémentaires à utiliser lors d'une compilation instrumentée.

Exemple de module avec 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",
    }
}

Si les benchmarks benchmark1 et benchmark2 exercent un comportement représentatif pour les bibliothèques libstatic1, libstatic2 ou libshared1, la propriété pgo de ces bibliothèques peut également inclure les benchmarks. Le module defaults dans Android.bp peut inclure une spécification pgo commune pour un ensemble de bibliothèques afin d'éviter de répéter les mêmes règles de compilation pour plusieurs modules.

Pour sélectionner différents fichiers de profil ou désactiver sélectivement PGO pour une architecture, spécifiez les propriétés profile_file, enable_profile_use et cflags par architecture. Exemple (avec la cible d'architecture en gras):

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

Pour résoudre les références à la bibliothèque d'exécution de profilage lors du profilage basé sur l'instrumentation, transmettez l'indicateur de compilation -fprofile-generate au linker. Les bibliothèques statiques instrumentées avec PGO, toutes les bibliothèques partagées et tout binaire qui dépend directement de la bibliothèque statique doivent également être instrumentées pour PGO. Toutefois, ces bibliothèques partagées ou exécutables n'ont pas besoin d'utiliser de profils PGO, et leur propriété enable_profile_use peut être définie sur false. En dehors de cette restriction, vous pouvez appliquer PGO à n'importe quelle bibliothèque statique, bibliothèque partagée ou exécutable.

Gérer les fichiers de profil LLVM

L'exécution d'une bibliothèque ou d'un exécutable instrumentés génère un fichier de profil nommé default_unique_id_0.profraw dans /data/local/tmp (où unique_id est un hachage numérique propre à cette bibliothèque). Si ce fichier existe déjà, le runtime de profilage fusionne le nouveau profil avec l'ancien lors de l'écriture des profils. Notez que /data/local/tmp n'est pas accessible aux développeurs d'applications. Ils doivent utiliser /storage/emulated/0/Android/data/packagename/files à la place. Pour modifier l'emplacement du fichier de profil, définissez la variable d'environnement LLVM_PROFILE_FILE au moment de l'exécution.

L'utilitaire llvm-profdata permet ensuite de convertir le fichier .profraw (et éventuellement de fusionner plusieurs fichiers .profraw) en fichier .profdata:

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

profile.profdata peut ensuite être enregistré dans l'arborescence source pour être utilisé lors de la compilation.

Si plusieurs binaires/bibliothèques instrumentés sont chargés lors d'un benchmark, chaque bibliothèque génère un fichier .profraw distinct avec un identifiant unique distinct. En règle générale, tous ces fichiers peuvent être fusionnés dans un seul fichier .profdata et utilisés pour la compilation PGO. Lorsqu'une bibliothèque est utilisée par un autre benchmark, elle doit être optimisée à l'aide des profils des deux benchmarks. Dans ce cas, l'option show de llvm-profdata est utile:

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

Pour mapper des unique_id à des bibliothèques individuelles, recherchez dans la sortie show pour chaque unique_id un nom de fonction propre à la bibliothèque.

Étude de cas: PGO pour l'ART

L'étude de cas présente ART comme un exemple pertinent. Toutefois, elle ne constitue pas une description précise de l'ensemble réel de bibliothèques profilées pour ART ni de leurs interdépendances.

Le compilateur AART dex2oat dans ART dépend de libart-compiler.so, qui dépend à son tour de libart.so. L'environnement d'exécution ART est implémenté principalement dans libart.so. Les benchmarks du compilateur et de l'environnement d'exécution seront différents:

Benchmark Bibliothèques profilées
dex2oat dex2oat (exécutable), libart-compiler.so, libart.so
art_runtime libart.so
  1. Ajoutez la propriété pgo suivante à dex2oat, libart-compiler.so:
        pgo: {
            instrumentation: true,
            benchmarks: ["dex2oat",],
            profile_file: "dex2oat.profdata",
        }
  2. Ajoutez la propriété pgo suivante à libart.so:
        pgo: {
            instrumentation: true,
            benchmarks: ["art_runtime", "dex2oat",],
            profile_file: "libart.profdata",
        }
  3. Créez des builds instrumentés pour les benchmarks dex2oat et art_runtime à l'aide des éléments suivants:
        make ANDROID_PGO_INSTRUMENT=dex2oat
        make ANDROID_PGO_INSTRUMENT=art_runtime
  4. Vous pouvez également créer une seule compilation instrumentée avec toutes les bibliothèques instrumentées à l'aide de:

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

    La deuxième commande compile tous les modules compatibles avec PGO pour le profilage.

  5. Exécutez les benchmarks qui utilisent dex2oat et art_runtime pour obtenir :
    • Trois fichiers .profraw de dex2oat (dex2oat_exe.profdata, dex2oat_libart-compiler.profdata et dexeoat_libart.profdata), identifiés à l'aide de la méthode décrite dans Gérer les fichiers de profil LLVM.
    • Un seul art_runtime_libart.profdata.
  6. Générez un fichier profdata commun pour l'exécutable dex2oat et libart-compiler.so à l'aide de:
    llvm-profdata merge -output=dex2oat.profdata \
        dex2oat_exe.profdata dex2oat_libart-compiler.profdata
  7. Obtenez le profil de libart.so en fusionnant les profils des deux benchmarks:
    llvm-profdata merge -output=libart.profdata \
        dex2oat_libart.profdata art_runtime_libart.profdata

    Les nombres bruts de libart.so des deux profils peuvent être différents, car les benchmarks diffèrent en termes de nombre de cas de test et de durée d'exécution. Dans ce cas, vous pouvez utiliser une fusion pondérée:

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

    La commande ci-dessus attribue deux fois le poids au profil à partir de dex2oat. Le poids réel doit être déterminé en fonction des connaissances du domaine ou de l'expérimentation.

  8. Vérifiez les fichiers de profil dex2oat.profdata et libart.profdata dans toolchain/pgo-profiles pour les utiliser lors de la compilation.