Pliki zamówień

Plik kolejności to najnowsza technika optymalizacji linkera. Te pliki kolejności to pliki tekstowe zawierające symbole reprezentujące funkcje. Linkery, takie jak lld, używają plików kolejności do układania funkcji w określonej kolejności. Te pliki binarne lub biblioteki z uporządkowanymi symbolami zmniejszają liczbę błędów strony i skracają czas uruchamiania programu dzięki efektywnemu wczytywaniu symboli podczas zimnego startu programu.

Funkcje pliku kolejności można dodać do aplikacji, wykonując te 3 kroki:

  1. Wygeneruj profile i plik mapowania
  2. Utwórz plik kolejności na podstawie profili i pliku mapowania
  3. Użyj pliku kolejności podczas kompilacji do publikacji, aby ułożyć symbole

Generowanie pliku kolejności

Generowanie pliku kolejności wymaga wykonania tych 3 kroków:

  1. Utwórz wersję aplikacji z instrumentacją, która zapisuje plik kolejności
  2. Uruchom aplikację, aby wygenerować profile
  3. Przetwórz profile i plik mapowania

Tworzenie kompilacji z instrumentacją

Profile są generowane przez uruchomienie kompilacji aplikacji z instrumentacją. Kompilacja z instrumentacją wymaga dodania -forder-file-instrumentation do flag kompilatora i linkera oraz -mllvm -orderfile-write-mapping=<filename>-mapping.txt dodania do flag kompilatora. Flaga instrumentacji włącza instrumentację pliku kolejności na potrzeby profilowania i wczytuje konkretną bibliotekę potrzebną do profilowania. Z kolei flaga mapowania po prostu generuje plik mapowania, który pokazuje skrót MD5 każdej funkcji w pliku binarnym lub bibliotece.

Dodatkowo pamiętaj, aby przekazać dowolną flagę optymalizacji inną niż -O0, ponieważ zarówno flaga instrumentacji, jak i flaga mapowania wymagają jednej z nich. Jeśli nie zostanie przekazana żadna flaga optymalizacji, plik mapowania nie zostanie wygenerowany, a kompilacja z instrumentacją może wygenerować nieprawidłowe skróty w pliku profilu.

ndk-build

Pamiętaj, aby kompilować za pomocą APP_OPTIM=release, aby ndk-build używał trybu optymalizacji innego niż -O0. W przypadku kompilacji za pomocą AGP jest to automatyczne w przypadku kompilacji wersji.

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

LOCAL_LDFLAGS += -forder-file-instrumentation

CMake

Pamiętaj, aby używać CMAKE_BUILD_TYPE innego niż Debug, aby CMake używał trybu optymalizacji innego niż -O0. W przypadku kompilacji za pomocą AGP jest to automatyczne w przypadku kompilacji wersji.

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

Inne systemy kompilacji

Skompiluj kod za pomocą -forder-file-instrumentation -O1 -mllvm -orderfile-write-mapping=mapping.txt.

-O1 nie jest wymagane, ale nie używaj -O0.

Podczas łączenia pomiń -mllvm -orderfile-write-mapping=mapping.txt.

Wszystkie te flagi nie są potrzebne w przypadku kompilacji do publikacji, więc powinny być kontrolowane przez zmienną kompilacji. Aby uprościć, możesz skonfigurować to wszystko w pliku CMakeLists.txt, tak jak w naszym przykładzie.

Tworzenie biblioteki plików kolejności

