Temel Profillerde Hata Ayıklama

Bu belgede, sorunları teşhis etmenize ve Temel Profillerinizin en fazla yararı sağlamak için doğru şekilde çalışmasını sağlamaya yardımcı olacak en iyi uygulamalar gösterilmektedir.

Derleme sorunları

Android'de Now örnek uygulamasındaki Temel Profiller örneğini kopyaladıysanız Temel 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):
        ...

Bu hatalar, Android'de Temel Profil oluşturmak için Gradle tarafından yönetilen bir cihaz kullandığından meydana gelir. Genellikle emülatörde performans karşılaştırmaları çalıştırmamanız gerektiğinden, hatalar normaldir. Ancak, Temel Profiller oluşturduğunuzda performans metrikleri toplamadığınızdan, kolaylık sağlamak için Temel Profil toplamayı emülatörlerde çalıştırabilirsiniz. Temel Profiller'i bir emülatörle kullanmak için, derleme ve yükleme işlemlerini 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 emülatörlerde Temel Profilleri etkinleştirmek için Android Studio'da özel bir çalıştırma yapılandırması oluşturabilirsiniz:

Artık Android'de Temel Profiller oluşturmak için özel çalıştırma yapılandırması ekleyin
Şekil 1. Android'de Now'da Temel Profiller oluşturmak için özel bir çalıştırma yapılandırması ekleyin.

Yükleme sorunları

Derlediğiniz APK veya AAB'nin, Temel Profiller içeren bir derleme varyantından olup olmadığını kontrol edin. Bunu kontrol etmenin en kolay yolu, APK'yı Android Studio'da açmaktır. Bunun için Oluştur > APK'yı analiz et'i seçin, APK'nızı açın ve /assets/dexopt/baseline.prof dosyasında profili arayın:

Android Studio'da APK Görüntüleyici'yi kullanarak Temel Profil kontrol etme
Şekil 2. Android Studio'da APK Görüntüleyici'yi kullanarak Temel Profil olup olmadığını kontrol edin.

Temel Profillerin uygulamayı çalıştıran cihazda derlenmesi gerekir. Cihaz üzerinde derleme, hem uygulama mağazası yüklemeleri hem de PackageInstaller kullanılarak yüklenen uygulamalar için uygulama yükleme işleminin bir parçası olarak gerçekleşir. Bununla birlikte, uygulama Android Studio'dan başka cihazdan yüklendiğinde veya komut satırı araçları kullanıldığında, Jetpack ProfileInstaller kitaplığı bir sonraki arka plan DEX optimizasyon işleminde profilleri derleme için sıraya koymaktan sorumludur. Böyle durumlarda, Temel Profillerinizin kullanıldığından emin olmak isterseniz Temel Profillerin derlenmesini zorunlu kılmanız gerekebilir. ProfileVerifier, aşağıdaki örnekte gösterildiği gibi, profil yükleme ve derleme işleminin 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 nedenleri hakkında ipuçları verir:

RESULT_CODE_COMPILED_WITH_PROFILE
Profil yüklenir, derlenir ve uygulama her çalıştırıldığında kullanılır. Görmek istediğiniz sonuç budur.
RESULT_CODE_ERROR_NO_PROFILE_EMBEDDED
Çalıştırılmakta olan APK'da veya AAB'de profil bulunamadı. Bu hatayı görüyorsanız Temel Profilleri içeren bir derleme varyantı kullandığınızdan ve APK'nın bir profil içerdiğinden emin olun.
RESULT_CODE_NO_PROFILE
Uygulama, uygulama mağazası veya paket yöneticisi üzerinden yüklenirken bu uygulama için hiç 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 yerleşik bir profilin hâlâ bulunduğunu unutmayın. Yerleştirilmiş profil bulunamadığında RESULT_CODE_ERROR_NO_PROFILE_EMBEDDED hata kodu döndürülür.
RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION
APK veya AAB'de bir profil bulundu ve derleme için sıraya alındı. Bir profil ProfileInstaller tarafından yüklendiğinde, arka plan DEX optimizasyonu sistem tarafından tekrar ç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 zorunlu kılmanız gerekebilir. Derleme, yükleme sırasında gerçekleştirildiğinden, uygulama Android 9 (API 28) ve sonraki sürümleri çalıştıran cihazlara 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üklendi ve uygulama bununla derlendi. Bu, Google Play Store veya paket yöneticisi üzerinden yüklemenin sonucudur. Eşleşmeyen profil yalnızca profil ile uygulama arasında paylaşılan yöntemleri derler. Bu nedenle bu sonucun RESULT_CODE_COMPILED_WITH_PROFILE değerinden farklı olduğunu unutmayın. Profil, etkin bir şekilde beklenenden daha küçüktür ve Temel Profil'e eklenenden daha az yöntem derlenir.
RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE
ProfileVerifier, doğrulama sonucu önbellek dosyasını yazamıyor. Bunun nedeni, uygulama klasörü izinleriyle ilgili bir sorun olması veya cihazda yeterli boş disk alanı olmaması olabilir.
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 bir PackageManager.NameNotFoundException döndürülür. Bu durumla nadiren karşılaşılır. Uygulamayı kaldırıp her şeyi yeniden yüklemeyi deneyin.
RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ
Doğrulama sonucu için eski bir önbellek dosyası var, ancak bu dosya okunamıyor. Bu tür bir durumla nadiren karşılaşılabilir. Uygulamayı kaldırıp her şeyi yeniden yüklemeyi deneyin.

