Meningkatkan performa aplikasi dengan Profil Dasar Pengukuran

1. Sebelum memulai

Dalam codelab ini, Anda akan mempelajari cara membuat Profil Dasar Pengukuran untuk mengoptimalkan performa aplikasi dan cara memverifikasi manfaat performa menggunakan Profil Dasar Pengukuran.

Prasyarat

Codelab ini dibuat berdasarkan codelab Memeriksa performa aplikasi dengan Macrobenchmark yang menunjukkan cara mengukur performa aplikasi dengan library Macrobenchmark.

Yang Anda butuhkan

Yang akan Anda lakukan

  • Membuat Profil Dasar Pengukuran untuk mengoptimalkan performa
  • Memverifikasi peningkatan performa dengan library Macrobenchmark

Yang akan Anda pelajari

  • Membuat profil dasar pengukuran
  • Memahami peningkatan performa Profil Dasar Pengukuran

2. Mempersiapkan

Untuk memulai, clone repositori GitHub dari command line dengan menggunakan perintah berikut:

$ git clone --branch baselineprofiles-main  https://github.com/googlecodelabs/android-performance.git
$ cd android-macrobenchmark-codelab

Atau, Anda dapat mendownload dua file ZIP:

Membuka Project ke Android Studio

  1. Di jendela Welcome to Android Studio, pilih c01826594f360d94.png Open an Existing Project
  2. Pilih folder [Download Location]/android-performance-codelabs/benchmarking (tips: pastikan Anda memilih direktori benchmarking yang berisi build.gradle)
  3. Setelah Android Studio mengimpor project, pastikan Anda dapat menjalankan modul app untuk mem-build aplikasi contoh yang akan diukur dengan benchmark.

3. Apa itu Profil Dasar Pengukuran

Profil Dasar Pengukuran adalah daftar class dan metode yang disertakan dalam APK yang digunakan oleh Android Runtime (ART) selama penginstalan aplikasi untuk melakukan prakompilasi jalur penting ke kode mesin. Ini adalah bentuk pengoptimalan yang dipandu profil (PGO) yang memungkinkan aplikasi mengoptimalkan startup, mengurangi jank, dan meningkatkan performa untuk pengguna akhir.

Cara kerja Profil Dasar Pengukuran

Aturan profil dikompilasi ke dalam bentuk biner di APK, dalam assets/dexopt/baseline.prof, lalu dikirimkan langsung ke pengguna (melalui Google Play) bersama dengan APK.

Selama penginstalan aplikasi, ART melakukan kompilasi metode Ahead-Of-Time (AOT) pada profil, sehingga metode tersebut dieksekusi lebih cepat. Jika profil berisi metode yang digunakan dalam peluncuran aplikasi atau selama rendering frame, pengguna akan mendapatkan waktu peluncuran yang lebih cepat dan/atau mengurangi jank.

4. Mengupdate modul benchmark

Sebagai developer aplikasi, Anda dapat membuat Profil Dasar Pengukuran secara otomatis menggunakan library Jetpack Macrobenchmark. Untuk menghasilkan Profil Dasar Pengukuran, Anda dapat menggunakan modul yang sama yang dibuat untuk menjalankan benchmark pada aplikasi Anda dengan beberapa perubahan tambahan.

Menonaktifkan obfuscation untuk Profil Dasar Pengukuran

Jika aplikasi Anda telah mengaktifkan obfuscation, Anda perlu menonaktifkannya untuk benchmark.

Anda dapat melakukannya dengan menambahkan file proguard tambahan ke modul :app dan menonaktifkan obfuscation di sana, lalu menambahkan file proguard ke buildType benchmark.

Buat file baru bernama benchmark-rules.pro dalam modul :app. File harus ditempatkan di folder /app/ di samping file build.gradle khusus modul. 27bd3b1881011d06.png

Dalam file ini, nonaktifkan obfuscation dengan menambahkan -dontobfuscate seperti dalam cuplikan berikut:

# Disables obfuscation for benchmark builds.
-dontobfuscate

Selanjutnya, ubah buildType benchmark dalam build.gradle khusus modul :app, lalu tambahkan file yang Anda buat. Karena kita menggunakan buildType rilis initWith, baris ini akan menambahkan file proguard benchmark-rules.pro ke file proguard rilis.

buildTypes {
   release {
      // ...
   }

   benchmark {
      initWith buildTypes.release
      // ...
      proguardFiles('benchmark-rules.pro')
   }
}

