Utwórz makroporównawczy

Biblioteki Macrobenchmark używaj do testowania większych przypadków użycia aplikacji, w tym uruchamiania aplikacji i złożonych manipulacji interfejsem, takich jak przewijanie LazyColumn czy uruchamianie animacji. Jeśli chcesz przetestować mniejsze obszary kodu, zapoznaj się z artykułem Microbenchmark. Z tej strony dowiesz się, jak skonfigurować bibliotekę Macrobenchmark.

Biblioteka wyświetla wyniki testów porównawczych w konsoli Android Studio oraz w pliku JSON zawierającym więcej szczegółów. Udostępnia też pliki śledzenia, które możesz wczytać i przeanalizować w Android Studio.

Biblioteki Macrobenchmark używaj w środowisku ciągłej integracji (CI), zgodnie z opisem w artykule Testowanie w trybie ciągłej integracji.

Za pomocą biblioteki Macrobenchmark możesz generować profile podstawowe. Najpierw skonfiguruj bibliotekę Macrobenchmark, a potem możesz utworzyć profil podstawowy.

Biblioteka Macrobenchmark to zalecane narzędzie do testowania interfejsów utworzonych za pomocą Jetpack Compose. Aby dowiedzieć się, jak pisać testy UI Automator, które wchodzą w interakcję z węzłami Compose, przeczytaj artykuł Współdziałanie.

Konfigurowanie projektu

Zalecamy używanie biblioteki Macrobenchmark z najnowszą wersją Android Studio.

Konfigurowanie modułu Macrobenchmark

Testy porównawcze Macrobenchmark wymagają modułu com.android.test – oddzielnego od kodu aplikacji – który odpowiada za uruchamianie testów mierzących aplikację.

W Android Studio dostępny jest szablon, który upraszcza konfigurowanie modułu Macrobenchmark. Szablon modułu testów porównawczych automatycznie tworzy w projekcie moduł do pomiaru aplikacji utworzonej przez moduł aplikacji, w tym przykładowy test porównawczy uruchamiania.

Aby utworzyć nowy moduł za pomocą szablonu modułu:

  1. W panelu Projekt w Android Studio kliknij prawym przyciskiem myszy projekt lub moduł i wybierz Nowy > Moduł.

  2. W panelu Szablony wybierz Test porównawczy. Możesz dostosować aplikację docelową – czyli aplikację, która ma zostać przetestowana – oraz nazwę pakietu i modułu nowego modułu Macrobenchmark.

  3. Kliknij Zakończ.

Szablon modułu testu porównawczego.
Rysunek 1. Szablon modułu testów porównawczych.

Konfigurowanie aplikacji

Aby przetestować aplikację – zwaną celem testu porównawczego Macrobenchmark – musi ona być profileable, co umożliwia odczytywanie szczegółowych informacji o śledzeniu bez wpływu na wydajność. Kreator modułu automatycznie dodaje tag <profileable> do pliku AndroidManifest.xml aplikacji.

Upewnij się, że aplikacja docelowa zawiera ProfilerInstaller w wersji 1.3 lub nowszej, która jest potrzebna bibliotece Macrobenchmark do włączania przechwytywania i resetowania profilu oraz czyszczenia pamięci podręcznej shaderów.

Skonfiguruj testowaną aplikację tak, aby była jak najbardziej zbliżona do wersji produkcyjnej. Ustaw ją jako niepodlegającą debugowaniu i najlepiej z włączoną minimalizacją, co poprawia wydajność. Zwykle robisz to, tworząc kopię wariantu wersji, która działa tak samo, ale jest podpisywana lokalnie za pomocą kluczy debugowania. Możesz też użyć initWith, aby poinstruować Gradle, aby zrobił to za Ciebie:

Kotlin

buildTypes {
    getByName("release") {
        isMinifyEnabled = true
        isShrinkResources = true
        proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
    }

    create("benchmark") {
        initWith(getByName("release"))
        signingConfig = signingConfigs.getByName("debug")
    }
}

