Temel Profillerde Hata Ayıklama

Bu dokümanda, sorunları teşhis etmenize ve temel profillerinizin en iyi şekilde fayda sağlayabilmesi için düzgün çalışmasını sağlamanıza yardımcı olacak en iyi uygulamalar gösterilmektedir.

Derleme sorunları

Android'de örnek uygulamasındaki referans profil örneğini kopyaladıysanız referans profil görevi sırasında testlerin bir emülatörde çalıştırılamayacağını belirten test hatalarıyla karşılaşabilirsiniz:

./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):
        ...

Başarısızlıklar, Android'de Şimdi'nin referans profili oluşturmak için Gradle tarafından yönetilen bir cihaz kullanmasından kaynaklanır. Genellikle performans karşılaştırmalarını bir emülatörde çalıştırmayacağınız için bu tür hatalar beklenir. Ancak, temel profil oluştururken performans metrikleri toplamayacağınız için kolaylık sağlamak amacıyla temel profil toplama işlemini emülatörlerde çalıştırabilirsiniz. Temel profilleri bir emülatörle kullanmak için derlemeyi ve yüklemeyi komut satırından gerçekleştirin ve temel profil kurallarını etkinleştirmek için bir bağımsız değişken ayarlayın:

installDemoRelease -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile

Alternatif olarak, Çalıştır > Yapılandırmaları Düzenle'yi seçerek Android Studio'da özel bir çalıştırma yapılandırması oluşturarak temel profilleri emülatörlerde etkinleştirebilirsiniz:

Android'deki Now'da referans profilleri oluşturmak için özel bir çalıştırma yapılandırması ekleme
Şekil 1. Android'de Şimdi'de referans profiller oluşturmak için özel bir çalıştırma yapılandırması ekleyin.

Yükleme sorunları

İncelediğiniz APK veya AAB'nin, temel profilleri içeren bir derleme varyantından geldiğini kontrol edin:

  1. Android Studio'da Derle > APK'yı Analiz Et'i seçin.
  2. AAB'nizi veya APK'nızı açın.
  3. Bir AAB'yi inceliyorsanız profil /BUNDLE-METADATA/com.android.tools.build.profiles/baseline.prof adresindedir. Bir APK'yı inceliyorsanız profil /assets/dexopt/baseline.prof adresindedir.
Android Studio'da APK Görüntüleyici'yi kullanarak temel profil olup olmadığını kontrol etme
Şekil 2. Android Studio'da APK Görüntüleyici'yi kullanarak referans profil olup olmadığını kontrol edin.

Temel profillerin, uygulamanın çalıştığı cihazda derlenmesi gerekir. Uygulamayı Play Store, Android Studio veya Gradle Wrapper komut satırı aracını kullanarak yüklediğinizde cihaz üzerinde derleme otomatik olarak gerçekleşir. Uygulama diğer araçlar kullanılarak yüklendiğinde, sonraki arka plan DEX optimizasyon işlemi sırasında profillerin derleme için sıraya eklenmesi Jetpack ProfileInstaller kitaplığının sorumluluğundadır. Bu gibi durumlarda, temel profillerinizin kullanılmasını sağlamak istiyorsanız temel profillerin derlenmesini zorlamanız gerekebilir. ProfileVerifier, aşağıdaki örnekte gösterildiği gibi profil yükleme ve derleme durumunu sorgulamanıza olanak tanır:

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

Aşağıdaki sonuç kodları, bazı sorunların nedenine dair ipuçları sağlar:

