Utwórz mikroporównanie

Aby dowiedzieć się, jak korzystać z biblioteki mikroporównawczych przez dodawanie zmian do kodu aplikacji, zapoznaj się z sekcją Krótkie wprowadzenie. Informacje o tym, jak przeprowadzić pełną konfigurację z bardziej skomplikowanymi zmianami w bazie kodu, znajdziesz w sekcji Pełna konfiguracja projektu.

Krótkie wprowadzenie

W tej sekcji dowiesz się, jak wypróbować testy porównawcze i przeprowadzić jednorazowe pomiary bez konieczności przenoszenia kodu do modułów. Aby uzyskać dokładne wyniki wydajności, wykonaj te czynności, aby wyłączyć debugowanie w aplikacji, więc zapisz to w lokalnej kopii roboczej bez wprowadzania zmian w systemie kontroli źródła.

Aby przeprowadzić jednorazową analizę porównawczą:

  1. Dodaj bibliotekę do pliku build.gradle lub build.gradle.kts modułu:

    Kotlin

    dependencies {
        implementation("androidx.benchmark:benchmark-junit4:1.2.3")
    }
    

    Odlotowy

    dependencies {
        implementation 'androidx.benchmark:benchmark-junit4:1.2.3'
    }
    

    Użyj zależności implementation zamiast androidTestImplementation. Jeśli używasz androidTestImplementation, testy porównawcze się nie udają, ponieważ plik manifest biblioteki nie został scalony z plikiem manifestu aplikacji.

  2. Zaktualizuj typ kompilacji debug tak, aby nie można było go debugować:

    Kotlin

    android {
        ...
        buildTypes {
            debug {
                isDebuggable = false
            }
        }
    }
    

    Odlotowy

    android {
        ...
        buildTypes {
            debug {
                debuggable false
            }
        }
    }
    
  3. Zmień testInstrumentationRunner na AndroidBenchmarkRunner:

    Kotlin

    android {
        ...
        defaultConfig {
            testInstrumentationRunner = "androidx.benchmark.junit4.AndroidBenchmarkRunner"
        }
    }
    

    Odlotowy

    android {
        ...
        defaultConfig {
            testInstrumentationRunner "androidx.benchmark.junit4.AndroidBenchmarkRunner"
        }
    }
    
  4. Dodaj instancję BenchmarkRule w pliku testowym w katalogu androidTest, aby dodać test porównawczy. Więcej informacji o tworzeniu testów porównawczych znajdziesz w artykule Tworzenie klasy mikroporównawczej.

    Ten fragment kodu pokazuje, jak dodać test porównawczy do testu z instrumentacją:

    Kotlin

    @RunWith(AndroidJUnit4::class)
    class SampleBenchmark {
        @get:Rule
        val benchmarkRule = BenchmarkRule()
    
        @Test
        fun benchmarkSomeWork() {
            benchmarkRule.measureRepeated {
                doSomeWork()
            }
        }
    }
    

    Java

    @RunWith(AndroidJUnit4.class)
    class SampleBenchmark {
        @Rule
        public BenchmarkRule benchmarkRule = new BenchmarkRule();
    
        @Test
        public void benchmarkSomeWork() {
                BenchmarkRuleKt.measureRepeated(
                    (Function1<BenchmarkRule.Scope, Unit>) scope -> doSomeWork()
                );
           }
        }
    }
    

Aby dowiedzieć się, jak napisać analizę porównawczą, przejdź do sekcji Tworzenie zajęć w ramach mikroporównania.

Pełna konfiguracja projektu

Aby skonfigurować regularne testy porównawcze zamiast jednorazowych testów porównawczych, utwórz osobny moduł dla testów porównawczych. Dzięki temu będziesz mieć pewność, że ich konfiguracja, np. ustawienie debuggable na false, będzie oddzielona od zwykłych testów.

Dzięki mikrotestom mikroporównawczym kod jest uruchamiany bezpośrednio, dlatego umieść go w osobnym module Gradle i ustaw zależność od tego modułu, jak pokazano na ilustracji 1.