Dynamiczny

buildTypes {
    release {
        isMinifyEnabled = true
        isShrinkResources = true
        proguardFiles(
            getDefaultProguardFile("proguard-android-optimize.txt"),
            "keep-rules.pro"
        )
        // In real app, this would use its own release keystore
        signingConfig = signingConfigs.getByName("debug")
        baselineProfile.automaticGenerationDuringBuild = true
    }
}

Aby mieć pewność, że uruchomienie testu porównawczego spowoduje skompilowanie i przetestowanie prawidłowego wariantu aplikacji, jak pokazano na rysunku 2, wykonaj te czynności:

  1. Przeprowadź synchronizację Gradle.
  2. Otwórz panel Warianty kompilacji.
  3. Wybierz wariant testu porównawczego zarówno aplikacji, jak i modułu Macrobenchmark.
Wybierz wariant wartości referencyjnej.
Rysunek 2. Wybierz wariant testu porównawczego.

(Opcjonalnie) Konfigurowanie aplikacji wielomodułowej

Jeśli Twoja aplikacja ma więcej niż 1 moduł Gradle, upewnij się, że skrypty kompilacji wiedzą, który wariant kompilacji ma zostać skompilowany. Dodaj właściwość matchingFallbacks do typu kompilacji benchmark modułów :macrobenchmark i :app. Pozostałe moduły Gradle mogą mieć taką samą konfigurację jak wcześniej.

Kotlin

create("benchmark") {
    initWith(getByName("release"))
    signingConfig = signingConfigs.getByName("debug")

    matchingFallbacks += listOf("release")
 }

Dynamiczny

benchmark {
    initWith buildTypes.release
    signingConfig signingConfigs.debug

    matchingFallbacks = ['release']
 }

Bez tego nowo dodany typ kompilacji benchmark spowoduje niepowodzenie kompilacji i wyświetlenie tego komunikatu o błędzie:

> Could not resolve project :shared.
     Required by:
         project :app
      > No matching variant of project :shared was found.
      ...

Podczas wybierania wariantów kompilacji w projekcie wybierz benchmark dla modułów :app i :macrobenchmark oraz release dla wszystkich innych modułów w aplikacji , jak pokazano na rysunku 3:

Warianty testu porównawczego dla projektu z wieloma modułami, w którym wybrano typy kompilacji wydania i testu porównawczego.
Rysunek 3. Warianty testów porównawczych dla projektu wielomodułowego z wybranymi typami kompilacji release i benchmark.

Więcej informacji znajdziesz w artykule Rozwiązywanie błędów kompilacji związanych z dopasowywaniem wariantów.

(Opcjonalnie) Konfigurowanie wariantów produktu

Jeśli w aplikacji masz skonfigurowanych kilka wariantów produktu, skonfiguruj moduł :macrobenchmark tak, aby wiedział, który wariant produktu ma zostać skompilowany i przetestowany.

Przykłady na tej stronie używają 2 wariantów produktu w module :app: demo i production, jak pokazano w tym fragmencie kodu:

Kotlin

flavorDimensions += "environment"
productFlavors {
    create("demo") {
        dimension = "environment"
        // ...
    }
    create("production") {
        dimension = "environment"
        // ...
    }
}

Dynamiczny

flavorDimensions 'environment'
productFlavors {
    demo {
        dimension 'environment'
        // ...
    }

    production {
        dimension 'environment'
        // ...
    }
}

Bez tej konfiguracji może wystąpić błąd kompilacji podobny do błędu spowodowanego przez wiele modułów Gradle:

Could not determine the dependencies of task ':macrobenchmark:connectedBenchmarkAndroidTest'.
> Could not determine the dependencies of null.
   > Could not resolve all task dependencies for configuration ':macrobenchmark:benchmarkTestedApks'.
      > Could not resolve project :app.
        Required by:
            project :macrobenchmark
         > The consumer was configured to find a runtime of a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'benchmark', attribute 'com.android.build.api.attributes.AgpVersionAttr' with value '7.3.0'. However we cannot choose between the following variants of project :app:
             -   demoBenchmarkRuntimeElements
             -   productionBenchmarkRuntimeElements
           All of them match the consumer attributes:
           ...