Sekarang, mari kita tulis class generator Profil Dasar Pengukuran.

5. Menulis class generator Profil Dasar Pengukuran

Biasanya, Anda akan membuat Profil Dasar Pengukuran untuk perjalanan pengguna biasa pada aplikasi Anda.

Pada contoh kami, Anda dapat mengidentifikasi tiga perjalanan ini:

  1. Memulai aplikasi (ini akan sangat penting untuk sebagian besar aplikasi)
  2. Men-scroll daftar camilan
  3. Membuka detail camilan

Untuk membuat Profil Dasar Pengukuran, kita akan menambahkan class pengujian baru BaselineProfileGenerator dalam modul :macrobenchmark. Class ini akan menggunakan aturan pengujian BaselineProfileRule dan akan berisi satu metode pengujian untuk membuat profil. Titik entri untuk membuat profil adalah fungsi collectBaselineProfile. Pengujian ini hanya memerlukan dua parameter:

  • packageName, yang merupakan paket aplikasi Anda
  • profileBlock (parameter lambda terakhir)
@RunWith(AndroidJUnit4::class)
class BaselineProfileGenerator {

   @get:Rule
   val rule = BaselineProfileRule()

   @Test
   fun generate() {
       rule.collectBaselineProfile("com.example.macrobenchmark_codelab") {
           // TODO Add interactions for the typical user journeys
       }
   }
}

Di lambda profileBlock, Anda menentukan interaksi yang mencakup perjalanan pengguna biasa pada aplikasi Anda. Library akan menjalankan profileBlock beberapa kali dan akan mengumpulkan class dan fungsi yang dipanggil untuk dioptimalkan dan membuat Profil Dasar Pengukuran di perangkat.

Anda dapat memeriksa garis besar generator Profil Dasar Pengukuran kami yang mencakup perjalanan biasa dalam cuplikan berikut:

@RunWith(AndroidJUnit4::class)
class BaselineProfileGenerator {

   @get:Rule
   val rule = BaselineProfileRule()

   @Test
   fun generate() {
       rule.collectBaselineProfile("com.example.macrobenchmark_codelab") {
           startApplicationJourney() // TODO Implement
           scrollSnackListJourney() // TODO Implement
           goToSnackDetailJourney() // TODO Implement
       }
   }
}

Sekarang, mari kita tulis interaksi untuk setiap perjalanan yang disebutkan. Anda dapat menulisnya sebagai fungsi ekstensi MacrobenchmarkScope sehingga Anda memiliki akses ke parameter dan fungsi yang disediakannya. Menulis seperti ini memungkinkan Anda menggunakan kembali interaksi dengan benchmark untuk memverifikasi peningkatan performa.

Memulai perjalanan aplikasi

Untuk perjalanan startup aplikasi (startApplicationJourney), Anda perlu mengikutsertakan interaksi berikut:

  1. Menekan tombol layar utama untuk memastikan status aplikasi dimulai ulang
  2. Memulai Aktivitas default dan menunggu hingga frame pertama dirender
  3. Menunggu hingga konten dimuat dan dirender, dan pengguna dapat berinteraksi dengannya
fun MacrobenchmarkScope.startApplicationJourney() {
   pressHome()
   startActivityAndWait()
   val contentList = device.findObject(By.res("snack_list"))
   // Wait until a snack collection item within the list is rendered
   contentList.wait(Until.hasObject(By.res("snack_collection")), 5_000)
}

Perjalanan daftar scroll

Untuk scroll perjalanan daftar camilan (scrollSnackListJourney), Anda dapat mengikuti interaksi berikut:

  1. Menemukan elemen UI daftar camilan
  2. Menetapkan margin gestur agar tidak memicu navigasi sistem
  3. Men-scroll daftar dan menunggu hingga UI selesai
fun MacrobenchmarkScope.scrollSnackListJourney() {
   val snackList = device.findObject(By.res("snack_list"))
   // Set gesture margin to avoid triggering gesture navigation
   snackList.setGestureMargin(device.displayWidth / 5)
   snackList.fling(Direction.DOWN)
   device.waitForIdle()
}

Membuka perjalanan detail

Perjalanan terakhir (goToSnackDetailJourney) menerapkan interaksi ini:

  1. Menemukan daftar camilan dan menemukan semua item camilan yang dapat Anda gunakan
  2. Memilih item dari daftar
  3. Mengklik item tersebut dan menunggu hingga layar detail dimuat. Anda dapat memanfaatkan fakta bahwa daftar camilan tidak akan muncul lagi di layar
