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:
W panelu Project (Projekt) w Android Studio kliknij prawym przyciskiem myszy projekt lub moduł i wybierz New > Module (Nowy > Moduł).
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.
Kliknij Zakończ.
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:
- Przeprowadź synchronizację Gradle.
- Otwórz panel Warianty kompilacji.
- Wybierz wariant testu porównawczego zarówno aplikacji, jak i modułu Macrobenchmark.
(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:
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:
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.
Możesz też uruchomić wszystkie testy porównawcze w module Gradle z poziomu wiersza poleceń, wykonując polecenie connectedCheck:
./gradlew :macrobenchmark:connectedCheckMożesz uruchomić pojedynczy test, wykonując to polecenie:
./gradlew :macrobenchmark:connectedCheck -P android.testInstrumentationRunnerArguments.class=com.example.macrobenchmark.startup.SampleStartupBenchmark#startupInformacje 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:
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:
Po wczytaniu pliku logu czasu Studio wyświetla wyniki w narzędziu CPU Profiler:
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.
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy język JavaScript jest wyłączony.
- Przechwytywanie danych makrotestu porównawczego
- Tworzenie profili podstawowych {:#creating-profile-rules}
- Automatyzacja pomiarów za pomocą biblioteki Macrobenchmark {:#measuring-optimization}