W 2 kolejnych sekcjach opisujemy, jak skonfigurować testy porównawcze z kilkoma wariantami produktu.

Używanie missingDimensionStrategy

Określenie missingDimensionStrategy w defaultConfig modułu :macrobenchmark informuje system kompilacji, aby powrócił do wymiaru wariantu. Określ, których wymiarów używać, jeśli nie znajdziesz ich w module. W tym przykładzie wariant production jest używany jako domyślny wymiar:

Kotlin

defaultConfig {
    missingDimensionStrategy("environment", "production")
}

Dynamiczny

defaultConfig {
    missingDimensionStrategy "environment", "production"
}

W ten sposób moduł :macrobenchmark może tylko kompilować i testować określony wariant produktu, co jest przydatne, jeśli wiesz, że tylko 1 wariant produktu ma odpowiednią konfigurację do testowania.

Definiowanie wariantów produktu w module :macrobenchmark

Jeśli chcesz kompilować i testować inne warianty produktu, zdefiniuj je w module :macrobenchmark. Określ je podobnie jak w module :app, ale przypisz productFlavors tylko do dimension. Nie są wymagane żadne inne ustawienia:

Kotlin

flavorDimensions += "environment"
productFlavors {
    create("demo") {
        dimension = "environment"
    }
    create("production") {
        dimension = "environment"
    }
}

Dynamiczny

flavorDimensions 'environment'
productFlavors {
    demo {
        dimension 'environment'
    }

    production {
        dimension 'environment'
    }
}

Po zdefiniowaniu i zsynchronizowaniu projektu wybierz odpowiedni wariant kompilacji w panelu Warianty kompilacji , jak pokazano na rysunku 4:

Warianty referencyjne projektu z wersjami produktu, w których wybrano opcje produkcji i premiery.
Rysunek 4. Warianty testów porównawczych dla projektu z wybranymi wariantami produktu „productionBenchmark” i „release”.

Więcej informacji znajdziesz w artykule Rozwiązywanie błędów kompilacji związanych z dopasowywaniem wariantów.

Tworzenie klasy Macrobenchmark

Testy porównawcze są przeprowadzane za pomocą MacrobenchmarkRule reguły JUnit4 interfejsu API w bibliotece Macrobenchmark. Zawiera on metodę measureRepeated która umożliwia określenie różnych warunków uruchamiania i testowania aplikacji docelowej.

Musisz co najmniej określić packageName aplikacji docelowej, metrics, które chcesz zmierzyć, oraz liczbę iterations, które mają zostać wykonane w teście porównawczym.

Kotlin

@LargeTest
@RunWith(AndroidJUnit4::class)
class SampleStartupBenchmark {
    @get:Rule
    val benchmarkRule = MacrobenchmarkRule()

    @Test
    fun startup() = benchmarkRule.measureRepeated(
        packageName = TARGET_PACKAGE,
        metrics = listOf(StartupTimingMetric()),
        iterations = DEFAULT_ITERATIONS,
    ) {
        // starts default launch activity
        uiAutomator { startApp(TARGET_PACKAGE) }
    }
}

Wszystkie opcje dostosowywania testu porównawczego znajdziesz w sekcji Dostosowywanie testów porównawczych.

Testowanie interakcji z interfejsem Compose

W poprzednim przykładzie mierzyliśmy uruchamianie aplikacji, ale często trzeba też mierzyć wydajność interfejsu, np. liczbę pominiętych klatek (jank) podczas przewijania.

Aby przetestować aplikację Jetpack Compose, np. zmierzyć FrameTimingMetric podczas przewijania LazyColumn, możesz użyć UI Automator do bezpośredniej interakcji z węzłami Compose, jak pokazano w tym fragmencie kodu.