RESULT_CODE_COMPILED_WITH_PROFILE
Profil yüklenir, derlenir ve uygulama her çalıştırıldığında kullanılır. Bu, görmek istediğiniz sonuçtur.
RESULT_CODE_ERROR_NO_PROFILE_EMBEDDED
Çalışılan APK'da profil bulunamadı. Bu hatayı görüyorsanız temel profiller içeren bir derleme varyantı kullandığınızdan ve APK'nın bir profil içerdiğinden emin olun.
RESULT_CODE_NO_PROFILE
Uygulama mağazası veya paket yöneticisi üzerinden yüklenirken bu uygulama için profil yüklenmedi. Bu hata kodunun başlıca nedeni, ProfileInstallerInitializer devre dışı bırakıldığı için profil yükleyicinin çalışmamasıdır. Bu hata bildirildiğinde uygulama APK'sında hâlâ yerleşik bir profil bulunduğunu unutmayın. Yerleştirilmiş bir profil bulunmadığında döndürülen hata kodu RESULT_CODE_ERROR_NO_PROFILE_EMBEDDED'tür.
RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION
APK veya AAB'da bir profil bulunur ve derleme için sıraya eklenir. Bir profil ProfileInstaller tarafından yüklendiğinde, sistem tarafından arka planda DEX optimizasyonu çalıştırıldığında derleme için sıraya alınır. Derleme tamamlanana kadar profil etkin olmaz. Derleme tamamlanana kadar referans profillerinizi karşılaştırmaya çalışmayın. Temel profillerin derlenmesini zorlamanız gerekebilir. Derleme işlemi yükleme sırasında yapıldığından, uygulama Android 9 (API 28) ve sonraki sürümleri çalıştıran cihazlarda uygulama mağazasından veya paket yöneticisinden yüklendiğinde bu hata oluşmaz.
RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING
Eşleşmeyen bir profil yüklenmiş ve uygulama bu profille derlenmiştir. Bu, Google Play Store veya paket yöneticisi üzerinden yüklemenin sonucudur. Eşleşmeyen profil yalnızca profil ile uygulama arasında hâlâ paylaşılan yöntemleri derleyeceğinden bu sonucun RESULT_CODE_COMPILED_WITH_PROFILE ile farklı olduğunu unutmayın. Profil, beklenenden daha küçüktür ve referans profilde yer alanlardan daha az yöntem derlenir.
RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE
ProfileVerifier, doğrulama sonucu önbelleği dosyasını yazamıyor. Bu durum, uygulama klasör izinleriyle ilgili bir sorun olduğunda veya cihazda yeterli boş disk alanı olmadığında ortaya çıkabilir.
RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION
ProfileVerifieris running on an unsupported API version of Android. ProfileVerifier yalnızca Android 9 (API düzeyi 28) ve sonraki sürümleri destekler.
RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST
Uygulama paketi için PackageManager sorgulanırken PackageManager.NameNotFoundException hatası atılır. Bu durum nadiren gerçekleşir. Uygulamayı kaldırıp her şeyi yeniden yüklemeyi deneyin.
RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ
Önceki bir doğrulama sonucu önbelleği dosyası var ancak okunamıyor. Bu durum nadiren gerçekleşir. Uygulamayı kaldırıp her şeyi yeniden yüklemeyi deneyin.

ProfileVerifier'ı üretimde kullanma

Üretimde, profil durumunu belirten analiz etkinlikleri oluşturmak için ProfileVerifierFirebase için Google Analytics gibi analiz raporlama kitaplıklarıyla birlikte kullanabilirsiniz. Örneğin, bu özellik, temel profil içermeyen yeni bir uygulama sürümü yayınlandığında sizi hızlı bir şekilde uyarır.

Referans profillerinin derlenmesini zorunlu kılma

Temel profillerinizin derleme durumu RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION ise adb kullanarak derlemeyi hemen zorlayabilirsiniz:

adb shell cmd package compile -r bg-dexopt PACKAGE_NAME

ProfileVerifier olmadan derleme durumunu kontrol etme

ProfileVerifier kullanmıyorsanız adb'ü kullanarak derleme durumunu kontrol edebilirsiniz. Ancak adb, ProfileVerifier kadar ayrıntılı analizler sunmaz:

adb shell dumpsys package dexopt | grep -A 2 PACKAGE_NAME

adb kullanıldığında aşağıdakine benzer bir sonuç elde edilir:

  [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]

Durum değeri, profil derleme durumunu belirtir ve aşağıdaki değerlerden biridir:

Derleme durumu Anlamı
speed‑profile Derlenmiş bir profil mevcut ve kullanılıyor.
verify Derlenmiş profil yok.

