Utwórz mikroporównanie

Aby dowiedzieć się, jak używać biblioteki Microbenchmark, dodając zmiany do kodu aplikacji, zapoznaj się z sekcją Szybki start. Aby dowiedzieć się, jak przeprowadzić pełną konfigurację z bardziej skomplikowanymi zmianami w bazie kodu, zapoznaj się z sekcją Pełna konfiguracja projektu.

Krótkie wprowadzenie

Z 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, musisz wyłączyć debugowanie w aplikacji. Zrób to w lokalnej kopii roboczej, nie zapisując 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.4")
    }

    Dynamiczny

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

    Użyj zależności implementation zamiast androidTestImplementation. Jeśli używasz androidTestImplementation, testy porównawcze nie zostaną uruchomione, ponieważ manifest biblioteki nie jest scalony z manifestem aplikacji.

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

    Kotlin

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

    Dynamiczny

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

    Kotlin

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

    Dynamiczny

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

    Poniższy fragment kodu pokazuje, jak dodać test porównawczy do testu instrumentowanego:

    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ć test porównawczy, przejdź do sekcji Tworzenie klasy Microbenchmark.

Pełna konfiguracja projektu

Aby skonfigurować regularne testy porównawcze, a nie jednorazowe, wyodrębnij je do osobnego modułu. Dzięki temu ich konfiguracja, np. ustawienie debuggable na false, jest oddzielona od zwykłych testów.

Ponieważ Microbenchmark uruchamia kod bezpośrednio, umieść kod, który chcesz przetestować, w osobnym module Gradle i ustaw zależność od tego modułu, jak pokazano na rysunku 1.

struktura aplikacji
Rysunek 1. Struktura aplikacji z modułami Gradle :app, :microbenchmark i :benchmarkable, która umożliwia mikrotestom porównawczym testowanie kodu w module :benchmarkable.

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

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

  2. W panelu Szablony wybierz Analiza porównawcza.

  3. Jako typ modułu testu porównawczego wybierz Microbenchmark.

  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 testu porównawczego:

Kotlin

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

Dynamiczny

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

Tworzenie klasy Microbenchmark

Testy porównawcze to standardowe testy z instrumentacją. Aby utworzyć test porównawczy, użyj klasy BenchmarkRule udostępnianej przez bibliotekę. Aby przeprowadzić test porównawczy działań, użyj ActivityScenario lub ActivityScenarioRule. Aby przeprowadzić test porównawczy kodu interfejsu, użyj @UiThreadTest.

Poniższy kod pokazuje przykładowy test porównawczy:

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łączanie czasu konfiguracji

Możesz wyłączyć pomiar czasu w przypadku fragmentów kodu, których nie chcesz mierzyć, za pomocą bloku runWithTimingDisabled{}. Te sekcje zwykle reprezentują kod, który należy uruchomić w każdej iteracji testu porównawczego.

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));
}
    

Postaraj się zminimalizować ilość pracy wykonywanej w bloku measureRepeated i w runWithTimingDisabled. Blok measureRepeated jest uruchamiany wielokrotnie i może mieć wpływ na łączny czas potrzebny na przeprowadzenie testu porównawczego. Jeśli chcesz sprawdzić niektóre wyniki testu porównawczego, możesz potwierdzić ostatni wynik, zamiast robić to w każdej iteracji testu.

Uruchamianie testu porównawczego

W Android Studio uruchom test porównawczy tak samo jak każdy @Test, używając działania w rynience obok klasy lub metody testowej, jak pokazano na rysunku 3.

Uruchomienie mikrotestu porównawczego
Rysunek 3. Uruchom test Microbenchmark, używając działania w rynience obok klasy testowej.

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

./gradlew benchmark:connectedCheck

Możesz też uruchomić pojedynczy test:

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

Wyniki testu porównawczego

Po pomyślnym uruchomieniu Microbenchmark dane są wyświetlane bezpośrednio w Android Studio, a pełny raport z testu porównawczego z dodatkowymi danymi i informacjami o urządzeniu jest dostępny w formacie JSON.

Wyniki mikrotestu porównawczego
Rysunek 4. Wyniki mikrotestu porównawczego.

Raporty JSON i wszelkie ślady profilowania są też automatycznie kopiowane 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/

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

Błędy konfiguracji

Biblioteka wykrywa te warunki, aby upewnić się, że projekt i środowisko są skonfigurowane pod kątem wydajności odpowiedniej dla wersji:

  • Atrybut debuggable jest ustawiony na false.
  • używasz urządzenia fizycznego – emulatory nie są obsługiwane;
  • Zegary są zablokowane, jeśli urządzenie ma dostęp do roota.
  • Wystarczający poziom baterii na urządzeniu (co najmniej 25%).

Jeśli któryś z tych testów zakończy się niepowodzeniem, test porównawczy zgłosi błąd, aby zapobiec niedokładnym pomiarom.

Aby ukryć określone typy błędów jako ostrzeżenia i zapobiec zatrzymaniu testu porównawczego, przekaż typ błędu na liście rozdzielonej przecinkami do argumentu instrumentacji androidx.benchmark.suppressErrors.

Możesz to ustawić w skrypcie Gradle, jak w tym przykładzie:

Kotlin

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

Dynamiczny

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

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

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

Pomijanie błędów umożliwia uruchomienie testu porównawczego w nieprawidłowo skonfigurowanym stanie, a dane wyjściowe testu porównawczego są celowo zmieniane przez dodanie błędów na początku nazw testów. Na przykład uruchomienie testu porównawczego z możliwością debugowania z pominięciem w poprzednim fragmencie kodu powoduje dodanie do nazw testów prefiksu DEBUGGABLE_.