@LargeTest
@RunWith(AndroidJUnit4::class)
class ComposeScrollBenchmark {
    @get:Rule
    val benchmarkRule = MacrobenchmarkRule()

    @Test
    fun scrollLazyColumn() = benchmarkRule.measureRepeated(
        packageName = "com.example.compose.app",
        metrics = listOf(FrameTimingMetric()),
        iterations = 5,
        startupMode = StartupMode.WARM,
        setupBlock = { pressHome() }
    ) {
        startActivityAndWait()

        // Find the Compose node using the testTag defined in your app
        val lazyColumn = device.findObject(By.res("my_lazy_column"))

        // Simulate a scroll gesture to measure FrameTimingMetric
        lazyColumn.setGestureMargin(device.displayWidth / 5)
        lazyColumn.fling(Direction.DOWN)
    }
}

Uruchamianie testu porównawczego

Uruchom test w Android Studio, aby zmierzyć wydajność aplikacji na urządzeniu. Testy porównawcze możesz uruchamiać tak samo jak inne testy @Test, używając działania w rynnie obok klasy lub metody testowej, jak pokazano na rysunku 5.

Uruchom Macrobenchmark z działaniem w rowie obok klasy testu.
Rysunek 5. Uruchamianie testu porównawczego Macrobenchmark za pomocą działania w rynnie obok klasy testowej.

Możesz też uruchomić wszystkie testy porównawcze w module Gradle z wiersza poleceń, wykonując polecenie connectedCheck:

./gradlew :macrobenchmark:connectedCheck

Aby uruchomić pojedynczy test, wykonaj te czynności:

./gradlew :macrobenchmark:connectedCheck -P android.testInstrumentationRunnerArguments.class=com.example.macrobenchmark.startup.SampleStartupBenchmark#startup

Informacje o tym, jak uruchamiać testy porównawcze w trybie ciągłej integracji i je monitorować, znajdziesz w artykule Testowanie w trybie ciągłej integracji.

Wyniki testu porównawczego

Po pomyślnym uruchomieniu testu porównawczego dane są wyświetlane bezpośrednio w Android Studio i zapisywane w pliku JSON do użycia w trybie ciągłej integracji. Każda zmierzona iteracja rejestruje oddzielne śledzenie systemu. Wyniki śledzenia możesz otworzyć, klikając linki w panelu Wyniki testu , jak pokazano na rysunku 6:

Wyniki uruchamiania testu porównawczego makro.
Rysunek 6. Wyniki testu porównawczego uruchamiania Macrobenchmark.

Po wczytaniu śledzenia Android Studio wyświetli prośbę o wybranie procesu do analizy. Wybór jest wstępnie wypełniony procesem aplikacji docelowej, jak pokazano na rysunku 7:

Wybór procesu śledzenia w Studio.
Rysunek 7. Wybór procesu śledzenia w Studio.

Po wczytaniu pliku śledzenia Studio wyświetli wyniki w narzędziu do profilowania procesora:

Śledzenie w Studio.
Rysunek 8. Śledzenie w Studio.

Raporty JSON i wszystkie ślady profilowania są też automatycznie kopiowane z urządzenia na hosta. Są one zapisywane na hoście w tej lokalizacji:

project_root/module/build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/device_id/

Ręczny dostęp do plików śledzenia

Jeśli chcesz przeanalizować plik śledzenia za pomocą narzędzia Perfetto, musisz wykonać dodatkowe czynności. Perfetto umożliwia sprawdzenie wszystkich procesów zachodzących na urządzeniu podczas śledzenia, a narzędzie do profilowania procesora w Android Studio ogranicza sprawdzanie do jednego procesu.

Jeśli wywołasz testy z Android Studio lub z wiersza poleceń Gradle, pliki śledzenia zostaną automatycznie skopiowane z urządzenia na hosta. Są one zapisywane na hoście w tej lokalizacji:

