Korzystanie z optymalizacji kierowania na podstawie profilu

System kompilacji Androida w wersji 13 lub starszej obsługuje optymalizację kierowaną przez profil (PGO) w narzędziu Clang w natywności natywnych modułów Androida, które mają reguły kompilacji blueprint. Na tej stronie opisaliśmy Clang PGO, ciągłe generowanie i aktualizowanie profili używanych w PGO oraz integrację PGO z systemem kompilacji (z przykladem zastosowania).

Uwaga: ten dokument opisuje korzystanie z PGO na platformie Android. Aby dowiedzieć się więcej o używaniu PGO w aplikacji na Androida, wejdź na tę stronę.

Informacje o Clang PGO

Clang może wykonywać optymalizację kierowaną przez profil za pomocą 2 typów profili:

  • Profile oparte na pomiarach są generowane na podstawie zinstrumentowanego programu docelowego. Te profile są szczegółowe i wymagają dużych nakładów na czas wykonywania.
  • Profile oparte na próbkowaniu są zwykle tworzone przez liczniki sprzętowe. Nie obciążają one procesora podczas działania i mogą być zbierane bez konieczności instrumentowania ani modyfikowania pliku binarnego. Są one mniej szczegółowe niż profile oparte na pomiarach.

Wszystkie profile powinny być generowane na podstawie reprezentatywnego obciążenia, które odzwierciedla typowe zachowanie aplikacji. Clang obsługuje zarówno profile oparte na AST (-fprofile-instr-generate), jak i na LLVM IR (-fprofile-generate)). Android obsługuje profile oparte na LLVM IR tylko w przypadku PGO z użyciem instrumentacji.

Aby móc tworzyć profile, musisz użyć tych flag:

  • -fprofile-generate dla urządzeń wykorzystujących podczerwień. W przypadku tej opcji backend używa ważonego minimalnego drzewa rozpinającego, aby zmniejszyć liczbę punktów pomiarowych i zoptymalizować ich rozmieszczenie na krawędziach o niskiej wadze (używaj tej opcji również w przypadku kroku łączenia). Kompilator Clang automatycznie przekazuje środowisko wykonawcze do profilowania (libclang_rt.profile-arch-android.a) do linkera. Biblioteka ta zawiera procedury zapisywania profili na dysku po zakończeniu pracy programu.
  • -gline-tables-only w przypadku zbierania profili na podstawie próbkowania, aby generować minimalne informacje debugowania.

Profil może być używany do PGO za pomocą -fprofile-use=pathname lub -fprofile-sample-use=pathname odpowiednio dla profili opartych na pomiarach i profili opartych na próbkowaniu.

Uwaga: jeśli po wprowadzeniu zmian w kodzie Clang nie może już korzystać z danych profilu, generuje ostrzeżenie -Wprofile-instr-out-of-date.

Używanie PGO

Korzystanie z PGO obejmuje te kroki:

  1. Utwórz bibliotekę lub plik wykonywalny z użyciem instrumentacji, przekazując parametr-fprofile-generate do kompilatora i linkera.
  2. Zbieraj profile, uruchamiając reprezentatywny zestaw zadań na zaimplementowanym binarnym pliku.
  3. Przeprowadź dodatkowe przetwarzanie profili za pomocą narzędzia llvm-profdata (szczegółowe informacje znajdziesz w artykule Praca z plikami profilu LLVM).
  4. Użyj profili, aby zastosować PGO, przekazując parametr -fprofile-use=<>.profdata do kompilatora i linkera.

W przypadku PGO na Androidzie profile powinny być zbierane w trybie offline i sprawdzane wraz z kodem, aby zapewnić możliwość odtworzenia kompilacji. Profilów można używać w miarę ewolucji kodu, ale należy je okresowo odtwarzać (lub zawsze, gdy Clang ostrzega, że są nieaktualne).

Zbieranie profili

Clang może używać profili zebranych przez uruchomione benchmarki za pomocą biblioteki z instrukcjami lub próbkowania rejestrów sprzętowych podczas uruchamiania benchmarku. Obecnie Android nie obsługuje zbierania profili na podstawie próbkowania, dlatego musisz zbierać profile za pomocą wersji z instrumentacją:

  1. Określ punkt odniesienia i zbiór bibliotek, które są używane przez ten punkt odniesienia.
  2. Dodaj do zestawu danych i bibliotek właściwości pgo (szczegóły poniżej).
  3. Utwórz wersję Androida z przeanalizowaną kopią tych bibliotek, korzystając z jednego z tych narzędzi:
    make ANDROID_PGO_INSTRUMENT=benchmark

