Profilbasierte Optimierung verwenden

Das Android-Buildsystem für Android 13 und niedriger unterstützt die Verwendung der profilbasierten Optimierung (PGO) von Clang in nativen Android-Modulen mit Blueprint-Buildregeln. Auf dieser Seite wird Clang PGO beschrieben, wie Sie Profile für PGO kontinuierlich generieren und aktualisieren und wie Sie PGO in das Build-System einbinden (mit Anwendungsfall).

Hinweis: In diesem Dokument wird die Verwendung von PGO auf der Android-Plattform beschrieben. Auf dieser Seite finden Sie Informationen zur Verwendung von PGO in einer Android-App.

Clang PGO

Clang kann eine profilbasierte Optimierung mit zwei Arten von Profilen ausführen:

  • Instrumentierungsbasierte Profile werden aus einem instrumentierten Zielprogramm generiert. Diese Profile sind detailliert und verursachen einen hohen Laufzeitoverhead.
  • Sampling-basierte Profile werden in der Regel durch Hardwarezähler erstellt. Sie verursachen einen geringen Laufzeitoverhead und können ohne Instrumentierung oder Modifikation des Binärcodes erfasst werden. Sie sind weniger detailliert als instrumentierungsbasierte Profile.

Alle Profile sollten aus einer repräsentativen Arbeitslast generiert werden, die das typische Verhalten der App ausübt. Clang unterstützt sowohl AST-basiert (-fprofile-instr-generate) als auch LLVM-IR-basiert (-fprofile-generate)). Android unterstützt für die instrumentierungsbasierte PGO jedoch nur LLVM-IR-basiert.

Für die Profilerhebung sind die folgenden Flags erforderlich:

  • -fprofile-generate für IR-basierte Instrumente. Bei dieser Option verwendet das Backend einen gewichteten minimalen Spannbaum, um die Anzahl der Instrumentierungsstellen zu reduzieren und ihre Platzierung auf Kanten mit geringem Gewicht zu optimieren. Verwenden Sie diese Option auch für den Verknüpfungsschritt. Der Clang-Treiber übergibt die Profiling-Laufzeit (libclang_rt.profile-arch-android.a) automatisch an den Linker. Diese Bibliothek enthält Routinen zum Schreiben der Profile auf die Festplatte beim Beenden des Programms.
  • -gline-tables-only für die stichprobenbasierte Profilerhebung, um nur minimale Informationen zur Fehlerbehebung zu generieren.

Ein Profil kann für die automatische Optimierung mit -fprofile-use=pathname oder -fprofile-sample-use=pathname für instrumentierungsbasierte bzw. stichprobenbasierte Profile verwendet werden.

Hinweis:Wenn Clang die Profildaten nach Änderungen am Code nicht mehr verwenden kann, wird eine -Wprofile-instr-out-of-date-Warnung generiert.

PGO verwenden

So verwenden Sie PGO:

  1. Erstellen Sie die Bibliothek/Ausführbare Datei mit Instrumentierung, indem Sie -fprofile-generate an den Compiler und Linker übergeben.
  2. Erfassen Sie Profile, indem Sie eine repräsentative Arbeitslast auf der instrumentierten Binärdatei ausführen.
  3. Bearbeiten Sie die Profile mit dem Dienstprogramm llvm-profdata. Weitere Informationen finden Sie unter LLVM-Profildateien verarbeiten.
  4. Verwenden Sie die Profile, um PGO anzuwenden, indem Sie -fprofile-use=<>.profdata an den Compiler und den Linker übergeben.

Für PGO auf Android-Geräten sollten Profile offline erfasst und zusammen mit dem Code eingecheckt werden, um reproduzierbare Builds zu ermöglichen. Die Profile können verwendet werden, während der Code weiterentwickelt wird, müssen aber regelmäßig neu generiert werden (oder immer dann, wenn Clang warnt, dass die Profile veraltet sind).

Profile erfassen

Clang kann Profile verwenden, die durch Ausführen von Benchmarks mit einem instrumentierten Build der Bibliothek oder durch Stichprobenerhebung von Hardwarezählern beim Ausführen des Benchmarks erfasst wurden. Derzeit unterstützt Android keine Stichprobenerhebung von Profilen. Sie müssen Profile daher mit einem instrumentierten Build erfassen:

  1. Identifizieren Sie einen Benchmark und die Bibliotheken, die von diesem Benchmark gemeinsam genutzt werden.
  2. Fügen Sie dem Benchmark und den Bibliotheken pgo-Properties hinzu (Details siehe unten).
  3. Erstellen Sie einen Android-Build mit einer instrumentierten Kopie dieser Bibliotheken mit:
    make ANDROID_PGO_INSTRUMENT=benchmark

