Pengantar library Jetpack Benchmark

Library Jetpack Benchmark memungkinkan Anda menjalankan benchmark dengan cepat terhadap kode berbasis Java atau Kotlin dari dalam Android Studio. Library ini menangani pemanasan, mengukur performa kode dan jumlah alokasi, serta menampilkan hasil benchmark ke konsol Android Studio dan file JSON dengan lebih detail.

Kasus penggunaan mencakup men-scroll RecyclerView, menjalankan kueri database, dan mengukur bagian lambat kode yang ingin Anda jadikan lebih cepat.

Anda dapat menggunakan library dalam lingkungan continuous integration (CI), seperti yang dijelaskan dalam Menjalankan benchmark dalam Continuous Integration.

Jika Anda belum menggunakan AndroidX dalam project yang ingin diukur, lihat Melakukan migrasi project yang ada menggunakan Android Studio.

Panduan memulai

Bagian ini memberikan serangkaian langkah cepat untuk mencoba menjalankan benchmark tanpa harus memindahkan kode ke dalam modul. Karena langkah-langkahnya melibatkan penonaktifan proses debug untuk hasil performa yang akurat, Anda tidak akan melakukan perubahan pada sistem kontrol sumber, tetapi hal tersebut masih dapat membantu saat ingin menjalankan pengukuran satu kali.

Untuk menjalankan benchmark satu kali dengan cepat, lakukan hal berikut:

  1. Tambahkan library ke file build.gradle modul Anda:

    project_root/module_dir/build.gradle

    Groovy

    buildscript {
    ...
    dependencies {
        ...
        classpath "androidx.benchmark:benchmark-gradle-plugin:1.0.0"
    }
    }
    

    Kotlin

    dependencies {
        androidTestImplementation("androidx.benchmark:benchmark-junit4:1.0.0")
    }
    

    Groovy

    dependencies {
        androidTestImplementation "androidx.benchmark:benchmark-junit4:1.0.0"
    }
    
  2. Untuk menonaktifkan proses debug di manifes pengujian, update elemen <application> untuk menonaktifkan paksa proses debug sementara seperti berikut:

    project_root/module_dir/src/androidTest/AndroidManifest.xml

    <!-- Important: disable debuggable for accurate performance results -->
    <application
        android:debuggable="false"
        tools:ignore="HardcodedDebugMode"
        tools:replace="android:debuggable"/>
    
  3. Untuk menambahkan benchmark, tambahkan instance dari BenchmarkRule dalam file pengujian di direktori androidTest. Untuk informasi selengkapnya tentang menulis benchmark, lihat Menulis benchmark.

    Cuplikan kode berikut ini menunjukkan cara menambahkan benchmark ke pengujian JUnit:

    Kotlin

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

    Java

    @RunWith(AndroidJUnit4.class)
    class MyBenchmark {
        @Rule
        public BenchmarkRule benchmarkRule = new BenchmarkRule();
    
        @Test
        public void benchmarkSomeWork() {
            final BenchmarkState state = benchmarkRule.getState();
            while (state.keepRunning()) {
                doSomeWork();
            }
        }
    }
    

Waktu untuk menjalankan benchmark

Sebaiknya buat profil kode Anda sebelum menulis benchmark. Ini akan membantu Anda menemukan operasi berharga yang dapat dioptimalkan. Tindakan ini juga dapat mengetahui mengapa operasi lambat dengan menunjukkan hal yang terjadi saat operasi berjalan. Contohnya dapat mencakup berjalan di thread prioritas rendah, tidur karena akses disk, atau panggilan telepon yang tidak terduga ke fungsi yang berharga, seperti decoding bitmap.

Menambahkan titik pelacakan khusus melalui API TraceCompat (atau -ktx wrapper) memungkinkan Anda melihatnya di CPU Profiler Android Studio atau Systrace:

Kotlin

fun proccessBitmap(bitmap: Bitmap): Bitmap {
    trace("processBitmap") {
        // perform work on bitmap...
    }
}

Java

public Bitmap processBitmap(Bitmap bitmaptest) {
    TraceCompat.beginSection("processBitmap");
    try {
        // perform work on bitmap...
    } finally {
        TraceCompat.endSection();
    }
}

Apa saja yang perlu diukur