benchmark to element zastępczy, który identyfikuje kolekcję bibliotek zinstrumentacją podczas kompilacji. Rzeczywiste reprezentatywne dane wejściowe (i być może inny plik wykonywalny, który łączy się z biblioteką, dla której wykonywane są testy porównawcze) nie są specyficzne dla PGO i nie są objęte zakresem tego dokumentu.

  1. Wgrywanie lub synchronizacja zmodyfikowanej wersji na urządzeniu.
  2. Uruchom test porównawczy, aby zebrać profile.
  3. Użyj narzędzia llvm-profdata (omówionego poniżej), aby przetworzyć profile i przygotować je do zacheckowania w drzewie źródeł.

Korzystanie z profili podczas kompilacji

Sprawdź profile w toolchain/pgo-profiles w drzewie Androida. Nazwa powinna być zgodna z wartością podrzędnej właściwości profile_file w przypadku właściwości pgo biblioteki. Podczas kompilowania biblioteki system kompilacji automatycznie przekazuje plik profilu do Clanga. Zmienną środowiskową ANDROID_PGO_DISABLE_PROFILE_USE można ustawić na wartość true, aby tymczasowo wyłączyć PGO i zmierzyć korzyści dla skuteczności.

Aby podać dodatkowe katalogi profili związane z poszczególnymi produktami, dodaj je do zmiennej PGO_ADDITIONAL_PROFILE_DIRECTORIES w sekcji BoardConfig.mk. Jeśli zostaną określone dodatkowe ścieżki, profile na tych ścieżkach zastąpią te w pliku toolchain/pgo-profiles.

Podczas generowania obrazu wersji za pomocą celu dist w pliku make system kompilacji zapisuje nazwy brakujących plików profilu w pliku $DIST_DIR/pgo_profile_file_missing.txt. Możesz sprawdzić ten plik, aby zobaczyć, które pliki profilu zostały przypadkowo usunięte (co powoduje automatyczne wyłączenie PGO).

Włączanie PGO w plikach Android.bp

Aby włączyć PGO w plikach Android.bp dla modułów natywnych, po prostu określ właściwość pgo. Ta usługa ma te usługi podrzędne:

Usługa Opis
instrumentation Ustaw na true w przypadku PGO z wykorzystaniem pomiarów. Wartość domyślna to false.
sampling Ustaw na true, aby używać PGO z próbkowaniem. Wartość domyślna to false.
benchmarks Lista ciągów tekstowych. Ten moduł jest przeznaczony do profilowania, jeśli w opcji ANDROID_PGO_INSTRUMENT build podany jest dowolny benchmark z listy.
profile_file Plik profilu (względnie do toolchain/pgo-profile) do użycia z PGO. Kompilacja ostrzega, że ten plik nie istnieje, dodając ten plik do $DIST_DIR/pgo_profile_file_missing.txt chyba że właściwość enable_profile_use jest ustawiona na false LUB zmienna kompilacji ANDROID_PGO_NO_PROFILE_USE jest ustawiona na true.
enable_profile_use Ustaw na false, jeśli nie chcesz używać profili podczas kompilacji. Można go używać podczas uruchamiania, aby włączyć zbieranie danych do profilu lub tymczasowo wyłączyć PGO. Wartość domyślna to true.
cflags Lista dodatkowych flag do użycia podczas kompilacji z instrumentacją.

Przykład modułu z 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",
    }
}

Jeśli punkty odniesienia benchmark1benchmark2 wykazują się reprezentatywnym zachowaniem w przypadku bibliotek libstatic1, libstatic2 lub libshared1, właściwości tych bibliotek pgo mogą również zawierać punkty odniesienia. Moduł defaults w pliku Android.bp może zawierać wspólną specyfikację pgo dla zestawu bibliotek, aby uniknąć powtarzania tych samych reguł kompilacji dla wielu modułów.

Aby wybrać różne pliki profilu lub selektywnie wyłączyć PGO dla danej architektury, określ właściwości profile_file, enable_profile_use i cflags dla każdej architektury. Przykład (z celem dotyczącym architektury w pogrubieniu):

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

Aby podczas profilowania na podstawie instrumentacji rozwiązywać odwołania do biblioteki profilowania w czasie wykonywania, przekaż linkerowi flagę kompilacji -fprofile-generate. Biblioteki statyczne zinstrumentowane za pomocą PGO, wszystkie biblioteki współdzielone i wszystkie pliki binarne, które są bezpośrednio zależne od biblioteki statycznej, muszą być również zinstrumentowane za pomocą PGO. Takie udostępnione biblioteki lub pliki wykonywalne nie muszą jednak używać profili PGO, a właściwość enable_profile_use może mieć wartość false. Poza tą restrykcją możesz zastosować PGO do dowolnej statycznej biblioteki, współdzielonej biblioteki lub pliku wykonywalnego.

Obsługa plików profilu LLVM