project_root/module/build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/device_id/TrivialStartupBenchmark_startup[mode=COLD]_iter002.perfetto-trace

Gdy masz plik śledzenia w systemie hosta, możesz go otworzyć w Android Studio, wybierając w menu Plik > Otwórz. Spowoduje to wyświetlenie widoku narzędzia do profilowania pokazanego w poprzedniej sekcji.

Błędy konfiguracji

Jeśli aplikacja jest nieprawidłowo skonfigurowana – można ją debugować lub nie można jej profilować – Macrobenchmark zwraca błąd zamiast nieprawidłowego lub niepełnego pomiaru. Te błędy możesz pominąć za pomocą androidx.benchmark.suppressErrors argumentu.

Macrobenchmark zwraca też błędy podczas próby pomiaru na emulatorze lub na urządzeniu z niskim poziomem naładowania baterii, co może wpłynąć na dostępność rdzeni i szybkość zegara.

Dostosowywanie testów porównawczych

Funkcja measureRepeated akceptuje różne parametry, które wpływają na to, jakie dane zbiera biblioteka, jak aplikacja jest uruchamiana i kompilowana oraz ile iteracji ma zostać wykonanych w teście porównawczym.

Przechwytywanie danych

Dane to główny typ informacji wyodrębnianych z testów porównawczych. Dostępne są te dane:

Więcej informacji o danych znajdziesz w artykule Przechwytywanie danych Macrobenchmark.

Ulepszanie danych śledzenia za pomocą zdarzeń niestandardowych

Może się przydać instrumentowanie aplikacji za pomocą niestandardowych zdarzeń śledzenia, które są widoczne w pozostałej części raportu śledzenia i mogą pomóc w wskazaniu problemów specyficznych dla Twojej aplikacji. Więcej informacji o tworzeniu niestandardowych zdarzeń śledzenia znajdziesz w artykule Definiowanie zdarzeń niestandardowych.

CompilationMode

Testy porównawcze Macrobenchmark mogą określać CompilationMode, który definiuje, ile aplikacji musi zostać wstępnie skompilowane z kodu bajtowego DEX (format kodu bajtowego w APK) do kodu maszynowego (podobnie jak wstępnie skompilowany kod C++).

Domyślnie testy porównawcze Macrobenchmark są uruchamiane w trybie CompilationMode.DEFAULT, który instaluje profil podstawowy – jeśli jest dostępny – na Androidzie 7 (poziom API 24) i nowszym. Jeśli używasz Androida 6 (poziom API 23) lub starszego, tryb kompilacji w pełni kompiluje APK jako domyślne działanie systemu.

Możesz zainstalować profil podstawowy, jeśli aplikacja docelowa zawiera zarówno profil podstawowy , jak i bibliotekę ProfileInstaller.

Na Androidzie 7 i nowszym możesz dostosować CompilationMode, aby wpływać na ilość wstępnej kompilacji na urządzeniu i naśladować różne poziomy kompilacji AOT lub buforowania JIT. Zobacz CompilationMode.Full, CompilationMode.Partial, CompilationMode.None i CompilationMode.Ignore.

Ta funkcja jest oparta na poleceniach kompilacji ART. Każdy test porównawczy przed rozpoczęciem czyści dane profilu, aby zapobiec zakłóceniom między testami.

StartupMode

Aby uruchomić działanie, możesz przekazać wstępnie zdefiniowany tryb uruchamiania: COLD, WARM, lub HOT. Ten parametr zmienia sposób uruchamiania działania i stan procesu na początku testu.

Więcej informacji o typach uruchamiania znajdziesz w artykule Czas uruchamiania aplikacji.

Przykłady

Przykładowy projekt jest dostępny w repozytorium GitHub w sekcji Macrobenchmark Sample.

Przesyłanie opinii

Aby zgłosić problemy lub przesłać prośby o dodanie funkcji do Jetpack Macrobenchmark, skorzystaj z publicznego narzędzia do śledzenia problemów.