Benchmark sangat berguna untuk tugas CPU yang dijalankan berkali-kali di aplikasi Anda. Contoh yang baik adalah men-scroll RecyclerView, konversi/pemrosesan data, dan potongan kode yang digunakan berulang kali.

Jenis kode yang lain lebih sulit diukur dengan benchmark. Karena benchmark berjalan dalam sebuah loop, semua kode yang jarang dijalankan atau berperforma berbeda saat dipanggil beberapa kali, mungkin tidak sesuai untuk benchmark.

Menyimpan ke cache

Coba hindari pengukuran hanya cache. Misalnya, benchmark tata letak tampilan kustom mungkin hanya mengukur performa cache tata letak. Untuk menghindarinya, Anda dapat meneruskan parameter tata letak yang berbeda di setiap loop. Dalam kasus lain, seperti saat mengukur performa sistem file, tindakan ini mungkin sulit karena OS menyimpan cache sistem file sementara dalam sebuah loop.

Kode yang jarang dijalankan

Kode yang dijalankan satu kali selama startup aplikasi kecil kemungkinannya untuk dikompilasi dengan JIT dari Android Runtime (ART). Karena itu, menjalankan benchmark pada kode ini saat berjalan dalam sebuah loop bukanlah cara yang realistis untuk mengetahui performanya.

Untuk kode semacam ini, sebaiknya jalankan rekaman aktivitas atau pembuatan profil kode di aplikasi Anda. Namun hal ini tidak berarti Anda tidak dapat menjalankan benchmark dalam jalur startup, tetapi Anda harus memilih kode yang dijalankan dalam sebuah loop, sehingga akan meningkatkan kemungkinan untuk dikompilasi dengan JIT.

Penyiapan project lengkap

Agar dapat menyiapkan benchmark untuk benchmark reguler bukan benchmark satu kali, Anda harus memisahkan benchmark ke dalam modulnya masing-masing. Tindakan ini memastikan konfigurasi modul, seperti menetapkan debuggable ke false, terpisah dari pengujian reguler.

Sebelum menambahkan modul benchmark, tempatkan kode dan resource yang ingin Anda ukur ke dalam modul library jika belum ada.

Contoh kami menunjukkan bagaimana menyiapkan project dengan cara tersebut.

Menyiapkan properti Android Studio

Jika menggunakan Android Studio 3.5, Anda harus menetapkan properti Android Studio secara manual untuk mengaktifkan dukungan wizard modul benchmark. Tindakan ini tidak diperlukan untuk Android Studio 3.6 atau yang lebih tinggi.

Untuk mengaktifkan template Android Studio pada benchmark, lakukan hal berikut:

  1. Di Android Studio 3.5, klik Help > Edit Custom Properties.

  2. Tambahkan baris berikut ke file yang terbuka:

    npw.benchmark.template.module=true

  3. Simpan dan tutup file tersebut.

  4. Mulai ulang Android Studio.

Membuat modul baru

Template modul benchmark akan secara otomatis mengonfigurasi setelan untuk benchmark.

Untuk menggunakan template modul guna membuat modul baru, lakukan hal berikut:

  1. Klik kanan project atau modul Anda, kemudian pilih New > Module.

  2. Pilih Benchmark Module lalu klik Next.

    Gambar 1. Modul benchmark

  3. Masukkan nama modul, pilih bahasa, dan klik Finish.

    Modul dibuat dan telah dikonfigurasi sebelumnya untuk benchmark, dengan menambahkan direktori benchmark serta menetapkan debuggable ke false.

Menulis tolok ukur

Benchmark adalah pengujian instrumentasi standar. Untuk membuat benchmark, gunakan class BenchmarkRule yang disediakan oleh library. Untuk menjalankan benchmark aktivitas, gunakan ActivityTestRule atau ActivityScenarioRule. Untuk menjalankan benchmark kode UI, gunakan @UiThreadTest.

Kode berikut menunjukkan benchmark contoh:

Kotlin

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

    @Test
    fun simpleViewInflate() {
        val context = ApplicationProvider.getApplicationContext<Context>()
        val inflater = LayoutInflater.from(context)
        val root = FrameLayout(context)

        benchmarkRule.measureRepeated {
            inflater.inflate(R.layout.test_simple_view, root, false)
        }
    }
}

Java

