Skorzystaj z optymalizacji opartej na profilu

System kompilacji Androida dla Androida 13 i starszych wersji obsługuje optymalizację opartą na profilach (PGO) firmy Clang w natywnych modułach Androida, które mają reguły kompilacji planów . Na tej stronie opisano Clang PGO, sposób ciągłego generowania i aktualizowania profili używanych w PGO oraz sposób integracji PGO z systemem kompilacji (z przypadkiem użycia).

Uwaga: ten dokument opisuje wykorzystanie PGO na platformie Android. Aby dowiedzieć się więcej o korzystaniu z PGO z aplikacji na Androida, odwiedź tę stronę .

O Clangu PGO

Clang może przeprowadzić optymalizację opartą na profilach, używając dwóch typów profili:

  • Profile oparte na oprzyrządowaniu są generowane na podstawie oprzyrządowanego programu docelowego. Profile te są szczegółowe i wymagają dużego narzutu w czasie działania.
  • Profile oparte na próbkowaniu są zwykle tworzone przez liczniki sprzętowe próbkowania. Narzucają niski narzut w czasie wykonywania i można je gromadzić bez żadnego oprzyrządowania lub modyfikacji pliku binarnego. Są mniej szczegółowe niż profile oparte na oprzyrządowaniu.

Wszystkie profile powinny być generowane na podstawie reprezentatywnego obciążenia, które sprawdza typowe zachowanie aplikacji. Podczas gdy Clang obsługuje zarówno oparte na AST ( -fprofile-instr-generate ), jak i oparte na LLVM IR ( -fprofile-generate) , Android obsługuje tylko oparte na LLVM IR dla PGO opartego na oprzyrządowaniu.

Do zbudowania kolekcji profili potrzebne są następujące flagi:

  • -fprofile-generate dla oprzyrządowania opartego na podczerwieni. Dzięki tej opcji backend wykorzystuje podejście oparte na minimalnym ważonym drzewie rozpinającym, aby zmniejszyć liczbę punktów oprzyrządowania i zoptymalizować ich rozmieszczenie do krawędzi o małej wadze (użyj tej opcji również w kroku łączenia). Sterownik Clang automatycznie przekazuje środowisko wykonawcze profilowania ( libclang_rt.profile- arch -android.a ) do konsolidatora. Biblioteka ta zawiera procedury zapisywania profili na dysku po wyjściu programu.
  • -gline-tables-only do zbierania profili w oparciu o próbkowanie w celu wygenerowania minimalnej ilości informacji debugowania.

Profilu można używać dla PGO, używając opcji -fprofile-use= pathname lub -fprofile-sample-use= pathname odpowiednio dla profili opartych na instrumentacji i na próbkowaniu.

Uwaga: jeśli w kodzie zostaną wprowadzone zmiany, Clang nie może już używać danych profilu, generuje ostrzeżenie -Wprofile-instr-out-of-date .

Użyj PGO

Korzystanie z PGO obejmuje następujące kroki:

  1. Zbuduj bibliotekę/plik wykonywalny za pomocą instrumentacji, przekazując -fprofile-generate do kompilatora i konsolidatora.
  2. Zbieraj profile, uruchamiając reprezentatywne obciążenie w oprzyrządowanym pliku binarnym.
  3. Przetwórz profile za pomocą narzędzia llvm-profdata (szczegółowe informacje można znaleźć w sekcji Obsługa plików profili LLVM ).
  4. Użyj profili, aby zastosować PGO, przekazując -fprofile-use=<>.profdata do kompilatora i linkera.

W przypadku PGO w systemie Android profile należy gromadzić w trybie offline i zapisywać wraz z kodem, aby zapewnić powtarzalność kompilacji. Profili można używać w miarę ewolucji kodu, ale należy je okresowo regenerować (lub gdy Clang ostrzeże, że profile są nieaktualne).

Zbieraj profile

Clang może używać profili zebranych podczas uruchamiania testów porównawczych przy użyciu oprzyrządowanej kompilacji biblioteki lub próbkowania liczników sprzętu podczas uruchamiania testu porównawczego. Obecnie system Android nie obsługuje zbierania profili na podstawie próbkowania, dlatego należy zbierać profile przy użyciu kompilacji instrumentowanej:

  1. Zidentyfikuj punkt odniesienia i zbiór bibliotek wspólnie wykorzystywanych przez ten punkt odniesienia.
  2. Dodaj właściwości pgo do benchmarku i bibliotek (szczegóły poniżej).
  3. Utwórz kompilację systemu Android z oprzyrządowaną kopią tych bibliotek, używając:
    make ANDROID_PGO_INSTRUMENT=benchmark