verify durumu, APK veya AAB'nin profil içermediği anlamına gelmez. Çünkü bir sonraki arka plan DEX optimizasyon görevi tarafından derleme için sıraya alınabilir.

Neden değeri, profilin derlenmesini neyin tetiklediğini belirtir ve aşağıdaki değerlerden biridir:

Neden Anlamı
install‑dm Temel Profil, uygulama yüklenirken manuel olarak veya Google Play tarafından derlenmiştir.
bg‑dexopt Cihazınız boştayken bir profil derlendi. Bu, bir referans profili veya uygulama kullanımı sırasında toplanan bir profil olabilir.
cmdline Derleme, adb kullanılarak tetiklendi. Bu, bir referans profili veya uygulama kullanımı sırasında toplanan bir profil olabilir.

Performans sorunları

Bu bölümde, temel profillerinizden en iyi şekilde yararlanmak için bunları doğru şekilde tanımlama ve karşılaştırma ile ilgili bazı en iyi uygulamalar gösterilmektedir.

Başlatma metrikleriyle doğru şekilde karşılaştırma yapma

Başlangıç metrikleriniz iyi tanımlanmışsa referans profilleriniz daha etkili olur. İki temel metrik ilk ekrana görüntülenene kadar geçen süre (TTID) ve tam ekrana görüntülenene kadar geçen süre (TTFD)'dir.

TTID, uygulamanın ilk karesini çizdiği andır. Bir şey göstermek, kullanıcıya uygulamanın çalıştığı bilgisini verdiği için bu işlemi olabildiğince kısa tutmak önemlidir. Hatta uygulamanın duyarlı olduğunu göstermek için belirsiz bir ilerleme durumu göstergesi de gösterebilirsiniz.

TTFD, uygulamayla gerçekten etkileşim kurulabileceği zamandır. Kullanıcıların canını sıkmamak için bu işlemi mümkün olduğunca kısa tutmak önemlidir. TTFD'yi doğru şekilde işaretlerseniz sisteme, TTFD'ye giden yolda çalıştırılan kodun uygulama başlatmanın bir parçası olduğunu bildirirsiniz. Sonuç olarak, sistemin bu kodu profile yerleştirme olasılığı artar.

Uygulamanızın duyarlı olmasını sağlamak için hem TTID hem de TTFD'yi mümkün olduğunca düşük tutun.

Sistem, TTID'yi algılayabilir, Logcat'te görüntüleyebilir ve başlangıç karşılaştırmaları kapsamında raporlayabilir. Ancak sistem, TTFD'yi belirleyemez. Tam olarak çizilmiş etkileşimli bir duruma ulaşıldığında bunu bildirmek uygulamanın sorumluluğundadır. Bunu, reportFullyDrawn()'u çağırarak veya Jetpack Compose kullanıyorsanız ReportDrawn'i çağırarak yapabilirsiniz. Uygulamanın tamamen çizilmiş olarak kabul edilmesi için tümünün tamamlanması gereken birden fazla arka plan göreviniz varsa Başlangıç zamanlaması doğruluğunu iyileştirme bölümünde açıklandığı gibi FullyDrawnReporter kullanabilirsiniz.

Kitaplık profilleri ve özel profiller

Profillerin etkisini karşılaştırırken, uygulamanızın profillerinin avantajlarını Jetpack kitaplıkları gibi kitaplıklar tarafından sağlanan profillerden ayırmak zor olabilir. APK'nızı derlediğinizde Android Gradle eklentisi, kitaplık bağımlılıklarındaki tüm profillerin yanı sıra özel profilinizi de ekler. Bu, genel performansı optimize etmek için iyidir ve sürüm derlemeleri için önerilir. Ancak bu, özel profilinizden ne kadar ek performans kazancı elde ettiğinizi ölçmeyi zorlaştırır.