fun MacrobenchmarkScope.goToSnackDetailJourney() {
   val snackList = device.findObject(By.res("snack_list"))
   val snacks = snackList.findObjects(By.res("snack_item"))
   // Select random snack from the list
   snacks[Random.nextInt(snacks.size)].click()
   // Wait until the screen is gone = the detail is shown
   device.wait(Until.gone(By.res("snack_list")), 5_000)
}

Sekarang Anda telah menentukan semua interaksi yang diperlukan agar generator Profil Dasar Pengukuran siap dijalankan, tetapi Anda harus menentukan perangkat yang akan digunakan terlebih dahulu untuk menjalankannya.

6. Menyiapkan Perangkat yang Dikelola Gradle

Untuk membuat Profil Dasar Pengukuran, Anda harus menyiapkan emulator userdebug terlebih dahulu. Untuk mengotomatiskan proses pembuatan Profil Dasar Pengukuran, Anda dapat menggunakan Perangkat yang Dikelola Gradle. Anda dapat mempelajari lebih lanjut Perangkat yang Dikelola Gradle dalam dokumentasi kami.

Pertama, tentukan Perangkat yang Dikelola Gradle dalam file build.gradle modul :macrobenchmark seperti dalam cuplikan berikut:

testOptions {
    managedDevices {
        devices {
            pixel2Api31(com.android.build.api.dsl.ManagedVirtualDevice) {
                device = "Pixel 2"
                apiLevel = 31
                systemImageSource = "aosp"
            }
        }
    }
}

Untuk membuat Profil Dasar Pengukuran, Anda harus menggunakan Android 9 (API 28) yang telah di-root atau yang lebih tinggi.

Dalam hal ini, kita akan menggunakan Android 11 (API level 31) dan image sistem aosp dapat mengakses root.

Perangkat yang Dikelola Gradle memungkinkan Anda menjalankan pengujian pada emulator Android tanpa perlu meluncurkannya secara manual dan membongkarnya. Setelah menambahkan definisi ke build.gradle, tugas pixel2Api31[BuildVariant]AndroidTest baru akan dapat dijalankan. Kita akan menggunakan tugas tersebut di langkah berikutnya untuk membuat Profil Dasar Pengukuran.

7. Menjalankan pengujian generator Profil Dasar Pengukuran

Setelah Perangkat yang Dikelola Gradle siap, Anda dapat memulai pengujian generator.

Menjalankan generator dari konfigurasi run

Perangkat yang Dikelola Gradle memerlukan pengujian yang dijalankan sebagai tugas Gradle. Untuk memulai dengan cepat, kami telah membuat konfigurasi run yang menentukan tugas dengan semua parameter yang diperlukan untuk dijalankan.

Untuk menjalankannya, cari konfigurasi run generateBaselineProfile dan klik tombol Run 229e32fcbe68452f.png.

8f6b7c9a5da6585.png

Pengujian akan membuat image emulator yang ditentukan sebelumnya, menjalankan interaksi beberapa kali, lalu membongkar emulator dan memberikan output ke Android Studio.

4b5b2d0091b4518c.png

(Opsional) Menjalankan generator dari command line

Untuk menjalankan generator dari command line, Anda dapat memanfaatkan tugas yang dibuat oleh Perangkat yang Dikelola Gradle – :macrobenchmark:pixel2Api31BenchmarkAndroidTest.

Perintah ini menjalankan semua pengujian dalam project, yang akan gagal, karena modul juga berisi Benchmark untuk verifikasi peningkatan performa di lain waktu.

Untuk itu, Anda dapat memfilter class yang ingin dijalankan dengan parameter -P android.testInstrumentationRunnerArguments.class dan menentukan com.example.macrobenchmark.BaselineProfileGenerator yang Anda tulis sebelumnya.

Seluruh perintah terlihat seperti berikut:

./gradlew :macrobenchmark:pixel2Api31BenchmarkAndroidTest -P android.testInstrumentationRunnerArguments.class=com.example.macrobenchmark.BaselineProfileGenerator

8. Menerapkan Profil Dasar Pengukuran yang dihasilkan

Setelah generator berhasil diselesaikan, Anda perlu melakukan beberapa hal agar Profil Dasar Pengukuran berfungsi dengan aplikasi Anda.

