Utwórz makroporównawczy

Używaj biblioteki Macrobenchmark do testowania większych przypadków użycia aplikacji, w tym jej uruchamiania i złożonych operacji na interfejsie, takich jak przewijanie LazyColumn czy uruchamianie animacji. Jeśli chcesz przetestować mniejsze obszary kodu, zapoznaj się z artykułem Mikrotest porównawczy. Na tej stronie dowiesz się, jak skonfigurować bibliotekę Macrobenchmark.

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

Używaj biblioteki Macrobenchmark w środowisku trybu ciągłej integracji (CI), jak opisano w artykule Test porównawczy w trybie ciągłej integracji.

Do generowania profili podstawowych możesz używać biblioteki Macrobenchmark. Najpierw skonfiguruj bibliotekę Macrobenchmark, a następnie utwórz profil podstawowy.

Macrobenchmark to zalecane narzędzie do testowania interfejsów utworzonych za pomocą Jetpack Compose. Aby dowiedzieć się, jak pisać testy UI Automator, które współdziałają z węzłami Compose, przeczytaj artykuł o interoperacyjności.

Konfigurowanie projektu

Zalecamy korzystanie z Macrobenchmarka z najnowszą wersją Android Studio.

Konfigurowanie modułu Macrobenchmark

Testy Macrobenchmark wymagają modułu com.android.test, który jest oddzielony od kodu aplikacji i odpowiada za przeprowadzanie testów mierzących wydajność aplikacji.

W Android Studio dostępny jest szablon, który upraszcza konfigurację modułu Macrobenchmark. Szablon modułu testu porównawczego 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, wykonaj te czynności:

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

  2. W panelu Szablony wybierz Analiza porównawcza. Możesz dostosować aplikację docelową, czyli aplikację, która ma zostać poddana testom porównawczym, a także nazwę pakietu i modułu nowego modułu Macrobenchmark.

  3. Kliknij Zakończ.

Szablon modułu testu porównawczego.
Rysunek 1. Szablon modułu testu porównawczego.

Konfigurowanie aplikacji

Aby przeprowadzić test porównawczy aplikacji (nazywanej obiektem docelowym makrotestu porównawczego), aplikacja musi być profileable, co umożliwia odczytywanie szczegółowych informacji o śladach 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órej biblioteka Macrobenchmark potrzebuje do włączenia przechwytywania i resetowania profilu oraz czyszczenia pamięci podręcznej obiektów cieniujących.

Skonfiguruj aplikację poddawaną testom porównawczym tak, aby była jak najbardziej zbliżona do wersji do publikacji lub wersji produkcyjnej. Skonfiguruj ją jako aplikację bez możliwości debugowania i najlepiej z włączoną minifikacją, która poprawia wydajność. Zwykle robi się to przez utworzenie kopii wariantu wersji, która działa tak samo, ale jest podpisana lokalnie kluczami debugowania. Możesz też użyć initWith, aby Gradle 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 zarówno skompilowanie, jak 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 testu porównawczego.
Rysunek 2. Wybierz wariant referencyjny.

(Opcjonalnie) Konfigurowanie aplikacji wielomodułowej

Jeśli aplikacja ma więcej niż 1 moduł Gradle, upewnij się, że skrypty kompilacji wiedzą, który wariant kompilacji skompilować. Dodaj właściwość matchingFallbacks do typu kompilacji benchmark w modułach :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 wielomodułowego z wybranymi typami kompilacji wersji i testu porównawczego.
Rysunek 3. Warianty testu porównawczego dla projektu wielomodułowego z wybranymi typami kompilacji wersji i testu porównawczego.

Więcej informacji znajdziesz w artykule Rozwiązywanie problemów z błędami kompilacji związanymi z dopasowywaniem wariantów.

(Opcjonalnie) Konfigurowanie wariantów usługi

Jeśli w aplikacji masz skonfigurowanych kilka wariantów usługi, skonfiguruj moduł :macrobenchmark, aby wiedział, który wariant usługi ma skompilować i przetestować.

Przykłady na tej stronie używają 2 wariantów usługi 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 tego, który jest powodowany 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 opisano sposoby konfigurowania testów porównawczych z wieloma wariantami usługi.

Użyj missingDimensionStrategy

Określenie missingDimensionStrategy w defaultConfig modułu :macrobenchmark informuje system kompilacji, że ma użyć zastępczego wymiaru rodzajów. Określ, których wymiarów chcesz użyć, jeśli nie znajdziesz ich w module. W poniższym przykładzie jako domyślny wymiar używany jest wariant production:

Kotlin

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

Dynamiczny

defaultConfig {
    missingDimensionStrategy "environment", "production"
}

Dzięki temu moduł :macrobenchmark może kompilować i testować tylko określony wariant usługi, co jest przydatne, jeśli wiesz, że tylko jeden wariant usługi ma odpowiednią konfigurację do testów porównawczych.

Definiowanie wariantów usługi w module :macrobenchmark

Jeśli chcesz utworzyć i przetestować inne warianty usługi, zdefiniuj je w module :macrobenchmark. Określ go podobnie jak w module :app, ale przypisz tylko productFlavors 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 testu porównawczego dla projektu z wariantami usługi, w których wybrano opcje productionBenchmark i release.
Rysunek 4. Warianty testu porównawczego dla projektu z wariantami usługi, w których wybrano „productionBenchmark” i „release”.

Więcej informacji znajdziesz w artykule Rozwiązywanie problemów z błędami kompilacji związanymi z dopasowywaniem wariantów.

Tworzenie klasy Macrobenchmark