@RunWith(AndroidJUnit4::class)
public class ViewBenchmark {
    @Rule
    public BenchmarkRule benchmarkRule = new BenchmarkRule();

    @Test
    public void simpleViewInflate() {
        Context context = ApplicationProvider.getApplicationContext<Context>();
        final BenchmarkState state = benchmarkRule.getState();
        LayoutInflater inflater = LayoutInflater.from(context);
        FrameLayout root = new FrameLayout(context);

        while (state.keepRunning()) {
            inflater.inflate(R.layout.test_simple_view, root, false);
        }
    }
}

Anda dapat menonaktifkan pengaturan waktu untuk bagian kode yang tidak ingin diukur, seperti yang ditunjukkan pada contoh kode berikut:

Kotlin

@Test
fun bitmapProcessing() = benchmarkRule.measureRepeated {
    val input: Bitmap = runWithTimingDisabled { constructTestBitmap() }
    processBitmap(input)
}

Java

@Test
public void bitmapProcessing() {
    final BenchmarkState state = benchmarkRule.getState();
    while (state.keepRunning()) {
        state.pauseTiming();
        Bitmap input = constructTestBitmap();
        state.resumeTiming();

        processBitmap(input);
    }
}

Untuk informasi tentang menjalankan benchmark, lihat Menjalankan benchmark.

Menjalankan benchmark

Di Android Studio, jalankan benchmark seperti halnya @Test. Di Android Studio 3.4 dan yang lebih tinggi, Anda dapat melihat output yang dikirim ke konsol.

Untuk menjalankan benchmark, dalam modul, buka benchmark/src/androidTest lalu tekan Control+Shift+F10 (Command+Shift+R di Mac). Hasil benchmark akan ditampilkan di konsol seperti yang ditunjukkan pada Gambar 2:

Output benchmark di Android Studio

Gambar 2. Output benchmark di Android Studio

Dari command line, jalankan connectedCheck reguler:

./gradlew benchmark:connectedCheck

Mengumpulkan data

Laporan benchmark lengkap dengan metrik tambahan dan informasi perangkat tersedia dalam format JSON.

Plugin Gradle androidx.benchmark mengaktifkan output JSON secara default. Untuk mengaktifkan output JSON secara manual di lingkungan build non-Gradle, teruskan argumen instrumentasi, androidx.benchmark.output.enable, lalu tetapkan ke true.

Berikut adalah contoh menggunakan perintah adb shell am instrument:

adb shell am instrument -w -e "androidx.benchmark.output.enable" "true" com.android.foo/androidx.benchmark.junit4.AndroidBenchmarkRunner

Secara default, laporan JSON ditulis ke disk di perangkat dalam folder download bersama eksternal APK pengujian, yang biasanya terletak di:

/storage/emulated/0/Download/app_id-benchmarkData.json

Anda dapat mengonfigurasi lokasi tempat laporan benchmark disimpan pada perangkat menggunakan argumen instrumentasi additionalTestOutputDir.

adb shell am instrument -w -e "androidx.benchmark.output.enable" "true" -e "additionalTestOutputDir" "/path_to_a_directory_on_device_test_has_write_permissions_to/" com.android.foo/androidx.benchmark.junit4.AndroidBenchmarkRunner

Laporan JSON akan disalin dari perangkat ke host. Laporan tersebut ditulis pada mesin host di:

project_root/module/build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/device_id/app_id-benchmarkData.json

Android Gradle Plugin 3.6 dan yang lebih baru

Saat menjalankan benchmark dari command line dengan Gradle, project yang menggunakan Android Gradle Plugin 3.6 dan yang lebih baru dapat menambahkan flag berikut ke file gradle.properties project:

android.enableAdditionalTestOutput=true

Tindakan ini akan mengaktifkan fitur Android Gradle Plugin eksperimental untuk mengambil laporan benchmark dari perangkat yang menjalankan API 16 dan yang lebih tinggi.

Android Gradle Plugin 3.5 dan yang lebih lama

Bagi pengguna Android Gradle Plugin versi yang lebih lama, plugin Gradle androidx.benchmark akan menangani penyalinan laporan benchmark JSON dari perangkat ke host.

Saat menggunakan AGP 3.5 atau yang lebih lama dan menargetkan API level 29 atau yang lebih tinggi, Anda harus menambahkan flag ke manifes Android di direktori androidTest dari benchmark untuk mengaktifkan perilaku penyimpanan eksternal lama.

