Utwórz mikroporównanie

Aby dowiedzieć się, jak używać biblioteki Microbenchmark, wprowadzając zmiany w kodzie 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 dotyczące skuteczności, wykonaj te czynności, wyłączając debugowanie w aplikacji. Zachowaj te zmiany w lokalnej kopii roboczej, nie zatwierdzając ich w systemie kontroli źródła.

Aby przeprowadzić jednorazowe testy porównawcze:

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

    Kotlin

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

    Groovy

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

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

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

    Kotlin

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

    Groovy

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

    Kotlin

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

    Groovy

    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 mikroporównawczej.

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

Pełna konfiguracja projektu

Aby skonfigurować regularne porównywanie z wartościami odniesienia zamiast jednorazowego, wydziel wartości odniesienia do osobnego modułu. Dzięki temu ich konfiguracja, np. ustawienie debuggable na false, jest oddzielona od zwykłych testów.

Mikrotesty uruchamiają Twój kod bezpośrednio, więc umieść kod, który chcesz przetestować, w osobnym module Gradle i ustaw zależność od tego modułu zgodnie z rysunkiem 1.

struktura aplikacji,
Rysunek 1. Struktura aplikacji z modułami Gradle :app, :microbenchmark:benchmarkable, która umożliwia testowanie kodu w module :benchmarkable za pomocą testów porównawczych.

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 ustawioną wartością debuggable na false.

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

  2. W panelu Szablony kliknij Analiza porównawcza.

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

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

  5. Kliknij Zakończ.

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

Po utworzeniu modułu zmień jego plik build.gradle lub build.gradle.kts i dodaj androidTestImplementation do modułu zawierającego kod, który ma być testowany:

Kotlin

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

Groovy

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

Tworzenie klasy mikropomiaru

Testy porównawcze to standardowe testy z instrumentacją. Aby utworzyć test porównawczy, użyj klasy BenchmarkRule udostępnianej przez bibliotekę. Do testów porównawczych aktywności używaj wartości ActivityScenario lub ActivityScenarioRule. Aby porównać kod interfejsu, użyj @UiThreadTest.

Poniższy kod przedstawia 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 dla fragmentów kodu, których nie chcesz mierzyć za pomocą bloku runWithTimingDisabled{}. Te sekcje zwykle zawierają kod, który musisz 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));
}
    

Staraj się minimalizować ilość pracy wykonywanej w bloku measureRepeated i w runWithTimingDisabled. Blok measureRepeated jest wykonywany wielokrotnie i może mieć wpływ na ogólny czas potrzebny do przeprowadzenia testu porównawczego. Jeśli chcesz zweryfikować niektóre wyniki testu porównawczego, możesz potwierdzić ostatni wynik zamiast robić to w każdej iteracji testu.

Przeprowadź test porównawczy

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

Uruchamianie mikroporównania
Rysunek 3. Uruchom test mikrobenchmarku za pomocą działania na marginesie obok klasy testowej.

Możesz też uruchomić wszystkie testy z określonego modułu Gradle z wiersza poleceń, wpisując to polecenie: connectedCheck

./gradlew benchmark:connectedCheck

lub pojedynczy test:

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

Wyniki testu porównawczego

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

Wyniki mikropomiarów
Rysunek 4. wyniki mikropomiarów.

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

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

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

Błędy konfiguracji

Biblioteka wykrywa te warunki, aby mieć pewność, że projekt i środowisko są skonfigurowane pod kątem dokładności danych o skuteczności:

  • Wartość opcji Debuggable to false.
  • Używasz urządzenia fizycznego – emulatory nie są obsługiwane.
  • Zegary są blokowane, jeśli urządzenie ma dostęp do roota.
  • Wystarczający poziom naładowania baterii urządzenia (co najmniej 25%).

Jeśli którykolwiek z powyższych testów zakończy się niepowodzeniem, test porównawczy zgłosi błąd, aby zniechęcić do dokonywania niedokładnych pomiarów.

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

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

Kotlin

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

Groovy

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

Możesz też pominąć 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 wynik testu porównawczego jest celowo zmieniany przez dodanie do nazw testów przedrostka „error”. Na przykład uruchomienie testu porównawczego z możliwością debugowania z pominięciem w poprzednim fragmencie kodu spowoduje dodanie do nazw testów prefiksu DEBUGGABLE_.