Dokumen ini menunjukkan praktik terbaik untuk membantu mendiagnosis masalah dan memastikan Profil Dasar Pengukuran Anda berfungsi dengan benar agar dapat memberikan manfaat maksimal.
Masalah build
Jika telah menyalin contoh Profil Dasar Pengukuran dalam aplikasi contoh Now in Android, Anda mungkin mengalami kegagalan pengujian selama tugas Profil Dasar Pengukuran yang menyatakan bahwa pengujian tidak dapat dijalankan di emulator:
./gradlew assembleDemoRelease
Starting a Gradle Daemon (subsequent builds will be faster)
Calculating task graph as no configuration cache is available for tasks: assembleDemoRelease
Type-safe project accessors is an incubating feature.
> Task :benchmarks:pixel6Api33DemoNonMinifiedReleaseAndroidTest
Starting 14 tests on pixel6Api33
com.google.samples.apps.nowinandroid.foryou.ScrollForYouFeedBenchmark > scrollFeedCompilationNone[pixel6Api33] FAILED
java.lang.AssertionError: ERRORS (not suppressed): EMULATOR
WARNINGS (suppressed):
...
Kegagalan terjadi karena Now in Android menggunakan perangkat yang dikelola Gradle untuk pembuatan Profil Dasar Pengukuran. Kegagalan sudah diperkirakan karena Anda pada umumnya tidak boleh menjalankan benchmark performa di emulator. Namun, karena Anda tidak mengumpulkan metrik performa saat membuat Profil Dasar Pengukuran, Anda dapat menjalankan pengumpulan Profil Dasar Pengukuran di emulator untuk memudahkan. Untuk menggunakan Profil Dasar Pengukuran dengan emulator, lakukan build dan penginstalan dari command line, serta tetapkan argumen untuk mengaktifkan aturan Profil Dasar Pengukuran:
installDemoRelease -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile
Bisa juga dengan membuat konfigurasi run kustom di Android Studio untuk mengaktifkan Profil Dasar Pengukuran pada emulator dengan memilih Jalankan > Edit Konfigurasi:
Masalah penginstalan
Pastikan APK atau AAB yang Anda bangun berasal dari varian build yang menyertakan
Profil Dasar Pengukuran. Cara termudah untuk memeriksanya adalah dengan membuka APK di
Android Studio dengan memilihBuild > Analisis APK, membuka
APK, dan mencari profil di file
/assets/dexopt/baseline.prof
:
Profil Dasar Pengukuran harus dikompilasi di perangkat yang menjalankan aplikasi. Untuk
penginstalan app store dan aplikasi yang diinstal menggunakan
PackageInstaller
, kompilasi di perangkat dilakukan sebagai bagian dari proses
penginstalan aplikasi. Namun, saat aplikasi di-sideload dari Android Studio, atau
menggunakan alat command line, library Jetpack ProfileInstaller
bertanggung jawab untuk mengantrekan profil untuk kompilasi selama
proses pengoptimalan DEX latar belakang berikutnya. Dalam kasus tersebut, jika ingin memastikan
Profil Dasar Pengukuran digunakan, Anda mungkin perlu
memaksa kompilasi Profil Dasar Pengukuran. ProfileVerifier
memungkinkan
Anda membuat kueri status penginstalan dan kompilasi profil, seperti yang ditunjukkan pada
contoh berikut:
Kotlin
private const val TAG = "MainActivity" class MainActivity : ComponentActivity() { ... override fun onResume() { super.onResume() lifecycleScope.launch { logCompilationStatus() } } private suspend fun logCompilationStatus() { withContext(Dispatchers.IO) { val status = ProfileVerifier.getCompilationStatusAsync().await() when (status.profileInstallResultCode) { RESULT_CODE_NO_PROFILE -> Log.d(TAG, "ProfileInstaller: Baseline Profile not found") RESULT_CODE_COMPILED_WITH_PROFILE -> Log.d(TAG, "ProfileInstaller: Compiled with profile") RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION -> Log.d(TAG, "ProfileInstaller: Enqueued for compilation") RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING -> Log.d(TAG, "ProfileInstaller: App was installed through Play store") RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST -> Log.d(TAG, "ProfileInstaller: PackageName not found") RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ -> Log.d(TAG, "ProfileInstaller: Cache file exists but cannot be read") RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE -> Log.d(TAG, "ProfileInstaller: Can't write cache file") RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION -> Log.d(TAG, "ProfileInstaller: Enqueued for compilation") else -> Log.d(TAG, "ProfileInstaller: Profile not compiled or enqueued") } } }
Java
public class MainActivity extends ComponentActivity { private static final String TAG = "MainActivity"; @Override protected void onResume() { super.onResume(); logCompilationStatus(); } private void logCompilationStatus() { ListeningExecutorService service = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor()); ListenableFuture<ProfileVerifier.CompilationStatus> future = ProfileVerifier.getCompilationStatusAsync(); Futures.addCallback(future, new FutureCallback<>() { @Override public void onSuccess(CompilationStatus result) { int resultCode = result.getProfileInstallResultCode(); if (resultCode == RESULT_CODE_NO_PROFILE) { Log.d(TAG, "ProfileInstaller: Baseline Profile not found"); } else if (resultCode == RESULT_CODE_COMPILED_WITH_PROFILE) { Log.d(TAG, "ProfileInstaller: Compiled with profile"); } else if (resultCode == RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION) { Log.d(TAG, "ProfileInstaller: Enqueued for compilation"); } else if (resultCode == RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING) { Log.d(TAG, "ProfileInstaller: App was installed through Play store"); } else if (resultCode == RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST) { Log.d(TAG, "ProfileInstaller: PackageName not found"); } else if (resultCode == RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ) { Log.d(TAG, "ProfileInstaller: Cache file exists but cannot be read"); } else if (resultCode == RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE) { Log.d(TAG, "ProfileInstaller: Can't write cache file"); } else if (resultCode == RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION) { Log.d(TAG, "ProfileInstaller: Enqueued for compilation"); } else { Log.d(TAG, "ProfileInstaller: Profile not compiled or enqueued"); } } @Override public void onFailure(Throwable t) { Log.d(TAG, "ProfileInstaller: Error getting installation status: " + t.getMessage()); } }, service); } }
Kode hasil berikut memberikan petunjuk penyebab beberapa masalah:
RESULT_CODE_COMPILED_WITH_PROFILE
- Profil diinstal, dikompilasi, dan digunakan setiap kali aplikasi dijalankan. Code ini merupakan hasil yang ingin Anda lihat.
RESULT_CODE_ERROR_NO_PROFILE_EMBEDDED
- Tidak ada profil yang ditemukan dalam APK atau AAB yang dijalankan. Pastikan Anda menggunakan varian build yang menyertakan Profil Dasar Pengukuran jika Anda melihat error ini, dan APK berisi profil.
RESULT_CODE_NO_PROFILE
- Tidak ada profil yang diinstal untuk aplikasi ini saat menginstal aplikasi melalui app
store atau pengelola paket. Alasan utama terjadinya kode error ini adalah penginstal
profil tidak berjalan karena
ProfileInstallerInitializer
dinonaktifkan. Perlu diperhatikan bahwa saat error ini dilaporkan, profil tersemat masih ditemukan dalam APK aplikasi. Jika profil tersemat tidak ditemukan, kode error yang ditampilkan adalahRESULT_CODE_ERROR_NO_PROFILE_EMBEDDED
. RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION
- Profil ditemukan di APK atau AAB lalu diantrekan untuk kompilasi. Jika
diinstal oleh
ProfileInstaller
, profil tersebut akan diantrekan untuk kompilasi saat pengoptimalan DEX latar belakang berikutnya akan dijalankan oleh sistem. Profil tidak akan aktif hingga kompilasi selesai. Jangan mencoba menjalankan benchmark Profil Dasar Pengukuran Anda hingga kompilasi selesai. Anda mungkin perlu memaksa kompilasi Profil Dasar Pengukuran. Error ini tidak akan terjadi jika aplikasi diinstal dari app store atau pengelola paket di perangkat yang menjalankan Android 9 (API 28) dan yang lebih baru karena kompilasi dilakukan selama penginstalan. RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING
- Profil yang tidak cocok akan diinstal dan aplikasi telah dikompilasi menggunakan profil tersebut.
Kode ini merupakan hasil penginstalan melalui Google Play Store atau pengelola paket.
Perhatikan bahwa hasil ini berbeda dengan
RESULT_CODE_COMPILED_WITH_PROFILE
karena profil yang tidak cocok hanya akan mengompilasi metode yang masih dibagikan antara profil dan aplikasi. Profil ini sebenarnya lebih kecil dari yang diharapkan, dan lebih sedikit metode yang akan dikompilasi dibandingkan metode yang disertakan dalam Profil Dasar Pengukuran. RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE
ProfileVerifier
tidak dapat menulis file cache hasil verifikasi. Hal ini dapat terjadi karena ada masalah dengan izin folder aplikasi atau jika kapasitas disk kosong di perangkat tidak cukup.RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION
- ProfileVerifier
is running on an unsupported API version of Android. ProfileVerifier
hanya mendukung Android 9 (level API 28) dan yang lebih tinggi. RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST
PackageManager.NameNotFoundException
ditampilkan saat membuat kueriPackageManager
untuk paket aplikasi. Hal ini biasanya jarang terjadi. Coba uninstal aplikasi lalu instal ulang semuanya.RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ
- File cache hasil verifikasi sebelumnya ada, tetapi tidak dapat dibaca. Hal ini biasanya jarang terjadi. Coba uninstal aplikasi lalu instal ulang semuanya.
Menggunakan ProfileVerifier dalam produksi
Dalam produksi, Anda dapat menggunakan ProfileVerifier
bersama dengan
library pelaporan analisis, seperti Google Analytics for Firebase, untuk
menghasilkan peristiwa analisis yang menunjukkan status profil. Misalnya, pemberitahuan ini
akan segera dikirimkan kepada Anda jika versi aplikasi baru yang tidak berisi
Profil Dasar Pengukuran dirilis.
Memaksa kompilasi Profil Dasar Pengukuran
Jika status kompilasi Profil Dasar Pengukuran Anda adalah
RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION
, Anda dapat memaksa kompilasi
langsung menggunakan adb
:
adb shell cmd package compile -r bg-dexopt PACKAGE_NAME
Memeriksa status kompilasi tanpa ProfileVerifier
Jika tidak menggunakan ProfileVerifier
, Anda dapat memeriksa status kompilasi menggunakan
adb
meskipun tidak memberikan insight sedalam ProfileVerifier
:
adb shell dumpsys package dexopt | grep -A 2 PACKAGE_NAME
Penggunaan adb
akan menghasilkan sesuatu yang mirip dengan berikut ini:
[com.google.samples.apps.nowinandroid.demo]
path: /data/app/~~dzJiGMKvp22vi2SsvfjkrQ==/com.google.samples.apps.nowinandroid.demo-7FR1sdJ8ZTy7eCLwAnn0Vg==/base.apk
arm64: [status=speed-profile] [reason=bg-dexopt] [primary-abi]
[location is /data/app/~~dzJiGMKvp22vi2SsvfjkrQ==/com.google.samples.apps.nowinandroid.demo-7FR1sdJ8ZTy7eCLwAnn0Vg==/oat/arm64/base.odex]
Nilai status menunjukkan status kompilasi profil dan merupakan salah satu nilai berikut:
Status kompilasi | Arti |
---|---|
speed‑profile |
Profil yang dikompilasi ada dan sedang digunakan. |
verify |
Tidak ada profil yang dikompilasi. |
Status verify
tidak berarti bahwa APK atau AAB tidak berisi profil,
karena dapat diantrekan untuk kompilasi oleh tugas pengoptimalan DEX latar belakang
berikutnya.
Nilai alasan menunjukkan faktor pemicu kompilasi profil dan merupakan salah satu dari nilai berikut:
Alasan | Arti |
---|---|
install‑dm
|
Profil Dasar Pengukuran dikompilasi secara manual atau oleh Google Play saat aplikasi diinstal. |
bg‑dexopt
|
Profil dikompilasi saat perangkat Anda tidak ada aktivitas. Profil ini mungkin Profil Dasar Pengukuran, atau mungkin profil yang dikumpulkan selama penggunaan aplikasi. |
cmdline
|
Kompilasi dipicu menggunakan adb. Profil ini mungkin Profil Dasar Pengukuran, atau mungkin profil yang dikumpulkan selama penggunaan aplikasi. |
Masalah performa
Bagian ini menunjukkan beberapa praktik terbaik untuk menentukan dan menjalankan benchmark Profil Dasar Pengukuran Anda dengan benar agar dapat memperoleh manfaat maksimal.
Melakukan benchmark metrik startup dengan benar
Profil Dasar Pengukuran Anda akan lebih efektif jika metrik startup ditentukan dengan baik. Dua metrik utama adalah waktu hingga tampilan awal (TTID) dan waktu hingga tampilan penuh (TTFD).
TTID adalah saat aplikasi menggambar frame pertamanya. Penting untuk membuatnya sesingkat mungkin karena menampilkan sesuatu akan menunjukkan kepada pengguna bahwa aplikasi sedang berjalan. Anda bahkan dapat menampilkan indikator progres yang tidak tentu untuk menunjukkan bahwa aplikasi responsif.
TTFD adalah saat aplikasi benar-benar dapat berinteraksi. Sangat penting untuk menjaganya sesingkat mungkin agar pengguna tidak frustrasi. Jika Anda memberikan sinyal TTFD dengan benar, berarti Anda memberi tahu sistem bahwa kode yang dijalankan dalam proses ke TTFD adalah bagian dari startup aplikasi. Akibatnya, sistem kemungkinan besar akan menempatkan kode ini di dalam profil.
Pastikan TTID dan TTFD serendah mungkin agar aplikasi Anda terasa responsif.
Sistem dapat mendeteksi TTID, menampilkannya di Logcat, serta melaporkannya sebagai bagian
dari benchmark startup. Namun, sistem tidak dapat menentukan TTFD, dan
aplikasi bertanggung jawab untuk melaporkan saat aplikasi mencapai status interaktif
yang sepenuhnya digambar. Anda dapat melakukannya dengan memanggil reportFullyDrawn()
, atau
ReportDrawn
jika menggunakan Jetpack Compose. Jika Anda memiliki beberapa
tugas latar belakang yang semuanya harus diselesaikan sebelum aplikasi dianggap telah sepenuhnya
digambar, Anda dapat menggunakan FullyDrawnReporter
seperti dijelaskan di Meningkatkan
akurasi pengaturan waktu startup.
Profil library dan profil kustom
Saat menjalankan benchmark dampak profil, mungkin sulit untuk memisahkan manfaat profil aplikasi Anda dari profil yang dikontribusikan oleh library, seperti library Jetpack. Saat Anda mem-build APK, plugin Android Gradle akan menambahkan profil apa pun dalam dependensi library serta profil kustom Anda. Cara ini bagus untuk mengoptimalkan performa secara keseluruhan, dan direkomendasikan untuk build rilis Anda. Namun, sulit untuk mengukur jumlah peningkatan performa tambahan yang berasal dari profil kustom Anda.
Cara cepat untuk melihat pengoptimalan tambahan yang disediakan oleh profil kustom Anda secara manual adalah dengan menghapusnya, lalu menjalankan benchmark Anda. Kemudian, ganti dan jalankan benchmark lagi. Membandingkan keduanya akan menunjukkan pengoptimalan yang disediakan oleh profil library saja, serta profil library dan profil kustom Anda.
Cara yang dapat diotomatiskan untuk membandingkan profil adalah dengan membuat varian build baru yang
hanya berisi profil library, bukan profil kustom Anda. Bandingkan
benchmark dari varian ini dengan varian rilis yang berisi
profil library dan profil kustom Anda. Contoh berikut menunjukkan cara
menyiapkan varian yang hanya menyertakan profil library. Tambahkan varian baru
bernama releaseWithoutCustomProfile
ke modul konsumen profil, yang
biasanya merupakan modul aplikasi Anda:
Kotlin
android { ... buildTypes { ... // Release build with only library profiles. create("releaseWithoutCustomProfile") { initWith(release) } ... } ... } ... dependencies { ... // Remove the baselineProfile dependency. // baselineProfile(project(":baselineprofile")) } baselineProfile { variants { create("release") { from(project(":baselineprofile")) } } }
Groovy
android { ... buildTypes { ... // Release build with only library profiles. releaseWithoutCustomProfile { initWith(release) } ... } ... } ... dependencies { ... // Remove the baselineProfile dependency. // baselineProfile ':baselineprofile"' } baselineProfile { variants { release { from(project(":baselineprofile")) } } }
Contoh kode sebelumnya menghapus dependensi baselineProfile
dari semua
varian dan secara selektif menerapkannya hanya ke varian release
. Mungkin
tampak kontra-intuitif bahwa profil library masih ditambahkan saat
dependensi pada modul pembuat profil dihapus. Namun, modul ini
hanya bertanggung jawab untuk membuat profil kustom Anda. Plugin Android Gradle
masih berjalan untuk semua varian, dan bertanggung jawab untuk menyertakan
profil library.
Anda juga perlu menambahkan varian baru ke modul generator profil. Dalam contoh
ini, modul produser diberi nama :baselineprofile
.
Kotlin
android { ... buildTypes { ... // Release build with only library profiles. create("releaseWithoutCustomProfile") {} ... } ... }
Groovy
android { ... buildTypes { ... // Release build with only library profiles. releaseWithoutCustomProfile {} ... } ... }
Untuk menjalankan benchmark dengan profil library saja, jalankan perintah berikut:
./gradlew :baselineprofile:connectedBenchmarkReleaseWithoutCustomProfileAndroidTest
Untuk menjalankan benchmark dengan profil library dan profil kustom Anda, jalankan hal berikut:
./gradlew :baselineprofile:connectedBenchmarkReleaseAndroidTest
Menjalankan kode sebelumnya pada aplikasi contoh Macrobenchmark menunjukkan
adanya perbedaan performa antara kedua varian. Hanya dengan profil
library, benchmark startupCompose
warm akan menunjukkan hasil berikut:
SmallListStartupBenchmark_startupCompose[mode=COLD]
timeToInitialDisplayMs min 70.8, median 79.1, max 126.0
Traces: Iteration 0 1 2 3 4 5 6 7 8 9
Ada profil library di banyak library Jetpack Compose, sehingga ada beberapa pengoptimalan hanya dengan menggunakan plugin Gradle Profil Dasar Pengukuran. Namun, ada pengoptimalan tambahan saat menggunakan profil kustom:
SmallListStartupBenchmark_startupCompose[mode=COLD]
timeToInitialDisplayMs min 57.9, median 73.5, max 92.3
Traces: Iteration 0 1 2 3 4 5 6 7 8 9
Menghindari startup aplikasi yang terikat I/O
Jika aplikasi Anda melakukan banyak panggilan I/O atau panggilan jaringan selama startup, hal ini dapat berdampak negatif pada waktu startup aplikasi dan akurasi benchmark startup Anda. Panggilan level tinggi ini dapat memerlukan waktu yang tidak tentu yang dapat bervariasi dari waktu ke waktu, bahkan di antara iterasi dari benchmark yang sama. Panggilan I/O umumnya lebih baik daripada panggilan jaringan karena panggilan jaringan dapat dipengaruhi oleh faktor-faktor eksternal dan internal perangkat. Hindari panggilan jaringan selama startup. Jika salah satunya tidak dapat dihindari, gunakan I/O.
Sebaiknya buat arsitektur aplikasi Anda mendukung startup aplikasi tanpa panggilan jaringan atau I/O meskipun hanya untuk menggunakannya saat menjalankan benchmark. Hal ini membantu memastikan variabilitas serendah mungkin antara berbagai iterasi benchmark Anda.
Jika aplikasi menggunakan Hilt, Anda dapat menyediakan implementasi terikat I/O palsu saat menjalankan benchmark di Microbenchmark dan Hilt.
Membahas semua perjalanan penting pengguna
Penting untuk mencakup semua perjalanan pengguna yang penting secara akurat dalam pembuatan Profil Dasar Pengukuran Anda. Setiap perjalanan pengguna yang tidak tercakup tidak akan ditingkatkan oleh Profil Dasar Pengukuran. Profil dasar pengukuran yang paling efektif mencakup semua perjalanan pengguna startup umum serta perjalanan pengguna dalam aplikasi yang sensitif terhadap performa seperti daftar scroll.