Dateien bestellen

Die Bestelldatei ist eine neue Optimierungstechnik für Linker. Diese Bestelldateien sind Textdateien, die Symbole enthalten, die Funktionen darstellen. Linker wie lld verwenden Bestelldateien, um Funktionen in einer bestimmten Reihenfolge anzuordnen. Diese Binärdateien oder Bibliotheken mit sortierten Symbolen reduzieren Seitenfehler und verbessern die Startzeit eines Programms, da Symbole beim Kaltstart eines Programms effizient geladen werden.

Sie können Ihrer Anwendung Bestelldateifunktionen hinzufügen. Dazu sind drei Schritte erforderlich:

  1. Profile und Zuordnungsdatei generieren
  2. Bestelldatei aus den Profilen und der Zuordnungsdatei erstellen
  3. Bestelldatei während des Release-Builds verwenden, um die Symbole anzuordnen

Bestelldatei generieren

Zum Generieren einer Bestelldatei sind drei Schritte erforderlich:

  1. Instrumentierte Version der App erstellen, die die Bestelldatei schreibt
  2. App ausführen, um die Profile zu generieren
  3. Profile und Zuordnungsdatei nachbearbeiten

Instrumentierten Build erstellen

Die Profile werden durch Ausführen eines instrumentierten Builds der Anwendung generiert. Für einen instrumentierten Build müssen sowohl den Compiler- als auch den Linker-Flags -forder-file-instrumentation hinzugefügt werden. -mllvm -orderfile-write-mapping=<filename>-mapping.txt muss unbedingt den Compiler-Flags hinzugefügt werden. Das Instrumentierungsflag aktiviert die Bestelldateiinstrumentierung für die Profilerstellung und lädt die für die Profilerstellung erforderliche Bibliothek. Das Zuordnungsflag gibt hingegen nur die Zuordnungsdatei aus, die den MD5-Hash für jede Funktion in der Binärdatei oder Bibliothek enthält.

Außerdem müssen Sie ein beliebiges Optimierungsflag außer -O0 übergeben, da sowohl das Instrumentierungsflag als auch das Zuordnungsflag eines erfordern. Wenn kein Optimierungsflag übergeben wird, wird die Zuordnungsdatei nicht generiert und der instrumentierte Build gibt möglicherweise falsche Hashes in die Profildatei aus.

ndk-build

Erstellen Sie den Build mit APP_OPTIM=release, damit ndk-build einen anderen Optimierungsmodus als -O0 verwendet. Beim Erstellen mit AGP ist dies für Release-Builds automatisch der Fall.

LOCAL_CFLAGS += \
    -forder-file-instrumentation \
    -mllvm -orderfile-write-mapping=mapping.txt \

LOCAL_LDFLAGS += -forder-file-instrumentation

CMake

Verwenden Sie einen anderen CMAKE_BUILD_TYPE als Debug, damit CMake einen anderen Optimierungsmodus als -O0 verwendet. Beim Erstellen mit AGP ist dies für Release-Builds automatisch der Fall.

target_compile_options(orderfiledemo PRIVATE
    -forder-file-instrumentation
    -mllvm -orderfile-write-mapping=mapping.txt
)
target_link_options(orderfiledemo PRIVATE -forder-file-instrumentation)

Andere Buildsysteme

Kompilieren Sie den Code mit -forder-file-instrumentation -O1 -mllvm -orderfile-write-mapping=mapping.txt.

-O1 ist nicht unbedingt erforderlich, aber verwenden Sie nicht -O0.

Lassen Sie -mllvm -orderfile-write-mapping=mapping.txt beim Verknüpfen weg.

Alle diese Flags sind für einen Release-Build nicht erforderlich und sollten daher von einer Buildvariablen gesteuert werden. Zur Vereinfachung können Sie alles in der Datei CMakeLists.txt einrichten, wie in unserem Beispiel.

Bestelldateibibliothek erstellen

Zusätzlich zu den Flags muss die Profildatei eingerichtet werden und die instrumentierte Binärdatei muss während der Ausführung explizit einen Profilschreibvorgang auslösen.

  • Rufen Sie __llvm_profile_set_filename(PROFILE_DIR "/<filename>-%m.profraw") auf, um den Profilpfad einzurichten. Obwohl das übergebene Argument <filename>-%m.profraw ist, wird die Profildatei als <filename>-%m.profraw.order gespeichert. Achten Sie darauf, dass PROFILE_DIR von der App beschreibbar ist und Sie Zugriff auf das Verzeichnis haben.
    • Da viele gemeinsam genutzte Bibliotheken profiliert werden, ist %m nützlich, da es zu einer eindeutigen Modulsignatur für die Bibliothek erweitert wird, was zu einem separaten Profil pro Bibliothek führt. Weitere Musterspezifizierer finden Sie unter diesem Link.
  • Rufen Sie __llvm_profile_initialize_file() auf, um die Profildatei einzurichten.
  • Rufen Sie __llvm_orderfile_dump() auf, um explizit in die Profildatei zu schreiben.

Die Profile werden im Arbeitsspeicher erfasst und die Dump-Funktion schreibt sie in die Datei. Achten Sie darauf, dass die Dump-Funktion am Ende des Starts aufgerufen wird, damit Ihre Profildatei alle Symbole bis zum Ende des Starts enthält.