<manifest ... >
  <!-- This attribute is "false" by default on apps targeting Android 10. -->
  <application android:requestLegacyExternalStorage="true" ... >
    ...
  </application>
</manifest>

Untuk informasi selengkapnya, lihat Memilih tidak menggunakan penyimpanan terbatas untuk sementara.

Stabilitas frekuensi CPU

Frekuensi CPU pada perangkat seluler berubah secara dinamis dari kondisi tinggi (untuk performa) ke kondisi rendah (untuk menghemat daya, atau saat perangkat menjadi panas). Frekuensi yang bervariasi ini dapat membuat hasil benchmark sangat bervariasi, sehingga library memberikan cara untuk mengatasi masalah ini.

Mengunci frekuensi CPU (membutuhkan root)

Mengunci frekuensi CPU adalah cara terbaik untuk mendapatkan performa yang stabil. Cara ini memastikan bahwa frekuensi CPU tidak terlalu tinggi sehingga membuat panas perangkat, atau rendah jika benchmark tidak sepenuhnya menggunakan CPU. Meskipun ini adalah cara terbaik untuk memastikan performa yang stabil, sebagian besar perangkat tidak mendukungnya karena memerlukan root adb.

Untuk mengunci frekuensi CPU, tambahkan plugin pembantu yang disediakan ke classpath project level teratas pada file build.gradle utama:

buildscript {
    ...
    dependencies {
        ...
        classpath("androidx.benchmark:benchmark-gradle-plugin:1.0.0")
    }
}

Terapkan plugin dalam build.gradle modul benchmark Anda:

Groovy

plugins {
    id 'com.android.app'
    id 'androidx.benchmark'
}
...

Kotlin

plugins {
    id("com.android.app")
    id("androidx.benchmark")
    ...
}

Tindakan ini akan menambahkan tugas Gradle benchmark ke project Anda, termasuk ./gradlew lockClocks dan ./gradlew unlockClocks. Gunakan tugas ini untuk mengunci dan membuka kunci CPU perangkat menggunakan adb.

Jika memiliki beberapa perangkat yang terlihat oleh adb, gunakan ANDROID_SERIAL variabel lingkungan untuk menentukan perangkat mana saja yang harus dijalankan oleh tugas Gradle di:

ANDROID_SERIAL=device-id-from-adb-devices ./gradlew lockClocks

Mode performa berkelanjutan

Window.setSustainedPerformanceMode() adalah fitur yang didukung oleh beberapa perangkat yang memungkinkan aplikasi memilih frekuensi CPU maksimal yang lebih rendah. Saat berjalan di perangkat yang didukung, library Benchmark akan menggunakan kombinasi API ini dan lmeluncurkan aktivitasnya sendiri untuk mencegah penurunan frekuensi karena panas dan menstabilkan hasil.

Fungsionalitas ini diaktifkan secara default oleh testInstrumentationRunner yang disetel oleh plugin Gradle. Jika ingin menggunakan runner kustom, Anda dapat membuat subclass AndroidBenchmarkRunner dan menggunakannya sebagai testInstrumentationRunner.

Runner meluncurkan aktivitas layar penuh buram untuk memastikan bahwa benchmark berjalan di latar depan dan tidak ada penggambaran aplikasi lain.

Penjedaan eksekusi otomatis

Jika penguncian frekuensi CPU atau performa berkelanjutan digunakan, library akan secara otomatis melakukan deteksi penurunan frekuensi karena panas. Jika diaktifkan, benchmark internal akan berjalan secara berkala untuk menentukan kapan suhu perangkat cukup tinggi yang akan menurunkan performa CPU. Ketika penurunan performa CPU terdeteksi, library akan menjeda eksekusi untuk mendinginkan perangkat, dan mencoba kembali benchmark saat ini.

Error Konfigurasi

Library akan mendeteksi kondisi berikut guna memastikan project dan lingkungan Anda telah disiapkan untuk performa akurat rilis:

  • Debuggable disetel ke false.
  • Perangkat fisik, bukan emulator, sedang digunakan.
  • Frekuensi CPU terkunci jika perangkat di-root.
  • Tingkat daya baterai di perangkat memadai.