Üretimde ProfileDoğrufier'ı kullanma

Üretimde, profil durumunu gösteren analiz etkinlikleri oluşturmak için ProfileVerifier aracını Firebase için Google Analytics gibi analiz raporlama kitaplıklarıyla birlikte kullanabilirsiniz. Örneğin, Temel Profiller içermeyen yeni bir uygulama sürümü yayınlanırsa bu uyarı sizi hızlı bir şekilde uyarır.

Temel Profillerin derlenmesini zorla

Temel Profillerinizin derleme durumu RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION ise adb ile hemen derlemeyi zorunlu kılabilirsiniz:

adb shell cmd package compile -r bg-dexopt PACKAGE_NAME

ProfileVerifier olmadan derleme durumunu kontrol etme

ProfileVerifier kullanmıyorsanız derleme durumunu adb ile kontrol edebilirsiniz. Ancak bu özellik, ProfileVerifier kadar ayrıntılı bilgi sağlamaz:

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 gösterir ve aşağıdaki değerlerden biridir:

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

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

Neden değeri, profilin derlemesini neyin tetiklediğini gösterir ve aşağıdaki değerlerden biridir:

Neden Anlamı
install‑dm Uygulama yüklendiğinde Temel Profil manuel olarak veya Google Play tarafından derlendi.
bg‑dexopt Cihazınız boştayken bir profil derlendi. Bu bir Temel Profil veya uygulama kullanımı sırasında toplanan bir profil olabilir.
cmdline Derleme, adb kullanılarak tetiklendi. Bu bir Temel Profil 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 Temel Profillerinizi doğru şekilde tanımlamaya ve karşılaştırmaya yönelik bazı en iyi uygulamalar gösterilmektedir.

Başlangıç metriklerini doğru şekilde karşılaştırma

Başlangıç metrikleriniz iyi tanımlanmışsa Temel Profilleriniz daha etkili olur. İki temel metrik şunlardır: İlk ekran görüntülenene kadar geçen süre (TTID) ve tam görüntüleme süresi (TTFD).

TTID, uygulamanın ilk karesini çizdiği zamandır. Bu süreyi mümkün olduğunca kısa tutmak önemlidir, çünkü bir öğe görüntülemek kullanıcıya uygulamanın çalıştığını gösterir. Uygulamanın yanıt verdiğini göstermek için bir belirsiz ilerleme göstergesi bile görüntüleyebilirsiniz.

TTFD, uygulamayla gerçekten etkileşimde bulunulabilen zamanı ifade eder. Kullanıcıların hayal kırıklığına uğramasını önlemek için bunu mümkün olduğunca kısa tutmak önemlidir. TTFD'ye doğru şekilde sinyal verirseniz TTFD'ye giden yolda çalıştırılan kodun uygulama başlatma işleminin bir parçası olduğunu sisteme bildirmiş olursunuz. Sonuç olarak, sistemin bu kodu profile yerleştirme olasılığı artar.

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

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

Kitaplık profilleri ve özel profiller