Testy porównawcze są przeprowadzane za pomocą interfejsu API reguły JUnit4 MacrobenchmarkRule w bibliotece Macrobenchmark. Zawiera metodę measureRepeated, która pozwala określić różne warunki uruchamiania i testowania aplikacji docelowej.

Musisz podać co najmniej packageName aplikacji docelowej, metrics, które chcesz mierzyć, oraz liczbę iterations, które musi wykonać test porównawczy.

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.

Analiza porównawcza interakcji z interfejsem Compose

Poprzedni przykład dotyczył pomiaru uruchamiania aplikacji, ale często będziesz chcieć mierzyć wydajność interfejsu, np. pomijanie klatek (zacinanie się) podczas przewijania.

Aby przeprowadzić test porównawczy aplikacji 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, korzystając z działania na marginesie obok klasy lub metody testowej, jak pokazano na rysunku 5.

Uruchom Macrobenchmark, klikając działanie w rynience obok klasy testowej.
Rysunek 5. Uruchom Macrobenchmark, klikając działanie w rynnie obok klasy testowej.

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

./gradlew :macrobenchmark:connectedCheck

Możesz uruchomić pojedynczy test, wykonując to polecenie:

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

Informacje o tym, jak uruchamiać i monitorować testy porównawcze w trybie ciągłej integracji, znajdziesz w artykule Test porównawczy 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 są generowane do użycia w CI w pliku JSON. Każda mierzona iteracja rejestruje osobne śledzenie systemu. Możesz otworzyć te wyniki logów, klikając linki w panelu Wyniki testu, jak pokazano na rysunku 6:

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

Po wczytaniu logu Android Studio poprosi Cię o wybranie procesu do przeanalizowania. Wybór jest wstępnie wypełniony procesem aplikacji docelowej, jak pokazano na rysunku 7:

Wybór procesu logu czasu w Studio.
Rysunek 7. Wybór procesu logu czasu w Studio.

Po wczytaniu pliku logu czasu Studio wyświetla wyniki w narzędziu CPU Profiler:

Ślad studyjny.
Rysunek 8. Studio.

Raporty JSON i wszystkie logi czasu 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/

Uzyskiwanie ręcznego dostępu do plików logów czasu

Jeśli chcesz użyć narzędzia Perfetto do przeanalizowania pliku logu, musisz wykonać dodatkowe czynności. Perfetto umożliwia sprawdzenie wszystkich procesów zachodzących na urządzeniu podczas śledzenia, podczas gdy CPU Profiler w Android Studio ogranicza kontrolę do jednego procesu.

Jeśli wywołasz testy z Android Studio lub z wiersza poleceń Gradle, pliki logów czasu zostaną automatycznie skopiowane z urządzenia na hosta. Są one zapisywane na maszynie hosta 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 plik logu czasu znajdzie się w systemie hosta, możesz go otworzyć w Android Studio, wybierając w menu Plik > Otwórz. Pokazuje widok narzędzia do profilowania, który został przedstawiony w poprzedniej sekcji.

Błędy konfiguracji

Jeśli aplikacja jest nieprawidłowo skonfigurowana (z możliwością debugowania lub bez możliwości profilowania), Macrobenchmark zwraca błąd, zamiast zgłaszać nieprawidłowy lub niepełny pomiar. Możesz je pominąć, używając argumentu androidx.benchmark.suppressErrors.

Macrobenchmark zwraca też błędy podczas próby pomiaru emulatora lub na urządzeniu z niskim poziomem baterii, co może mieć wpływ na dostępność rdzeni i taktowanie.

Dostosowywanie testów porównawczych

Funkcja measureRepeated przyjmuje różne parametry, które wpływają na to, jakie dane zbiera biblioteka, jak aplikacja jest uruchamiana i kompilowana oraz ile iteracji wykonuje test porównawczy.

Zapisywanie danych

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

Więcej informacji o wskaźnikach znajdziesz w artykule Przechwytywanie wskaźników Macrobenchmark.

Ulepszanie danych śledzenia za pomocą zdarzeń niestandardowych

Warto wyposażyć aplikację w niestandardowe zdarzenia śledzenia, które są widoczne w pozostałej części raportu śledzenia i mogą pomóc w wykrywaniu problemów specyficznych dla aplikacji. Więcej informacji o tworzeniu niestandardowych zdarzeń śledzenia znajdziesz w artykule Definiowanie zdarzeń niestandardowych.

CompilationMode

Testy porównawcze makro mogą określać CompilationMode, który definiuje, jaka część aplikacji musi zostać wstępnie skompilowana z kodu bajtowego DEX (format kodu bajtowego w pliku APK) do kodu maszynowego (podobnego do wstępnie skompilowanego kodu C++).

Domyślnie testy Macrobenchmark są uruchamiane z CompilationMode.DEFAULT, co powoduje instalację profilu Baseline (jeśli jest dostępny) na Androidzie 7 (poziom API 24) i nowszych. Jeśli używasz Androida 6 (interfejs API na poziomie 23) lub starszego, tryb kompilacji w pełni kompiluje plik APK jako domyślne działanie systemu.

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

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

Ta funkcja jest oparta na poleceniach kompilacji ART. Przed rozpoczęciem każdego testu porównawczego dane profilu są czyszczone, aby zapobiec zakłóceniom między testami.

StartupMode

Aby uruchomić aktywność, możesz przekazać wstępnie zdefiniowany tryb uruchamiania: COLD, WARM lub HOT. Ten parametr zmienia sposób uruchamiania aktywności 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 przykładzie Macrobenchmark w repozytorium na GitHubie.

Prześlij opinię

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