Utwórz mikroporównanie

Aby dowiedzieć się, jak korzystać z biblioteki Microbenchmark, wprowadzając zmiany w kodzie aplikacji, zapoznaj się z sekcją Krótkie wprowadzenie. Aby dowiedzieć się, jak przeprowadzić pełną konfigurację z bardziej skomplikowanymi zmianami w kodzie, przeczytaj sekcję Pełna konfiguracja projektu.

Krótkie wprowadzenie

Z tej sekcji dowiesz się, jak przeprowadzić test porównawczy i jednorazowe pomiary bez konieczności przenoszenia kodu do modułów. Aby uzyskać dokładne wyniki dotyczące wydajności, te kroki obejmują wyłączenie debugowania w aplikacji, więc zachowaj te dane w lokalnej kopii roboczej bez wprowadzania zmian w systemie sterowania źródłem.

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

    Groovy

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

    Zamiast zależności androidTestImplementation użyj zależności implementation. Jeśli używasz androidTestImplementation, testy porównawcze nie są wykonywane, ponieważ manifest biblioteki nie jest scalany z plikiem manifestu aplikacji.

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

    Kotlin

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

    Odlotowe

    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ć wartość testu porównawczego, dodaj instancję funkcji BenchmarkRule w pliku testowym w katalogu androidTest. Więcej informacji o tworzeniu testów porównawczych znajdziesz w artykule Tworzenie klasy testów mikroporównawczych.

    Ten fragment kodu pokazuje, jak dodać punkt odniesienia do testu z użyciem pomiarów:

    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 testów mikroporównawczych.

Pełna konfiguracja projektu

Aby skonfigurować regularne testy porównawcze zamiast jednorazowych, wyodrębnij testy porównawcze w osobnym module. Dzięki temu ich konfiguracja (np. ustawienie debuggable na false) będzie odrębna od zwykłych testów.

Ponieważ Microbenchmark uruchamia kod bezpośrednio, umieść kod, który chcesz przetestować, w oddzielnym 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:benchmarkable, która umożliwia testowanie kodu w modułach :benchmarkable w Microbenchmarks.

Aby dodać nowy moduł Gradle, możesz użyć kreatora modułów w Android Studio. Kreator tworzy moduł wstępnie skonfigurowany do porównywania, z dodanym katalogiem porównywania 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 kliknij Porównanie.

  3. Jako typ modułu porównawczego wybierz Microbenchmark (Test porównawczy mikrobenchmarków).

  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 do modułu zawierającego kod do testów porównawczych element androidTestImplementation:

Kotlin

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

Groovy

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

Tworzenie klasy mikrotestów

Wskaźniki to standardowe testy z wykorzystaniem instrumentacji. Aby utworzyć punkt odniesienia, użyj klasy BenchmarkRule udostępnionej przez bibliotekę. Aby porównać aktywność, użyj kolumny ActivityScenario lub ActivityScenarioRule. Aby porównać kod interfejsu, użyj narzędzia @UiThreadTest.

Poniższy kod pokazuje przykładowe testy porównawcze:

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 ustawień czasu

Za pomocą bloku runWithTimingDisabled{} możesz wyłączyć pomiar czasu w sekcjach kodu, których nie chcesz mierzyć. Sekcje te zazwyczaj zawierają kod, który należy uruchamiać przy 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));
}
    

Spróbuj zminimalizować ilość pracy wykonywanej w bloku measureRepeated i w bloku runWithTimingDisabled. Blok measureRepeated jest uruchamiany wiele razy i może wpłynąć na ogólny czas potrzebny na przeprowadzenie testu porównawczego. Jeśli chcesz zweryfikować niektóre wyniki testu porównawczego, możesz potwierdzić ostatni wynik zamiast weryfikować go po każdej iteracji testu.

Przeprowadzanie testu porównawczego

W Android Studio uruchom test porównawczy tak jak dowolną funkcję @Test, korzystając z działania w sekcji ścieżki obok testowanej klasy lub metody, jak pokazano na rysunku 3.

Uruchomienie mikrotestu porównawczego
Rysunek 3. Uruchom test Microbenchmark za pomocą działania w pasku bocznym obok klasy testowej.

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

./gradlew benchmark:connectedCheck

Możesz też wybrać pojedynczy test:

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

Wyniki testów porównawczych

Po udanym przeprowadzeniu testu porównawczego mikrodane 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 mikrotestów porównawczych
Rysunek 4. Wyniki mikrotestów porównawczych.

Raporty JSON i wszystkie ś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 w formacie JSON jest zapisywany na dysku urządzenia w zewnętrznym folderze udostępnionych multimediów pliku APK testowego, 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 dokładności działania w wersji:

  • Debuggable ma wartość false.
  • Używane jest urządzenie fizyczne – emulatory nie są obsługiwane.
  • Zegary są zablokowane, jeśli urządzenie ma dostęp do roota.
  • Urządzenie musi mieć co najmniej 25% poziom naładowania baterii.

Jeśli którykolwiek z poprzednich testów zakończy się niepowodzeniem, benchmark zgłosi błąd, aby uniknąć niedokładnych pomiarów.

Aby pominąć wyświetlanie określonych typów błędów jako ostrzeżeń i uniemożliwić im zatrzymanie testu porównawczego, prześlij typ błędu w postaci listy oddzielonych przecinkami do argumentu 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"
    }
}

Odlotowe

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

Błędy możesz też pomijać z poziomu wiersza poleceń:

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

Funkcja pomijania błędów umożliwia przeprowadzanie testu w nieprawidłowo skonfigurowanym stanie, a dane wyjściowe są celowo zmieniane przez nazwy testów z błędem na początku. Na przykład uruchomienie testu porównawczego z możliwością debugowania i wyłączeniem w poprzednim fragmencie kodu powoduje, że nazwy testów są poprzedzone symbolem DEBUGGABLE_.