struktura aplikacji
Rysunek 1. Struktura aplikacji z modułami :app, :microbenchmark i :benchmarkable Gradle, która umożliwia mikrotesty porównawcze w ramach modułu :benchmarkable.

Aby dodać nowy moduł Gradle, możesz użyć kreatora modułów w Android Studio. Kreator utworzy moduł skonfigurowany wstępnie do testów porównawczych, z dodanym katalogiem porównawczym, a debuggable ustawionym na false.

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

  2. W panelu Szablony kliknij Analiza porównawcza.

  3. Jako typ modułu analizy porównawczej wybierz Mikroporównanie.

  4. Jako nazwę modułu wpisz „microbenchmark”.

  5. Kliknij Zakończ.

Konfigurowanie nowego modułu biblioteki
Rysunek 2. Dodaj nowy moduł Gradle w Android Studio Bumblebee.

Po utworzeniu modułu zmień jego plik build.gradle lub build.gradle.kts i dodaj androidTestImplementation do modułu zawierającego kod do analizy porównawczej:

Kotlin

dependencies {
    // The module name might be different.
    androidTestImplementation(project(":benchmarkable"))
}

Odlotowy

dependencies {
    // The module name might be different.
    androidTestImplementation project(':benchmarkable')
}

Tworzenie zajęć mikroporównawczych

Testy porównawcze to standardowe testy z instrumentacją. Aby utworzyć analizę porównawczą, użyj klasy BenchmarkRule dostępnej w bibliotece. Aby porównać aktywności, użyj ActivityScenario lub ActivityScenarioRule. Aby porównać kod interfejsu, użyj @UiThreadTest.

Ten kod pokazuje przykładową analizę porównawczą:

Kotlin

@RunWith(AndroidJUnit4::class)
class SampleBenchmark {
    @get:Rule
    val benchmarkRule = BenchmarkRule()

    @Test
    fun benchmarkSomeWork() {
        benchmarkRule.measureRepeated {
            doSomeWork()
        }
    }
}
    

Java

@RunWith(AndroidJUnit4.class)
class SampleBenchmark {
    @Rule
    public BenchmarkRule benchmarkRule = new BenchmarkRule();

    @Test
    public void benchmarkSomeWork() {
        final BenchmarkState state = benchmarkRule.getState();
        while (state.keepRunning()) {
            doSomeWork();
        }
    }
}
    

Wyłącz czas na potrzeby konfiguracji

Możesz wyłączyć czas w przypadku fragmentów kodu, których nie chcesz mierzyć blokiem runWithTimingDisabled{}. Sekcje te zwykle zawierają jakiś kod, który należy uruchomić przy każdym powtórzeniu testu.

Kotlin

// using random with the same seed, so that it generates the same data every run
private val random = Random(0)

// create the array once and just copy it in benchmarks
private val unsorted = IntArray(10_000) { random.nextInt() }

@Test
fun benchmark_quickSort() {
    // ...
    benchmarkRule.measureRepeated {
        // copy the array with timing disabled to measure only the algorithm itself
        listToSort = runWithTimingDisabled { unsorted.copyOf() }

        // sort the array in place and measure how long it takes
        SortingAlgorithms.quickSort(listToSort)
    }

    // assert only once not to add overhead to the benchmarks
    assertTrue(listToSort.isSorted)
}
    

Java

private final int[] unsorted = new int[10000];

public SampleBenchmark() {
    // Use random with the same seed, so that it generates the same data every
    // run.
    Random random = new Random(0);

    // Create the array once and copy it in benchmarks.
    Arrays.setAll(unsorted, (index) -> random.nextInt());
}

@Test
public void benchmark_quickSort() {
    final BenchmarkState state = benchmarkRule.getState();

    int[] listToSort = new int[0];

    while (state.keepRunning()) {
        
        // Copy the array with timing disabled to measure only the algorithm
        // itself.
        state.pauseTiming();
        listToSort = Arrays.copyOf(unsorted, 10000);
        state.resumeTiming();
        
        // Sort the array in place and measure how long it takes.
        SortingAlgorithms.quickSort(listToSort);
    }

    // Assert only once, not to add overhead to the benchmarks.
    assertTrue(SortingAlgorithmsKt.isSorted(listToSort));
}
    