benchmark to symbol zastępczy identyfikujący kolekcję bibliotek oprzyrządowanych podczas kompilacji. Rzeczywiste reprezentatywne dane wejściowe (i prawdopodobnie inny plik wykonywalny, który łączy się z biblioteką podlegającą testowi porównawczemu) nie są specyficzne dla PGO i wykraczają poza zakres tego dokumentu.

  1. Flashuj lub synchronizuj oprzyrządowaną kompilację na urządzeniu.
  2. Uruchom test porównawczy, aby zebrać profile.
  3. Użyj narzędzia llvm-profdata (omówione poniżej), aby przetworzyć profile i przygotować je do włączenia do drzewa źródłowego.

Użyj profili podczas kompilacji

Sprawdź profile w toolchain/pgo-profiles w drzewie Androida. Nazwa powinna odpowiadać nazwie określonej we właściwości podrzędnej profile_file właściwości pgo biblioteki. System kompilacji automatycznie przekazuje plik profilu do Clang podczas budowania biblioteki. Zmienną środowiskową ANDROID_PGO_DISABLE_PROFILE_USE można ustawić na true , aby tymczasowo wyłączyć PGO i zmierzyć korzyści związane z wydajnością.

Aby określić dodatkowe katalogi profili specyficzne dla produktu, dołącz je do zmiennej make PGO_ADDITIONAL_PROFILE_DIRECTORIES w pliku BoardConfig.mk . Jeśli określono dodatkowe ścieżki, profile w tych ścieżkach zastępują te w toolchain/pgo-profiles .

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

Włącz PGO w plikach Android.bp

Aby włączyć PGO w plikach Android.bp dla modułów natywnych, wystarczy określić właściwość pgo . Ta właściwość ma następujące właściwości podrzędne:

Nieruchomość Opis
instrumentation Ustaw na true dla PGO przy użyciu oprzyrządowania. Wartość domyślna to false .
sampling Ustaw na true dla PGO przy użyciu próbkowania. Wartość domyślna to false .
benchmarks Lista ciągów. Moduł ten jest zbudowany do profilowania, jeśli w opcji kompilacji ANDROID_PGO_INSTRUMENT określono dowolny benchmark na liście.
profile_file Plik profilu (względem toolchain/pgo-profile ) do użycia z PGO. Kompilacja ostrzega, że ​​ten plik nie istnieje, dodając go 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 profile nie powinny być używane podczas kompilacji. Można go użyć podczas ładowania początkowego, aby włączyć gromadzenie profili lub tymczasowo wyłączyć PGO. Wartość domyślna to true .
cflags Lista dodatkowych flag do użycia podczas kompilacji instrumentalnej.

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 testy porównawcze benchmark1 i benchmark2 zachowują się reprezentatywnie dla bibliotek libstatic1 , libstatic2 lub libshared1 , właściwość pgo tych bibliotek może również obejmować testy porównawcze. Moduł defaults w Android.bp może zawierać wspólną specyfikację pgo dla zestawu bibliotek, aby uniknąć powtarzania tych samych reguł kompilacji dla kilku modułów.

Aby wybrać różne pliki profili lub selektywnie wyłączyć PGO dla architektury, określ właściwości profile_file , enable_profile_use i cflags dla każdej architektury. Przykład (z pogrubioną czcionką docelową architektury):

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 rozwiązać odniesienia do biblioteki środowiska uruchomieniowego profilowania podczas profilowania opartego na instrumentacji, przekaż flagę kompilacji -fprofile-generate do konsolidatora. Biblioteki statyczne oprzyrządowane za pomocą PGO, wszystkie biblioteki współdzielone i wszystkie pliki binarne, które bezpośrednio zależą od biblioteki statycznej, muszą również zostać oprzyrządowane dla PGO. Jednak takie współdzielone biblioteki lub pliki wykonywalne nie muszą używać profili PGO, a ich właściwość enable_profile_use można ustawić na false . Poza tym ograniczeniem możesz zastosować PGO do dowolnej biblioteki statycznej, biblioteki współdzielonej lub pliku wykonywalnego.

Obsługuj pliki profili LLVM