extern "C" {
extern int __llvm_profile_set_filename(const char*);
extern int __llvm_profile_initialize_file(void);
extern int __llvm_orderfile_dump(void);
}

#define PROFILE_DIR "<location-writable-from-app>"
void workload() {
  // ...
  // run workload
  // ...

  // set path and write profiles after workload execution
  __llvm_profile_set_filename(PROFILE_DIR "/default-%m.profraw");
  __llvm_profile_initialize_file();
  __llvm_orderfile_dump();
  return;
}

Build für Profile ausführen

Führen Sie die instrumentierte App auf einem physischen oder virtuellen Gerät aus, um die Profile zu generieren. Sie können die Profildateien mit adb pull extrahieren.

adb shell "run-as <package-name> sh -c 'cat /data/user/0/<package-name>/cache/default-%m.profraw.order' | cat > /data/local/tmp/default-%m.profraw.order"
adb pull /data/local/tmp/default-%m.profraw.order .

Wie bereits erwähnt, muss der Ordner, der die geschriebene Profildatei enthält, für Sie zugänglich sein. Wenn es sich um ein virtuelles Gerät handelt, sollten Sie die Emulatoren mit Google Play Store vermeiden, da Sie keinen Zugriff auf viele Ordner haben.

Profil- und Zuordnungsdatei nachbearbeiten

Wenn Sie die Profile erhalten, müssen Sie die Zuordnungsdatei suchen und jedes Profil in ein Hexadezimalformat konvertieren. Normalerweise finden Sie die Zuordnungsdatei im Buildordner der App. Wenn Sie beides haben, können Sie mit unserem Skript eine Profildatei und die richtige Zuordnungsdatei verwenden, um eine Bestelldatei zu generieren.

Linux/Mac/ChromeOS

hexdump -C default-%m.profraw.order > default-%m.prof
python3 create_orderfile.py --profile-file default-%m.prof --mapping-file <filename>-mapping.txt

Windows

certutil -f -encodeHex default-%m.profraw.order default-%m.prof
python3 create_orderfile.py --profile-file default-%m.prof --mapping-file <filename>-mapping.txt

Weitere Informationen zum Skript finden Sie in dieser README-Datei.

Bestelldatei zum Erstellen der Anwendung verwenden

Nachdem Sie eine Bestelldatei generiert haben, sollten Sie die vorherigen Flags und die Bestelldateifunktionen entfernen, da diese nur für die Generierungsschritte vorgesehen sind. Sie müssen nur -Wl,--symbol-ordering-file=<filename>.orderfile an die Compiler- und Linker-Flags übergeben. Manchmal können Symbole nicht gefunden oder verschoben werden und es werden Warnungen ausgegeben. Sie können -Wl,--no-warn-symbol-ordering übergeben, um diese Warnungen zu unterdrücken.

ndk-build

LOCAL_CFLAGS += \
    -Wl,--symbol-ordering-file=<filename>.orderfile \
    -Wl,--no-warn-symbol-ordering \

LOCAL_LDFLAGS += \
    -Wl,--symbol-ordering-file=<filename>.orderfile \
    -Wl,--no-warn-symbol-ordering \

CMake

target_compile_options(orderfiledemo PRIVATE
    -Wl,--symbol-ordering-file=<filename>.orderfile
    -Wl,--no-warn-symbol-ordering
)
target_link_options(orderfiledemo PRIVATE
    -Wl,--symbol-ordering-file=<filename>.orderfile
    -Wl,--no-warn-symbol-ordering
)

Andere Buildsysteme

Kompilieren Sie den Code mit -Wl,--symbol-ordering-file=<filename>.orderfile -Wl,--no-warn-symbol-ordering.

Weitere Informationen finden Sie im Beispiel für eine Bestelldatei.

Implementierungsdetails für Bestelldateien

Es gibt viele Möglichkeiten, Bestelldateien zu generieren und sie für das Erstellen zu verwenden. Das NDK verwendet die Methode von LLVM. Daher ist sie für Ihre gemeinsam genutzten C- oder C++-Bibliotheken nützlicher als für die eigentliche Java- oder Kotlin-App. Clang verwendet jeden Funktionsnamen (jedes Symbol), erstellt einen MD5-Hash davon und gibt diese Beziehung in eine Zuordnungsdatei aus. Der MD5-Hash einer Funktion wird in die Profildatei (profraw-Format) geschrieben, wenn die Funktion zum ersten Mal ausgeführt wird. Bei nachfolgenden Ausführungen der Funktion wird der MD5-Hash nicht in die Profildatei geschrieben, um Duplikate zu vermeiden. Daher wird nur die erste Ausführung der Funktion in der Reihenfolge aufgezeichnet. Wenn Sie die Profildatei und die Zuordnungsdatei durchgehen, können Sie jeden MD5-Hash durch die entsprechende Funktion ersetzen und eine Bestelldatei erhalten.

Beispiele für eine Profildatei im Hexadezimalformat und eine Zuordnungsdatei finden Sie unter example.prof und example-mapping.txt bzw.