Oprócz flag należy skonfigurować plik profilu, a plik binarny z instrumentacją musi jawnie wywołać zapis profilu podczas wykonywania.

  • Aby skonfigurować ścieżkę profilu, wywołaj __llvm_profile_set_filename(PROFILE_DIR "/<filename>-%m.profraw") dla. Chociaż przekazany argument to <filename>-%m.profraw, plik profilu jest zapisywany jako <filename>-%m.profraw.order. Upewnij się, że aplikacja ma uprawnienia do zapisu w PROFILE_DIR i że masz dostęp do tego katalogu.
    • Ze względu na profilowanie wielu bibliotek współdzielonych %m jest przydatne, ponieważ rozszerza się do unikalnego podpisu modułu biblioteki, co powoduje utworzenie osobnego profilu dla każdej biblioteki. Więcej specyfikatorów wzorców znajdziesz pod tym linkiem.
  • Aby skonfigurować plik profilu, wywołaj __llvm_profile_initialize_file().
  • Aby jawnie zapisać w pliku profilu, wywołaj __llvm_orderfile_dump().

Profile są zbierane w pamięci, a funkcja zrzutu zapisuje je w pliku. Musisz się upewnić, że funkcja zrzutu jest wywoływana na końcu uruchamiania, aby plik profilu zawierał wszystkie symbole do końca uruchamiania.

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;
}

Uruchamianie kompilacji na potrzeby profili

Aby wygenerować profile, uruchom aplikację z instrumentacją na urządzeniu fizycznym lub urządzeniu wirtualnym. Pliki profilu możesz wyodrębnić za pomocą adb pull.

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 .

Jak wspomnieliśmy wcześniej, upewnij się, że masz dostęp do folderu zawierającego zapisany plik profilu. Jeśli jest to urządzenie wirtualne, możesz unikać emulatorów ze Sklepem Play, ponieważ nie masz dostępu do wielu folderów.

Przetwarzanie pliku profilu i pliku mapowania

Gdy uzyskasz profile, musisz znaleźć plik mapowania i przekonwertować każdy profil na format szesnastkowy. Zazwyczaj plik mapowania można znaleźć w folderze kompilacji aplikacji. Gdy masz oba pliki, możesz użyć naszego skryptu aby na podstawie pliku profilu i prawidłowego pliku mapowania wygenerować plik kolejności.

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

Jeśli chcesz dowiedzieć się więcej o skrypcie, przeczytaj ten plik README.

Używanie pliku kolejności do kompilowania aplikacji

Po wygenerowaniu pliku kolejności usuń wcześniejsze flagi i funkcje pliku kolejności, ponieważ są one przeznaczone tylko do etapów generowania. Wystarczy przekazać -Wl,--symbol-ordering-file=<filename>.orderfile do flag kompilatora i linkera. Czasami nie można znaleźć symboli lub nie można ich przenieść, co powoduje wyświetlenie ostrzeżeń. Aby je pominąć, możesz przekazać -Wl,--no-warn-symbol-ordering.

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
)

Inne systemy kompilacji

Skompiluj kod za pomocą -Wl,--symbol-ordering-file=<filename>.orderfile -Wl,--no-warn-symbol-ordering.

Więcej informacji znajdziesz w przykładzie pliku kolejności.

Szczegóły implementacji pliku kolejności

Istnieje wiele sposobów generowania plików kolejności i używania ich do kompilacji. NDK używa metody LLVM, więc jest ona najbardziej przydatna w przypadku bibliotek współdzielonych C lub C++ niż w przypadku aplikacji Java lub Kotlin. Clang pobiera nazwę każdej funkcji (symbol) i tworzy jej skrót MD5, a następnie zapisuje tę relację w pliku mapowania. Skrót MD5 funkcji jest zapisywany w pliku profilu (format profraw), gdy funkcja jest wykonywana po raz pierwszy. Kolejne wykonania funkcji nie zapisują jej skrótu MD5 w pliku profilu, ponieważ chcą uniknąć duplikatów. W rezultacie w kolejności jest rejestrowane tylko pierwsze wykonanie funkcji. Przeglądając plik profilu i plik mapowania, możesz pobrać każdy skrót MD5 i zastąpić go odpowiednią funkcją, aby uzyskać plik kolejności.

Przykłady pliku profilu w formacie szesnastkowym i pliku mapowania znajdziesz odpowiednio w plikach example.prof i example-mapping.txt.