Profillerin etkisini karşılaştırırken uygulama profillerinizin avantajlarını Jetpack kitaplıkları gibi kitaplıklar tarafından sağlanan profillerden ayırmak zor olabilir. Android Gradle eklentisi, APK'nızı oluştururken kitaplık bağımlılıklarındaki profilleri ve özel profilinizi ekler. Bu, genel performansı optimize etmek için faydalıdır ve sürüm derlemeleriniz için önerilir. Ancak, ö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, profili kaldırıp karşılaştırmalarınızı çalıştırmaktır. Daha sonra, bu etiketi değiştirin ve karşılaştırmalarınızı tekrar çalıştırın. Bu ikisini karşılaştırdığınızda yalnızca kitaplık profilleri tarafından sağlanan optimizasyonlar ve kitaplık profilleri ile özel profiliniz gösterilir.

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. Genellikle uygulama modülünüz olan profil tüketici modülünize 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"))
    }
  }
}

Modern

android {
  ...
  buildTypes {
    ...
    // Release build with only library profiles.
    releaseWithoutCustomProfile {
      initWith(release)
    }
    ...
  }
  ...
}
...
dependencies {
  ...
  // Remove the baselineProfile dependency.
  // baselineProfile ':baselineprofile"'
}

baselineProfile {
  variants {
    release {
      from(project(":baselineprofile"))
    }
  }
}

Yukarıdaki kod örneği, baselineProfile bağımlılığını tüm varyantlardan kaldırır ve bunu seçici bir şekilde yalnızca release varyantına uygular. Profil üretici modülüne olan bağımlılık kaldırıldığında kitaplık profillerinin eklenmeye devam etmesi mantıksız gelebilir. Ancak bu modül yalnızca özel profilinizi oluşturmaktan sorumludur. Android Gradle eklentisi tüm varyantlar için çalışmaya devam etmektedir ve kitaplık profillerini eklemekten sorumludur.

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

Kotlin

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

Modern

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

Yalnızca kitaplık profilleriyle karşılaştırma yapmak için aşağıdaki komutu çalıştırın:

./gradlew :baselineprofile:connectedBenchmarkReleaseWithoutCustomProfileAndroidTest

Hem kitaplık profilleri hem de özel profilinizle karşılaştırmak için aşağıdaki komutu çalıştırın:

./gradlew :baselineprofile:connectedBenchmarkReleaseAndroidTest

Makrobenchmark örnek uygulamasında önceki kodu çalıştırdığınızda, iki varyant arasında bir performans farkı olduğu gösterilir. Yalnızca kitaplık profilleri kullanıldığında, sıcak startupCompose karşılaştırması aşağıdaki sonuçları gösterir:

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

Birçok Jetpack Compose kitaplığında kitaplık profili bulunur. Bu nedenle, yalnızca Baseline Profile Gradle eklentisini kullanarak bazı optimizasyonlar yapabilirsiniz. Ancak, özel profili kullanırken ek optimizasyonlar yapılabilir:

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

G/Ç'ye bağlı uygulamaların başlatılmasını önleme

Uygulamanız başlatma sırasında çok sayıda G/Ç çağrısı veya ağ çağrısı yapıyorsa bu durum hem uygulama başlatma süresini hem de başlatma karşılaştırmanızın doğruluğunu olumsuz yönde etkileyebilir. Bu yoğun çağrılar belirsiz süreler alabilir. Bu süre, zaman içinde ve hatta aynı karşılaştırmanın yinelemeleri arasında farklılık gösterebilir. G/Ç çağrıları genellikle ağ aramalarından daha iyidir. Çünkü ağ aramaları, cihaz dışındaki faktörlerden ve cihazın kendisinden etkilenebilir. Başlatma sırasında ağ çağrılarından kaçının. Bunlardan birinin veya diğerinin kullanılması kaçınılmaz olduğunda I/O'yu kullanın.

Uygulama mimarinizin, ağ veya G/Ç çağrıları olmadan uygulama başlatmayı desteklemesini öneririz (yalnızca başlatma karşılaştırması yaparken kullanmak için bile). Böylece karşılaştırmalarınızın farklı yinelemeleri arasında mümkün olan en düşük değişkenliği sağlarsınız.

Uygulamanız Hilt kullanıyorsa Microbenchmark ve Hilt'te karşılaştırma yaparken sahte I/O-bound uygulamalar sağlayabilirsiniz.

Tüm önemli kullanıcı yolculuklarını kapsama dahil

Temel Profil oluşturduğunuzda önemli kullanıcı yolculuklarının tümünü doğru şekilde ele almanız gerekir. Kapsama dahil olmayan kullanıcı yolculukları Temel Profiller tarafından iyileşmez. En etkili temel profiller, tüm yaygın başlangıç kullanıcı yolculuklarının yanı sıra kaydırma listeleri gibi performans açısından hassas uygulama içi kullanıcı yolculuklarını içerir.