Spróbuj zminimalizować ilość pracy wykonywanej wewnątrz bloku measureRepeated i wewnątrz runWithTimingDisabled. Blok measureRepeated jest uruchamiany wiele razy i może mieć wpływ na ogólny czas potrzebny do przeprowadzenia analizy porównawczej. Jeśli chcesz zweryfikować niektóre wyniki testu porównawczego, możesz podać ostatni wynik, zamiast robić to przy każdym powtórzeniu testu.

Przeprowadź test porównawczy

W Android Studio przeprowadź test porównawczy tak samo jak w przypadku każdego innego elementu @Test, używając działania rynku obok klasy lub metody testowej, jak pokazano na rysunku 3.

Uruchom mikroporównanie
Rysunek 3. Uruchom test mikroporównawczy za pomocą działania rynku obok klasy testowej.

Możesz też z poziomu wiersza poleceń uruchomić connectedCheck, aby uruchomić wszystkie testy z określonego modułu Gradle:

./gradlew benchmark:connectedCheck

Albo pojedynczy test:

./gradlew benchmark:connectedCheck -P android.testInstrumentationRunnerArguments.class=com.example.benchmark.SampleBenchmark#benchmarkSomeWork

Wyniki testu porównawczego

Po udanym przeprowadzeniu mikrotestów dane są wyświetlane bezpośrednio w Android Studio, a pełny raport porównawczy z dodatkowymi danymi i informacjami o urządzeniach jest dostępny w formacie JSON.

Wyniki z mikroporównania
Rysunek 4. Wyniki mikroporównań.

Raporty JSON i wszystkie ślady profilowania są również 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/

Domyślnie raport JSON jest zapisywany na dysku na urządzeniu w zewnętrznym udostępnionym folderze multimediów pakietu APK, który zwykle znajduje się w folderze /storage/emulated/0/Android/media/**app_id**/**app_id**-benchmarkData.json.

Błędy konfiguracji

Biblioteka wykrywa te warunki, aby zapewnić, że projekt i środowisko są skonfigurowane pod kątem wydajności z dokładnością do wersji:

  • Wartość „Debuggable” (Możliwość debugowania) jest ustawiona na false.
  • Używasz urządzenia fizycznego – emulatory nie są obsługiwane.
  • Zegary są blokowane, jeśli urządzenie ma dostęp do roota.
  • Poziom naładowania baterii urządzenia wynosi co najmniej 25%.

Jeśli którykolwiek z poprzednich testów się nie powiedzie, test porównawczy zgłosi błąd, co pozwoli uniknąć niedokładności pomiarów.

Aby pominąć określone typy błędów jako ostrzeżenia i uniemożliwić im zatrzymanie testu porównawczego, przekaż typ błędu w postaci listy oddzielonej przecinkami do argumentu instrumentacji androidx.benchmark.suppressErrors.

Możesz go ustawić w skrypcie Gradle w ten sposób:

Kotlin

android {
    defaultConfig {
       …
      testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "DEBUGGABLE,LOW-BATTERY"
    }
}

Odlotowy

android {
    defaultConfig {
       …
      testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "DEBUGGABLE,LOW-BATTERY"
    }
}

Możesz też pomijać błędy z poziomu wiersza poleceń:

$ ./gradlew :benchmark:connectedCheck -P andoidtestInstrumentationRunnerArguments.androidx.benchmark.supperssErrors=DEBUGGABLE,LOW-BATTERY

Ograniczenie błędów umożliwia uruchomienie testu w nieprawidłowo skonfigurowanym stanie, a dane wyjściowe testu są celowo zmieniane przez dodanie nazw testowych z błędem. Na przykład przeprowadzanie testu porównawczego z możliwością debugowania z pominięciem kodu w poprzednim fragmencie powoduje dodanie nazw testowych przed znakiem DEBUGGABLE_.