Anda perlu menempatkan file Profil Dasar Pengukuran yang dihasilkan ke dalam folder src/main (di sebelah AndroidManifest.xml). Untuk mengambil file tersebut, Anda dapat menyalinnya dari folder managed_device_android_test_additional_output/ yang terletak di /macrobenchmark/build/outputs/, seperti yang ditunjukkan pada screenshot berikut.

b104f315f06b3578.png

Atau, Anda dapat mengklik link results di output Android Studio dan menyimpan konten, atau ​​gunakan perintah adb pull yang dicetak di output.

Selanjutnya, ganti nama file menjadi baseline-prof.txt.

8973f012921669f6.png

Kemudian, tambahkan dependensi profileinstaller ke modul :app Anda.

dependencies {
  implementation("androidx.profileinstaller:profileinstaller:1.2.0-rc01")
}

Menambahkan dependensi ini memungkinkan Anda untuk:

  • Menjalankan benchmark secara lokal pada Profil Dasar Pengukuran.
  • Menggunakan Profil Dasar Pengukuran di Android 7 (API level 24) dan Android 8 (API level 26), yang tidak mendukung profil Cloud.
  • Menggunakan Profil Dasar Pengukuran di perangkat yang tidak memiliki Layanan Google Play.

Terakhir, sinkronkan project dengan File Gradle dengan mengklik ikon 1079605eb7639c75.png.

40cb2ba3d0b88dd6.png

Pada langkah berikutnya, kita akan melihat cara memverifikasi seberapa baik performa aplikasi dengan Profil Dasar Pengukuran.

9. Memverifikasi peningkatan performa startup

Sekarang, kita telah membuat Profil Dasar Pengukuran dan menambahkannya ke aplikasi. Mari kita verifikasi bahwa Profil Dasar Pengukuran ini memiliki efek yang diinginkan pada performa aplikasi.

Mari kita kembali ke class ExampleStartupBenchmark yang berisi benchmark untuk mengukur startup aplikasi. Anda perlu sedikit mengubah pengujian startup() agar dapat digunakan kembali dengan berbagai mode kompilasi. Ini akan memungkinkan Anda membandingkan perbedaan saat menggunakan Profil Dasar Pengukuran.

CompilationMode

Parameter CompilationMode menentukan cara aplikasi dikompilasi sebelumnya ke dalam kode mesin. Parameter ini memiliki opsi berikut:

  • DEFAULT – Mengompilasi sebagian aplikasi terlebih dahulu menggunakan Profil Dasar Pengukuran jika tersedia (ini digunakan jika tidak ada parameter compilationMode yang diterapkan)
  • None() – Mereset status kompilasi aplikasi dan tidak mengompilasi aplikasi terlebih dahulu. Kompilasi tepat waktu (JIT) masih diaktifkan selama eksekusi aplikasi.
  • Partial() – Mengompilasi aplikasi terlebih dahulu dengan Profil Dasar Pengukuran dan/atau menjalankan pemanasan.
  • Full() – Mengompilasi seluruh kode aplikasi terlebih dahulu. Ini adalah satu-satunya opsi di Android 6 (API 23) dan yang lebih rendah.

Jika Anda ingin mulai mengoptimalkan performa aplikasi, Anda dapat memilih mode kompilasi DEFAULT, karena performanya akan mirip dengan saat aplikasi diinstal dari Google Play. Jika ingin membandingkan manfaat performa yang diberikan Profil Dasar Pengukuran, Anda dapat melakukannya dengan membandingkan hasil mode kompilasi None dan Partial.

Mengubah pengujian startup dengan CompilationMode yang berbeda

Pertama, hapus anotasi @Test dari metode startup (karena pengujian JUnit tidak dapat memiliki parameter) dan tambahkan parameter compilationMode serta gunakan dalam fungsi measureRepeated:

// Remove @Test annotation and add the compilationMode parameter.
fun startup(compilationMode: CompilationMode) = benchmarkRule.measureRepeated(
   packageName = "com.google.samples.apps.sunflower",
   metrics = listOf(StartupTimingMetric()),
   iterations = 5,
   compilationMode = compilationMode, // Set the compilation mode
   startupMode = StartupMode.COLD
) {
   pressHome()
   startActivityAndWait()
}

Sekarang setelah Anda memilikinya, tambahkan dua fungsi pengujian dengan CompilationMode yang berbeda. Yang pertama akan menggunakan CompilationMode.None, yang berarti bahwa sebelum setiap benchmark, status aplikasi akan direset dan aplikasi tidak akan memiliki kode yang telah dikompilasi sebelumnya.