Wykonywanie zinstrumentowanej biblioteki lub pliku wykonywalnego powoduje wygenerowanie pliku profilu o nazwie default_unique_id_0.profraw w folderze /data/local/tmp (gdzie unique_id to identyfikator numeryczny, który jest niepowtarzalny dla tej biblioteki). Jeśli ten plik już istnieje, podczas zapisywania profili środowisko uruchomieniowe profilowania scala nowy profil ze starym. Pamiętaj, że deweloperzy aplikacji nie mają dostępu do wartości /data/local/tmp. Powinni zamiast tego używać wartości takich jak /storage/emulated/0/Android/data/packagename/files. Aby zmienić lokalizację pliku profilu, ustaw zmienną środowiskową LLVM_PROFILE_FILE w czasie wykonywania.

Następnie za pomocą narzędzia llvm-profdata konwertujesz plik .profraw (i ewentualnie scalasz kilka plików .profraw) na plik .profdata:

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

profile.profdata można wtedy zaimportować do drzewa źródeł na potrzeby kompilacji.

Jeśli podczas testu porównawczego wczytywane są skompilowane biblioteki binarne, każda z nich generuje osobny plik .profraw z osobnym identyfikatorem. Wszystkie te pliki można zwykle scalić w jeden plik .profdata i używać go do tworzenia PGO. W przypadku biblioteki, która jest używana przez inny test porównawczy, należy ją zoptymalizować, korzystając z profili z obu testów porównawczych. W tej sytuacji przydatna jest opcja show: llvm-profdata:

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

Aby zmapować unique_id na poszczególne biblioteki, wyszukaj w wyniku show dla każdego unique_id nazwę funkcji, która jest unikalna dla danej biblioteki.

Studium przypadku: PGO dla ART

Przypadek ten przedstawia ART jako przykład, ale nie jest to dokładny opis rzeczywistego zestawu bibliotek profilowanych pod kątem ART ani ich wzajemnych zależności.

Kompilator dex2oat w ART zależy od libart-compiler.so, który z kolei zależy od libart.so. Środowisko wykonawcze ART jest wdrażane głównie w libart.so. Benchmarki dla kompilatora i czasu wykonywania będą różne:

Benchmark Profilowane biblioteki
dex2oat dex2oat (plik wykonywalny), libart-compiler.so,libart.so
art_runtime libart.so
  1. Dodaj do dex2oat, libart-compiler.so tę właściwość pgo:
        pgo: {
            instrumentation: true,
            benchmarks: ["dex2oat",],
            profile_file: "dex2oat.profdata",
        }
  2. Dodaj do pliku libart.so tę właściwość pgo:
        pgo: {
            instrumentation: true,
            benchmarks: ["art_runtime", "dex2oat",],
            profile_file: "libart.profdata",
        }
  3. Utwórz kompilacje z instrumentacją na potrzeby testów porównawczych dex2oatart_runtime za pomocą:
        make ANDROID_PGO_INSTRUMENT=dex2oat
        make ANDROID_PGO_INSTRUMENT=art_runtime
  4. Możesz też utworzyć pojedynczą wersję z wszystkimi zinstrumentowanymi bibliotekami za pomocą:

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

    Drugie polecenie tworzy wszystkie moduły z włączonym PGO na potrzeby profilowania.

  5. Uruchom testy porównawcze z użyciem dex2oatart_runtime, aby uzyskać:
    • 3 pliki .profrawdex2oat (dex2oat_exe.profdata, dex2oat_libart-compiler.profdatadexeoat_libart.profdata), zidentyfikowane za pomocą metody opisanej w artykule Przetwarzanie plików profilu LLVM.
    • pojedynczy art_runtime_libart.profdata,
  6. Utwórz wspólny plik profdata dla pliku wykonywalnego dex2oatlibart-compiler.so za pomocą:
    llvm-profdata merge -output=dex2oat.profdata \
        dex2oat_exe.profdata dex2oat_libart-compiler.profdata
  7. Uzyskaj profil libart.so, zliczając profile z tych 2 benchmarków:
    llvm-profdata merge -output=libart.profdata \
        dex2oat_libart.profdata art_runtime_libart.profdata

    Liczby libart.so z 2 profili mogą się różnić, ponieważ punkty odniesienia różnią się liczbą przypadków testowych i czasem ich trwania. W takim przypadku możesz użyć złączenia ważonego:

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

    Powyższe polecenie przypisuje profilowi z adresu dex2oat podwójną wagę. Rzeczywista waga powinna być określana na podstawie wiedzy w danej dziedzinie lub eksperymentowania.

  8. Sprawdź pliki profilu dex2oat.profdatalibart.profdata w folderze toolchain/pgo-profiles, aby użyć ich podczas kompilacji.