Wykonanie instrumentowanej biblioteki lub pliku wykonywalnego powoduje utworzenie pliku profilu o nazwie default_ unique_id _0.profraw w /data/local/tmp (gdzie unique_id to numeryczny skrót, który jest unikalny dla tej biblioteki). Jeśli ten plik już istnieje, środowisko wykonawcze profilowania scala nowy profil ze starym podczas zapisywania profili. Pamiętaj, że /data/local/tmp nie jest dostępny dla twórców aplikacji; zamiast tego powinni używać czegoś takiego jak /storage/emulated/0/Android/data/ packagename /files . Aby zmienić lokalizację pliku profilu, ustaw zmienną środowiskową LLVM_PROFILE_FILE w czasie wykonywania.

Następnie narzędzie llvm-profdata służy do konwersji pliku .profraw (i ewentualnie połączenia wielu plików .profraw ) do pliku .profdata :

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

profile.profdata można następnie zapisać w drzewie źródłowym w celu wykorzystania podczas kompilacji.

Jeśli podczas testu porównawczego zostanie załadowanych wiele oprzyrządowanych plików binarnych/bibliotek, każda biblioteka generuje oddzielny plik .profraw z oddzielnym unikalnym identyfikatorem. Zazwyczaj wszystkie te pliki można połączyć w jeden plik .profdata i wykorzystać do kompilacji PGO. W przypadku, gdy biblioteka jest wykorzystywana przez inny benchmark, należy ją zoptymalizować przy użyciu profili z obu benchmarków. 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 do poszczególnych bibliotek, przeszukaj wyniki show dla każdego Unique_id w poszukiwaniu nazwy funkcji, która jest unikalna dla danej biblioteki.

Studium przypadku: PGO dla ART

Studium przypadku przedstawia ART jako możliwy do odniesienia przykład; nie jest to jednak dokładny opis rzeczywistego zestawu bibliotek profilowanych pod kątem ART lub ich współzależności.

Kompilator dex2oat z wyprzedzeniem w ART zależy od libart-compiler.so , który z kolei zależy od libart.so . Środowisko wykonawcze ART jest zaimplementowane głównie w libart.so . Benchmarki dla kompilatora i środowiska wykonawczego będą inne:

Reper Biblioteki profilowane
dex2oat dex2oat (plik wykonywalny), libart-compiler.so , libart.so
art_runtime libart.so
  1. Dodaj następującą właściwość pgo do dex2oat , libart-compiler.so :
        pgo: {
            instrumentation: true,
            benchmarks: ["dex2oat",],
            profile_file: "dex2oat.profdata",
        }
  2. Dodaj następującą właściwość pgo do libart.so :
        pgo: {
            instrumentation: true,
            benchmarks: ["art_runtime", "dex2oat",],
            profile_file: "libart.profdata",
        }
  3. Twórz oprzyrządowane kompilacje dla testów porównawczych dex2oat i art_runtime , używając:
        make ANDROID_PGO_INSTRUMENT=dex2oat
        make ANDROID_PGO_INSTRUMENT=art_runtime
  4. Alternatywnie utwórz pojedynczą kompilację oprzyrządowaną ze wszystkimi bibliotekami oprzyrządowanymi przy użyciu:

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

    Drugie polecenie buduje wszystkie moduły obsługujące PGO do profilowania.

  5. Uruchom testy porównawcze, ćwicząc dex2oat i art_runtime , aby uzyskać:
    • Trzy pliki .profraw z dex2oat ( dex2oat_exe.profdata , dex2oat_libart-compiler.profdata i dexeoat_libart.profdata ) zidentyfikowane przy użyciu metody opisanej w sekcji Obsługa plików profili LLVM .
    • Pojedynczy art_runtime_libart.profdata .
  6. Utwórz wspólny plik profdata dla pliku wykonywalnego dex2oat i libart-compiler.so , używając:
    llvm-profdata merge -output=dex2oat.profdata \
        dex2oat_exe.profdata dex2oat_libart-compiler.profdata
  7. Uzyskaj profil dla libart.so łącząc profile z dwóch testów porównawczych:
    llvm-profdata merge -output=libart.profdata \
        dex2oat_libart.profdata art_runtime_libart.profdata

    Surowe dane dotyczące libart.so z obu profili mogą się różnić, ponieważ testy porównawcze różnią się liczbą przypadków testowych i czasem ich działania. W takim przypadku możesz użyć scalania 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 dwukrotnie większą wagę do profilu z dex2oat . Rzeczywistą wagę należy określić na podstawie wiedzy branżowej lub eksperymentów.

  8. Sprawdź pliki profili dex2oat.profdata i libart.profdata w toolchain/pgo-profiles aby można było ich użyć podczas kompilacji.