Jika salah satu pemeriksaan di atas gagal, benchmark akan memberikan pesan error untuk mencegah pengukuran yang tidak akurat.

Untuk mengubah error tersebut menjadi peringatan, dan mencegahnya memberikan pesan error sehingga menghentikan benchmark, teruskan jenis error yang ingin Anda ubah dalam daftar yang dipisahkan koma ke argumen instrumentasi androidx.benchmark.suppressErrors:

adb shell am instrument -w -e "androidx.benchmark.suppressErrors" "DEBUGGABLE" com.android.foo/androidx.benchmark.junit4.AndroidBenchmarkRunner

Yang dapat Anda tetapkan di Gradle sebagai berikut:

Groovy

android {
    defaultConfig {
        testInstrumentationRunnerArgument 'androidx.benchmark.suppressErrors', 'DEBUGGABLE'
    }
}

Kotlin

android {
    defaultConfig {
        testInstrumentationRunnerArguments(mapOf(
            "androidx.benchmark.suppressErrors" to "DEBUGGABLE"
        ))
    }
}

Perlu diperhatikan bahwa mengubah error memungkinkan benchmark berjalan dalam keadaan yang tidak terkonfigurasi dengan benar, tetapi output dari benchmark akan secara sengaja dirusak dengan menambahkan nama pengujian dengan error tersebut. Artinya, menjalankan benchmark yang dapat di-debug dengan penggantian seperti di atas akan menambahkan nama pengujian dengan DEBUGGABLE_.

Pembuatan profil

Anda dapat membuat profil benchmark untuk menyelidiki mengapa kode yang diukur berjalan lambat.

Tambahkan kode berikut ini ke file build.gradle modul benchmark:

Groovy

android {
    defaultConfig {
        // must be one of: 'None', 'StackSampling', or 'MethodTracing'
        testInstrumentationRunnerArgument 'androidx.benchmark.profiling.mode', 'StackSampling'
    }
}

Kotlin

android {
    defaultConfig {
        // must be one of: 'None', 'StackSampling', or 'MethodTracing'
        testInstrumentationRunnerArgument = mapOf(
            "androidx.benchmark.profiling.mode", "StackSampling"
        )
    }
}

Saat Anda menjalankan benchmark, file .trace output akan disalin ke host, dalam direktori di samping hasil JSON. Buka file ini dengan Android Studio menggunakanFile - Open untuk memeriksa hasil pembuatan profil di CPU Profiler.

Pelacakan metode

Dengan pelacakan metode, benchmark akan melakukan pemanasan sebelum menangkap pelacakan metode, mencatat setiap metode yang dipanggil oleh benchmark Anda. Perlu diperhatikan bahwa hasil performa akan dipengaruhi secara signifikan oleh overhead pengambilan setiap masuk/keluar metode.

Pengambilan sampel stack

Dengan pengambilan sampel stack, benchmark akan mengambil sampel stack panggilan, setelah warmup selesai. Anda dapat mengontrol frekuensi sampel dan durasi pengambilan sampel menggunakan argumen instrumentasi.

Di Android 10 (API 29) dan yang lebih tinggi, pengambilan sampel stack menggunakan Simpleperf untuk mengambil sampel callstack aplikasi, termasuk kode C++. Di Android 9 (API 28) dan yang lebih rendah, Debug.startMethodTracingSampling digunakan untuk mengambil sampel stack.

Untuk membaca selengkapnya cara menggunakan pelacakan dan pengambilan sampel metode, lihat konfigurasi pembuatan profil CPU.

Contoh benchmark

Kode benchmark contoh tersedia di project berikut:

Project contoh meliputi:

  • BenchmarkSample: ini adalah contoh mandiri, yang menunjukkan cara menggunakan modul benchmark untuk mengukur kode dan UI.

  • PagingWithNetworkSample: Contoh Komponen Arsitektur Android, yang menunjukkan cara mengukur performa RecyclerView.

  • WorkManagerSample: Contoh Komponen Arsitektur Android, yang menunjukkan cara mengukur pekerja WorkManager.

Referensi lainnya

Untuk mempelajari lebih lanjut tentang Benchmark, lihat referensi tambahan berikut.

Blog

Berikan masukan

Untuk melaporkan masalah atau mengirimkan permintaan fitur saat menggunakan benchmark, lihat Issue Tracker publik.