@Test
fun startupCompilationNone() = startup(CompilationMode.None())

Pengujian kedua akan memanfaatkan CompilationMode.Partial, yang memuat Profil Dasar Pengukuran dan mengompilasi terlebih dahulu class serta fungsi yang ditentukan dari profil sebelum menjalankan benchmark.

@Test
fun startupCompilationPartial() = startup(CompilationMode.Partial())

Secara opsional, Anda dapat menambahkan metode ketiga yang akan mengompilasi seluruh aplikasi terlebih dahulu menggunakan CompilationMode.Full. Ini adalah satu-satunya opsi di Android 6 (API 23) atau yang lebih rendah, karena sistem hanya menjalankan aplikasi yang telah sepenuhnya dikompilasi sebelumnya.

@Test
fun startupCompilationFull() = startup(CompilationMode.Full())

Selanjutnya, jalankan benchmark seperti yang Anda lakukan sebelumnya (pada perangkat fisik) dan tunggu Macrobenchmark untuk mengukur waktu startup dengan mode kompilasi yang berbeda.

Setelah benchmark selesai, Anda dapat melihat pengaturan waktu di output Android Studio seperti di screenshot berikut:

cbbc9660374a438.png

Dari screenshot, Anda dapat melihat bahwa waktu startup aplikasi berbeda untuk setiap CompilationMode. Nilai median ditampilkan dalam tabel berikut:

timeToInitialDisplay [ms]

timeToFullDisplay [ms]

Tidak ada

364.4

846.5

Penuh

325.8

739.1

Parsial

296.1

708.1

Secara intuitif, kompilasi None memiliki performa terburuk karena perangkat harus melakukan kompilasi JIT paling banyak selama startup aplikasi. Hal yang mungkin berlawanan dengan harapan adalah kompilasi Full tidak berperforma terbaik. Karena semuanya dikompilasi dalam kasus ini, file odex aplikasi sangat besar, dan oleh karena itu, sistem biasanya harus melakukan IO secara signifikan selama startup aplikasi. Performa terbaik ditunjukkan oleh kasus Partial yang menggunakan Profil Dasar Pengukuran. Hal ini karena kompilasi parsial mencapai keseimbangan antara mengompilasi kode yang kemungkinan besar akan digunakan pengguna, tetapi menyebabkan kode yang tidak begitu penting tidak dikompilasi sebelumnya sehingga tidak perlu dimuat seketika.

10. Memverifikasi performa scroll

Demikian pula dengan apa yang Anda lakukan di langkah sebelumnya, Anda dapat mengukur dan memverifikasi benchmark scroll. Mari kita ubah class ScrollBenchmarks seperti sebelumnya – menambahkan parameter ke pengujian scroll dan menambahkan pengujian lainnya dengan parameter mode kompilasi yang berbeda.

Buka file ScrollBenchmarks.kt, ubah fungsi scroll() untuk menambahkan parameter compilationMode:

fun scroll(compilationMode: CompilationMode) {
        benchmarkRule.measureRepeated(
            compilationMode = compilationMode, // Set the compilation mode
            // ...

Sekarang, tentukan beberapa pengujian yang menggunakan parameter berbeda:

@Test
fun scrollCompilationNone() = scroll(CompilationMode.None())

@Test
fun scrollCompilationPartial() = scroll(CompilationMode.Partial())

Dan jalankan benchmark seperti sebelumnya untuk mendapatkan hasil seperti dalam screenshot berikut: 249af52e917a4fcf.png

Dari hasil tersebut, Anda dapat melihat bahwa CompilationMode.Partial memiliki waktu render frame yang rata-rata lebih pendek sebesar 0,5 md, yang mungkin tidak terlihat oleh pengguna, tetapi untuk persentil lainnya, hasilnya lebih jelas. Untuk P90, perbedaannya adalah 11,7 md, yaitu ~70% dari waktu yang ditentukan untuk menghasilkan frame (di Pixel 3, yaitu ~16 md).

11. Selamat

Selamat, Anda berhasil menyelesaikan codelab ini dan meningkatkan performa aplikasi Anda dengan menggunakan Profil Dasar Pengukuran.

Apa selanjutnya?

Lihat repositori GitHub contoh performa kami yang berisi Macrobenchmark dan contoh performa lainnya. Selain itu, periksa aplikasi contoh Now In Android – aplikasi dunia nyata yang menggunakan benchmark dan Profil Dasar Pengukuran untuk meningkatkan performa.

Dokumen referensi