benchmark ist ein Platzhalter, der die Sammlung von Bibliotheken angibt, die während des Builds instrumentiert wurden. Die tatsächlichen repräsentativen Eingaben (und möglicherweise eine andere ausführbare Datei, die mit einer Bibliothek verknüpft ist, die einem Benchmarking unterzogen wird) sind nicht spezifisch für PGO und fallen nicht in den Geltungsbereich dieses Dokuments.

  1. Flashen oder synchronisieren Sie den instrumentierten Build auf einem Gerät.
  2. Führen Sie den Benchmark aus, um Profile zu erfassen.
  3. Verwenden Sie das Tool llvm-profdata (siehe unten), um die Profile nachzubearbeiten und für das Einchecken in den Quellbaum vorzubereiten.

Profile während des Builds verwenden

Überprüfen Sie die Profile in toolchain/pgo-profiles in einem Android-Baum. Der Name muss mit dem in der untergeordneten Property profile_file der Property pgo für die Bibliothek angegebenen Namen übereinstimmen. Das Build-System übergibt die Profildatei beim Erstellen der Bibliothek automatisch an Clang. Sie können die Umgebungsvariable ANDROID_PGO_DISABLE_PROFILE_USE auf true setzen, um PGO vorübergehend zu deaktivieren und den Leistungsvorteil zu messen.

Wenn Sie zusätzliche produktspezifische Profilverzeichnisse angeben möchten, hängen Sie sie an die PGO_ADDITIONAL_PROFILE_DIRECTORIES-Variable in einer BoardConfig.mk an. Wenn zusätzliche Pfade angegeben werden, werden die Profile in diesen Pfaden über die in toolchain/pgo-profiles hinweggeschrieben.

Wenn Sie ein Release-Image mit dem Ziel dist für make generieren, schreibt das Build-System die Namen der fehlenden Profildateien in $DIST_DIR/pgo_profile_file_missing.txt. In dieser Datei können Sie sehen, welche Profildateien versehentlich gelöscht wurden, wodurch PGO stillschweigend deaktiviert wird.

PGO in Android.bp-Dateien aktivieren

Wenn Sie PGO in Android.bp-Dateien für native Module aktivieren möchten, geben Sie einfach das Attribut pgo an. Diese Property hat die folgenden Untereigenschaften:

Property Beschreibung
instrumentation Legen Sie true für PGO mit Instrumentierung fest. Der Standardwert ist false.
sampling Legen Sie true für PGO mit Stichprobenerhebung fest. Der Standardwert ist false.
benchmarks Liste mit Strings. Dieses Modul wird für das Profiling verwendet, wenn in der Option ANDROID_PGO_INSTRUMENT build ein Benchmark in der Liste angegeben ist.
profile_file Profildatei (relativ zu toolchain/pgo-profile), die mit PGO verwendet werden soll. Im Build wird eine Warnung ausgegeben, dass diese Datei nicht existiert, indem sie $DIST_DIR/pgo_profile_file_missing.txt hinzugefügt wird, es sei denn, die Eigenschaft enable_profile_use ist auf false gesetzt ODER die Buildvariable ANDROID_PGO_NO_PROFILE_USE auf true.
enable_profile_use Legen Sie false fest, wenn Profile beim Build nicht verwendet werden sollen. Kann während des Bootstrappings verwendet werden, um die Profilerhebung zu aktivieren oder PGO vorübergehend zu deaktivieren. Der Standardwert ist true.
cflags Liste der zusätzlichen Flags, die bei einem instrumentierten Build verwendet werden sollen.

Beispiel für ein Modul mit 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",
    }
}

Wenn die Benchmarks benchmark1 und benchmark2 ein repräsentatives Verhalten für die Bibliotheken libstatic1, libstatic2 oder libshared1 zeigen, kann die Eigenschaft pgo dieser Bibliotheken auch die Benchmarks enthalten. Das defaults-Modul in Android.bp kann eine gemeinsame pgo-Spezifikation für eine Reihe von Bibliotheken enthalten, um zu vermeiden, dass dieselben Build-Regeln für mehrere Module wiederholt werden.

Wenn Sie verschiedene Profildateien auswählen oder PGO für eine Architektur selektiv deaktivieren möchten, geben Sie die Eigenschaften profile_file, enable_profile_use und cflags pro Architektur an. Beispiel (mit fett formatiertem Architekturziel):

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

Wenn Sie Verweise auf die Laufzeitbibliothek für das Profiling während des instrumentierungsbasierten Profilings auflösen möchten, übergeben Sie dem Linker das Build-Flag -fprofile-generate. Statische Bibliotheken, die mit PGO instrumentiert sind, alle gemeinsam genutzten Bibliotheken und alle Binärdateien, die direkt von der statischen Bibliothek abhängen, müssen ebenfalls für PGO instrumentiert werden. Für solche freigegebenen Bibliotheken oder ausführbaren Dateien müssen jedoch keine PGO-Profile verwendet werden und die enable_profile_use-Eigenschaft kann auf false gesetzt werden. Abgesehen von dieser Einschränkung können Sie PGO auf jede statische Bibliothek, jede freigegebene Bibliothek oder jede ausführbare Datei anwenden.

LLVM-Profildateien verarbeiten