Özel profilinizin sağladığı ek optimizasyonu manuel olarak görmenin hızlı bir yolu, özel profilinizi kaldırıp karşılaştırmalarınızı çalıştırmaktır. Ardından, pili değiştirip karşılaştırmalarınızı tekrar çalıştırın. İkisini karşılaştırarak kitaplık profillerinin tek başına ve kitaplık profillerinin yanı sıra özel profilinizin sağladığı optimizasyonları görebilirsiniz.

Profilleri karşılaştırmanın otomatik bir yolu, özel profilinizi değil yalnızca kitaplık profillerini içeren yeni bir derleme varyantı oluşturmaktır. Bu varyanttaki karşılaştırmaları, hem kitaplık profillerini hem de özel profillerinizi içeren sürüm varyantıyla karşılaştırın. Aşağıdaki örnekte, yalnızca kitaplık profillerini içeren varyantın nasıl oluşturulacağı gösterilmektedir. Profil tüketici modülünüze (genellikle uygulama modülünüz) releaseWithoutCustomProfile adlı yeni bir varyant ekleyin:

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

Önceki kod örneğinde, baselineProfile bağımlılığı tüm varyantlardan kaldırılır ve yalnızca release varyantına seçici olarak uygulanır. Profil üretici modülüne olan bağımlılık kaldırılmış olsa bile kitaplık profillerinin hâlâ eklenmesi mantıksız görünebilir. Ancak bu modül yalnızca özel profilinizi oluşturmaktan sorumludur. Android Gradle eklentisi tüm varyantlar için çalışmaya devam eder ve kitaplık profillerini dahil etmekten sorumludur.

Yeni varyantı profil oluşturucu modülüne de eklemeniz gerekir. Bu örnekte yapımcı modülü :baselineprofile olarak adlandırılmıştır.

Kotlin

android {
  ...
    buildTypes {
      ...
      // Release build with only library profiles.
      create("releaseWithoutCustomProfile") {}
      ...
    }
  ...
}

Groovy

android {
  ...
    buildTypes {
      ...
      // Release build with only library profiles.
      releaseWithoutCustomProfile {}
      ...
    }
  ...
}

Android Studio'dan karşılaştırmayı çalıştırırken performansı yalnızca kitaplık profilleriyle ölçmek için bir releaseWithoutCustomProfile varyantı veya performansı kitaplık ve özel profillerle ölçmek için bir release varyantı seçin.

G/Ç'ye bağlı uygulama başlatmayı önleyin

Uygulamanız, başlangıç sırasında çok sayıda G/Ç çağrısı veya ağ çağrısı yapıyorsa bu durum hem uygulamanın başlangıç süresini hem de başlangıç karşılaştırmasının doğruluğunu olumsuz yönde etkileyebilir. Bu ağır çağrılar, zaman içinde ve hatta aynı karşılaştırmanın iterasyonları arasında değişebilen belirsiz bir süre alabilir. G/Ç çağrıları, cihazın dışındaki ve cihazdaki faktörlerden etkilenebileceğinden genellikle ağ çağrılarından daha iyidir. Başlangıç sırasında ağ çağrılarından kaçının. Bunlardan birini kullanmak kaçınılmaz olduğunda G/Ç'yi kullanın.

Uygulama mimarinizin, yalnızca başlangıç karşılaştırması yaparken kullanmak için bile olsa ağ veya G/Ç çağrıları olmadan uygulama başlatmayı desteklemesini öneririz. Bu, karşılaştırmalarınızın farklı iterasyonları arasında mümkün olan en düşük varyansı sağlamanıza yardımcı olur.

Uygulamanız Hilt kullanıyorsa Microbenchmark ve Hilt'te karşılaştırma yaparken sahte G/Ç sınırlı uygulamalar sağlayabilirsiniz.

Tüm önemli kullanıcı yolculuklarını kapsayın

Referans profilinizi oluştururken tüm önemli kullanıcı yolculuklarını doğru şekilde kapsaması önemlidir. Kapsam dışında kalan kullanıcı yolculukları, temel profiller tarafından iyileştirilmez. En etkili temel profiller, tüm yaygın başlangıç kullanıcı yolculuklarının yanı sıra kaydırmalı listeler gibi performansa duyarlı uygulama içi kullanıcı yolculuklarını içerir.