Men-debug Profil Dasar Pengukuran

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:

Menambahkan konfigurasi run kustom untuk membuat Profil Dasar Pengukuran di Now in Android
Gambar 1. Tambahkan konfigurasi run kustom untuk membuat Profil Baseline di Now in Android.

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:

Memeriksa Profil Dasar Pengukuran menggunakan APK Viewer di Android Studio
Gambar 2. Memeriksa Profil Dasar Pengukuran menggunakan APK Viewer di Android Studio.

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 adalah RESULT_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
ProfileVerifieris 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 kueri PackageManager 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.