Wenn Sie eine instrumentierte Bibliothek oder ausführbare Datei ausführen, wird eine Profildatei mit dem Namen default_unique_id_0.profraw in /data/local/tmp erstellt. Dabei ist unique_id ein numerischer Hashwert, der für diese Bibliothek eindeutig ist. Wenn diese Datei bereits vorhanden ist, wird das neue Profil beim Schreiben der Profile von der Laufzeit für das Profiling mit dem alten Profil zusammengeführt. Hinweis: /data/local/tmp ist für App-Entwickler nicht zugänglich. Sie sollten stattdessen eine Adresse wie /storage/emulated/0/Android/data/packagename/files verwenden. Wenn Sie den Speicherort der Profildatei ändern möchten, legen Sie die Umgebungsvariable LLVM_PROFILE_FILE zur Laufzeit fest.

Mit dem Dienstprogramm llvm-profdata wird dann die .profraw-Datei konvertiert (und gegebenenfalls mehrere .profraw-Dateien zusammengeführt) in eine .profdata-Datei:

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

profile.profdata kann dann in den Quellbaum für die Verwendung während des Builds eingecheckt werden.

Wenn während eines Benchmarks mehrere instrumentierte Binärdateien/Bibliotheken geladen werden, generiert jede Bibliothek eine separate .profraw-Datei mit einer separaten eindeutigen ID. Normalerweise können alle diese Dateien in eine einzige .profdata-Datei zusammengeführt und für den PGO-Build verwendet werden. Wenn eine Bibliothek von einem anderen Benchmark genutzt wird, muss sie mithilfe von Profilen aus beiden Benchmarks optimiert werden. In diesem Fall ist die Option show von llvm-profdata nützlich:

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

Wenn Sie unique_ids einzelnen Bibliotheken zuordnen möchten, suchen Sie in der show-Ausgabe für jede unique_id nach einem Funktionsnamen, der für die Bibliothek eindeutig ist.

Fallstudie: PGO für ART

In der Fallstudie wird ART als nachvollziehbares Beispiel vorgestellt. Es ist jedoch keine genaue Beschreibung der tatsächlichen Bibliotheken, die für ART profiliert wurden, oder ihrer Abhängigkeiten.

Der dex2oat-Compiler in ART hängt von libart-compiler.so ab, der wiederum von libart.so abhängt. Die ART-Laufzeit wird hauptsächlich in libart.so implementiert. Die Benchmarks für den Compiler und die Laufzeit unterscheiden sich:

Benchmark Profilierte Bibliotheken
dex2oat dex2oat (ausführbar), libart-compiler.so, libart.so
art_runtime libart.so
  1. Fügen Sie dex2oat, libart-compiler.so die folgende pgo-Property hinzu:
        pgo: {
            instrumentation: true,
            benchmarks: ["dex2oat",],
            profile_file: "dex2oat.profdata",
        }
  2. Fügen Sie libart.so die folgende pgo-Eigenschaft hinzu:
        pgo: {
            instrumentation: true,
            benchmarks: ["art_runtime", "dex2oat",],
            profile_file: "libart.profdata",
        }
  3. Erstellen Sie instrumentierte Builds für die dex2oat- und art_runtime-Benchmarks mit:
        make ANDROID_PGO_INSTRUMENT=dex2oat
        make ANDROID_PGO_INSTRUMENT=art_runtime
  4. Alternativ können Sie einen einzelnen instrumentierten Build mit allen Bibliotheken erstellen, die mithilfe der folgenden Tools instrumentiert wurden:

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

    Mit dem zweiten Befehl werden alle PGO-fähigen Module für das Profiling erstellt.

  5. Führen Sie die Benchmarks mit dex2oat und art_runtime aus, um Folgendes zu erhalten:
    • Drei .profraw-Dateien von dex2oat (dex2oat_exe.profdata, dex2oat_libart-compiler.profdata und dexeoat_libart.profdata), die mit der unter LLVM-Profildateien verarbeiten beschriebenen Methode ermittelt wurden.
    • Eine einzelne art_runtime_libart.profdata.
  6. Erstellen Sie eine gemeinsame profdata-Datei für die ausführbare Datei dex2oat und libart-compiler.so mit:
    llvm-profdata merge -output=dex2oat.profdata \
        dex2oat_exe.profdata dex2oat_libart-compiler.profdata
  7. Erstellen Sie das Profil für libart.so, indem Sie die Profile aus den beiden Benchmarks zusammenführen:
    llvm-profdata merge -output=libart.profdata \
        dex2oat_libart.profdata art_runtime_libart.profdata

    Die Rohzahlen für libart.so aus den beiden Profilen können unterschiedlich sein, da sich die Benchmarks in der Anzahl der Testfälle und der Dauer unterscheiden, für die sie ausgeführt werden. In diesem Fall können Sie eine gewichtete Zusammenführung verwenden:

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

    Mit dem obigen Befehl wird dem Profil von dex2oat das doppelte Gewicht zugewiesen. Das tatsächliche Gewicht sollte auf der Grundlage von Fachwissen oder Tests ermittelt werden.

  8. Prüfen Sie die Profildateien dex2oat.profdata und libart.profdata in toolchain/pgo-profiles für die Verwendung während des Builds.