اشکال زدایی پروفایل های پایه

این سند بهترین روش‌ها را برای کمک به تشخیص مشکلات و اطمینان از اینکه نمایه‌های خط پایه شما به درستی کار می‌کنند و بیشترین سود را ارائه می‌دهند، نشان می‌دهد.

مسائل را بسازید

اگر نمونه Baseline Profiles را در برنامه نمونه Now in Android کپی کرده باشید، ممکن است در طول کار Baseline Profile با خطاهای تست مواجه شوید که بیان می‌کند که آزمایش‌ها را نمی‌توان روی یک شبیه‌ساز اجرا کرد:

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

خرابی‌ها به این دلیل رخ می‌دهند که Now در Android از دستگاهی با مدیریت Gradle برای تولید نمایه پایه استفاده می‌کند. شکست‌ها قابل انتظار هستند، زیرا معمولاً نباید معیارهای عملکرد را روی یک شبیه‌ساز اجرا کنید. با این حال، از آنجایی که هنگام تولید نمایه‌های خط پایه، معیارهای عملکرد را جمع‌آوری نمی‌کنید، می‌توانید برای راحتی، مجموعه نمایه خط پایه را روی شبیه‌سازها اجرا کنید. برای استفاده از پروفایل های پایه با یک شبیه ساز، ساخت و نصب را از خط فرمان انجام دهید و یک آرگومان برای فعال کردن قوانین پروفایل های پایه تنظیم کنید:

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

همچنین، می‌توانید یک پیکربندی اجرای سفارشی در Android Studio ایجاد کنید تا با انتخاب Run > Edit Configurations، نمایه‌های پایه را در شبیه‌سازها فعال کنید:

یک پیکربندی اجرای سفارشی برای ایجاد نمایه‌های خط پایه در Now در Android اضافه کنید
شکل 1. یک پیکربندی اجرای سفارشی برای ایجاد نمایه های خط پایه در Now در اندروید اضافه کنید.

مسائل نصب

بررسی کنید که APK یا AABی که می‌سازید از نوع ساختی است که شامل نمایه‌های خط پایه است. ساده‌ترین راه برای بررسی این موضوع، باز کردن APK در Android Studio با انتخاب Build > Analyze APK ، باز کردن APK خود و جستجوی نمایه در فایل /assets/dexopt/baseline.prof است:

نمایه خط پایه را با استفاده از APK Viewer در Android Studio بررسی کنید
شکل 2. نمایه خط پایه را با استفاده از APK Viewer در Android Studio بررسی کنید.

نمایه های خط پایه باید در دستگاهی که برنامه را اجرا می کند، کامپایل شود. هم برای نصب‌های فروشگاه برنامه و هم برای برنامه‌هایی که با استفاده از PackageInstaller نصب شده‌اند، کامپایل روی دستگاه به عنوان بخشی از فرآیند نصب برنامه انجام می‌شود. با این حال، زمانی که برنامه از Android Studio بارگذاری می‌شود یا از ابزارهای خط فرمان استفاده می‌کند، کتابخانه Jetpack ProfileInstaller مسئول قرار دادن پروفایل‌ها برای کامپایل در طول فرآیند بهینه‌سازی DEX پس‌زمینه بعدی است. در این موارد، اگر می‌خواهید از استفاده از نمایه‌های پایه خود اطمینان حاصل کنید، ممکن است لازم باشد نمایه‌های خط پایه را اجباری کنید . ProfileVerifier به شما امکان می دهد وضعیت نصب و کامپایل پروفایل را جویا شوید، همانطور که در مثال زیر نشان داده شده است:

کاتلین

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

جاوا


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

کدهای نتیجه زیر نکاتی را برای علت برخی از مشکلات ارائه می دهند:

RESULT_CODE_COMPILED_WITH_PROFILE
نمایه نصب می شود، کامپایل می شود و هر زمان که برنامه اجرا می شود استفاده می شود. این نتیجه ای است که می خواهید ببینید.
RESULT_CODE_ERROR_NO_PROFILE_EMBEDDED
هیچ نمایه ای در APK یا AAB در حال اجرا یافت نشد. در صورت مشاهده این خطا، مطمئن شوید که از نوع ساختی استفاده می‌کنید که شامل نمایه‌های خط پایه باشد و APK حاوی نمایه باشد.
RESULT_CODE_NO_PROFILE
هنگام نصب برنامه از طریق فروشگاه برنامه یا مدیر بسته، هیچ نمایه ای برای این برنامه نصب نشده است. دلیل اصلی این کد خطا این است که نصب کننده پروفایل به دلیل غیرفعال بودن ProfileInstallerInitializer اجرا نمی شود. توجه داشته باشید که وقتی این خطا گزارش می‌شود، یک نمایه تعبیه‌شده همچنان در APK برنامه یافت می‌شود. وقتی نمایه تعبیه شده پیدا نشد، کد خطای بازگردانده شده RESULT_CODE_ERROR_NO_PROFILE_EMBEDDED است.
RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION
یک نمایه در APK یا AAB یافت می شود و برای کامپایل در صف قرار می گیرد. هنگامی که یک نمایه توسط ProfileInstaller نصب می‌شود، دفعه بعد که بهینه‌سازی DEX پس‌زمینه توسط سیستم اجرا می‌شود، در صف کامپایل قرار می‌گیرد. نمایه تا زمانی که کامپایل کامل نشود فعال نیست. تا زمانی که کامپایل کامل نشده است، سعی نکنید پروفایل های پایه خود را محک بزنید. ممکن است لازم باشد نمایه‌های خط پایه را اجباری کنید . وقتی برنامه از فروشگاه برنامه یا مدیر بسته روی دستگاه‌های دارای Android 9 (API 28) و بالاتر نصب می‌شود، این خطا رخ نمی‌دهد، زیرا کامپایل در حین نصب انجام می‌شود.
RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING
یک نمایه غیر منطبق نصب شده است و برنامه با آن کامپایل شده است. این نتیجه نصب از طریق فروشگاه Google Play یا مدیر بسته است. توجه داشته باشید که این نتیجه با RESULT_CODE_COMPILED_WITH_PROFILE متفاوت است، زیرا نمایه غیر منطبق فقط روش‌هایی را که هنوز بین نمایه و برنامه به اشتراک گذاشته شده است، جمع‌آوری می‌کند. نمایه عملاً کوچک‌تر از حد انتظار است و روش‌های کمتری نسبت به نمایه خط پایه وارد می‌شود.
RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE
ProfileVerifier نمی تواند فایل کش نتیجه تأیید را بنویسد. این ممکن است به این دلیل اتفاق بیفتد که مشکلی در مجوزهای پوشه برنامه وجود دارد یا اگر فضای دیسک آزاد کافی در دستگاه وجود نداشته باشد.
RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION
ProfileVerifier is running on an unsupported API version of Android. ProfileVerifier فقط از اندروید 9 (سطح API 28) و بالاتر پشتیبانی می کند.
RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST
یک PackageManager.NameNotFoundException هنگام پرس و جو PackageManager برای بسته برنامه پرتاب می شود. این به ندرت باید اتفاق بیفتد. سعی کنید برنامه را حذف نصب کنید و همه چیز را دوباره نصب کنید.
RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ
یک فایل کش نتیجه تأیید قبلی وجود دارد، اما قابل خواندن نیست. این به ندرت باید اتفاق بیفتد. سعی کنید برنامه را حذف نصب کنید و همه چیز را دوباره نصب کنید.

از ProfileVerifier در تولید استفاده کنید

در تولید، می‌توانید از ProfileVerifier به همراه کتابخانه‌های گزارش‌دهنده تجزیه و تحلیل، مانند Google Analytics برای Firebase ، برای تولید رویدادهای تحلیلی که وضعیت نمایه را نشان می‌دهند، استفاده کنید. به عنوان مثال، اگر نسخه جدیدی از برنامه منتشر شود که حاوی نمایه های خط پایه نباشد، به سرعت به شما هشدار می دهد.

کامپایل اجباری پروفایل های پایه

اگر وضعیت کامپایل پروفایل های پایه شما RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION است، می توانید با استفاده از adb ، کامپایل فوری را اجباری کنید:

adb shell cmd package compile -r bg-dexopt PACKAGE_NAME

وضعیت کامپایل را بدون ProfileVerifier بررسی کنید

اگر از ProfileVerifier استفاده نمی کنید، می توانید وضعیت کامپایل را با استفاده از adb بررسی کنید، اگرچه به اندازه ProfileVerifier بینش عمیقی ارائه نمی دهد:

adb shell dumpsys package dexopt | grep -A 2 PACKAGE_NAME

استفاده از adb چیزی شبیه به موارد زیر تولید می کند:

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

مقدار status وضعیت کامپایل پروفایل را نشان می دهد و یکی از مقادیر زیر است:

وضعیت تالیف معنی
speed‑profile یک نمایه کامپایل شده وجود دارد و در حال استفاده است.
verify هیچ نمایه کامپایل شده ای وجود ندارد.

وضعیت verify به این معنی نیست که APK یا AAB حاوی نمایه نیست، زیرا می‌توان آن‌ها را برای جمع‌آوری توسط کار بهینه‌سازی DEX پس‌زمینه بعدی در صف قرار داد.

مقدار دلیل نشان می دهد که چه چیزی باعث کامپایل پروفایل می شود و یکی از مقادیر زیر است:

دلیل معنی
install‑dm هنگام نصب برنامه، نمایه خط پایه به صورت دستی یا توسط Google Play گردآوری شد.
bg‑dexopt در حالی که دستگاه شما بیکار بود، یک نمایه جمع آوری شد. این ممکن است یک نمایه پایه باشد، یا ممکن است نمایه ای باشد که در طول استفاده از برنامه جمع آوری شده است.
cmdline کامپایل با استفاده از adb راه اندازی شد. این ممکن است یک نمایه پایه باشد، یا ممکن است نمایه ای باشد که در طول استفاده از برنامه جمع آوری شده است.

مسائل مربوط به عملکرد

این بخش برخی از بهترین روش‌ها را برای تعریف درست و محک‌گذاری نمایه‌های پایه خود برای بهره‌گیری از بیشترین مزایا از آنها نشان می‌دهد.

معیارهای استارت آپ را به درستی محک بزنید

اگر معیارهای راه اندازی شما به خوبی تعریف شده باشد، نمایه های پایه شما موثرتر خواهد بود. دو معیار کلیدی زمان تا نمایش اولیه (TTID) و زمان تا نمایش کامل (TTFD) هستند.

TTID زمانی است که برنامه اولین فریم خود را ترسیم می کند. مهم است که این مورد را تا حد امکان کوتاه نگه دارید زیرا نمایش چیزی به کاربر نشان می دهد که برنامه در حال اجرا است. حتی می توانید یک نشانگر پیشرفت نامشخص نمایش دهید تا نشان دهد که برنامه پاسخگو است.

TTFD زمانی است که برنامه واقعاً می تواند با آن تعامل داشته باشد. مهم است که این را تا حد امکان کوتاه نگه دارید تا از ناراحتی کاربر جلوگیری شود. اگر به درستی به TTFD سیگنال دهید، به سیستم می‌گویید که کدی که در مسیر TTFD اجرا می‌شود، بخشی از راه‌اندازی برنامه است. در نتیجه سیستم به احتمال زیاد این کد را در نمایه قرار می دهد.

TTID و TTFD را تا حد امکان پایین نگه دارید تا برنامه شما پاسخگو باشد.

این سیستم قادر به تشخیص TTID، نمایش آن در Logcat و گزارش آن به عنوان بخشی از معیارهای راه اندازی است. با این حال، سیستم قادر به تعیین TTFD نیست و این مسئولیت برنامه است که در صورت رسیدن به حالت تعاملی کاملاً ترسیم شده، گزارش دهد. اگر از Jetpack Compose استفاده می‌کنید، می‌توانید با فراخوانی reportFullyDrawn() یا ReportDrawn این کار را انجام دهید. اگر چندین کار پس‌زمینه دارید که همگی باید قبل از طراحی کامل برنامه تکمیل شوند، می‌توانید از FullyDrawnReporter استفاده کنید، همانطور که در بهبود دقت زمان‌بندی راه‌اندازی توضیح داده شده است.

پروفایل های کتابخانه و پروفایل های سفارشی

هنگام محک زدن تأثیر نمایه‌ها، جدا کردن مزایای نمایه‌های برنامه شما از نمایه‌های ارائه شده توسط کتابخانه‌ها، مانند کتابخانه‌های Jetpack، می‌تواند دشوار باشد. هنگامی که APK خود را می سازید، افزونه Android Gradle هر نمایه ای را در وابستگی های کتابخانه و همچنین نمایه سفارشی شما اضافه می کند. این برای بهینه سازی عملکرد کلی خوب است و برای نسخه های منتشر شده شما توصیه می شود. با این حال، اندازه گیری میزان افزایش عملکرد بیشتر از نمایه سفارشی شما را دشوار می کند.

یک راه سریع برای مشاهده دستی بهینه سازی اضافی ارائه شده توسط نمایه سفارشی شما، حذف آن و اجرای معیارهای خود است. سپس آن را جایگزین کنید و دوباره بنچمارک های خود را اجرا کنید. مقایسه این دو به شما بهینه سازی های ارائه شده توسط پروفایل های کتابخانه به تنهایی و پروفایل های کتابخانه به همراه نمایه سفارشی شما را نشان می دهد.

یک روش خودکار برای مقایسه پروفایل ها با ایجاد یک نوع ساخت جدید است که فقط نمایه های کتابخانه را شامل می شود و نمایه سفارشی شما را ندارد. معیارهای این نوع را با نسخه انتشاری که هم نمایه های کتابخانه و هم پروفایل های سفارشی شما را در بر می گیرد، مقایسه کنید. مثال زیر نشان می دهد که چگونه می توان گونه ای را تنظیم کرد که فقط نمایه های کتابخانه را شامل می شود. یک نوع جدید به نام releaseWithoutCustomProfile را به ماژول مصرف کننده نمایه خود اضافه کنید، که معمولاً ماژول برنامه شما است:

کاتلین

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

شیار

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

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

مثال کد قبلی وابستگی baselineProfile را از همه انواع حذف می کند و به طور انتخابی آن را فقط برای نوع release اعمال می کند. ممکن است زمانی که وابستگی به ماژول تولیدکننده نمایه حذف شود، نمایه‌های کتابخانه همچنان اضافه می‌شوند. با این حال، این ماژول تنها مسئول ایجاد نمایه سفارشی شما است. پلاگین Android Gradle هنوز برای همه انواع در حال اجرا است و مسئولیت درج نمایه های کتابخانه را بر عهده دارد.

همچنین باید نوع جدید را به ماژول مولد پروفایل اضافه کنید. در این مثال ماژول تولید کننده :baselineprofile نام دارد.

کاتلین

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

شیار

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

وقتی معیار را از Android Studio اجرا می‌کنید، یک نوع releaseWithoutCustomProfile را برای اندازه‌گیری عملکرد فقط با نمایه‌های کتابخانه انتخاب کنید، یا یک نوع release برای اندازه‌گیری عملکرد با پروفایل‌های کتابخانه و سفارشی انتخاب کنید.

از راه اندازی اپلیکیشن I/O-bound خودداری کنید

اگر برنامه شما در حین راه‌اندازی تماس‌های ورودی/خروجی یا تماس‌های شبکه‌ای زیادی انجام می‌دهد، می‌تواند بر زمان راه‌اندازی برنامه و دقت بنچمارک راه‌اندازی شما تأثیر منفی بگذارد. این تماس‌های سنگین می‌تواند زمان‌های نامشخصی طول بکشد که می‌تواند در طول زمان و حتی بین تکرارهای یک معیار متفاوت باشد. تماس‌های ورودی/خروجی معمولاً بهتر از تماس‌های شبکه هستند، زیرا تماس‌های ورودی می‌توانند تحت تأثیر عوامل خارجی دستگاه و خود دستگاه قرار گیرند. در هنگام راه اندازی از تماس های شبکه خودداری کنید. در جاهایی که استفاده از یکی یا دیگری اجتناب ناپذیر است، از I/O استفاده کنید.

توصیه می‌کنیم راه‌اندازی برنامه‌های پشتیبانی معماری برنامه‌تان را بدون تماس‌های شبکه یا ورودی/خروجی انجام دهید، حتی اگر فقط در هنگام بنچمارک کردن راه‌اندازی از آن استفاده کنید. این کمک می کند تا کمترین تنوع ممکن را بین تکرارهای مختلف معیارهای شما تضمین کنید.

اگر برنامه شما از Hilt استفاده می کند، می توانید پیاده سازی های جعلی I/O-bound را هنگام محک زدن در Microbenchmark و Hilt ارائه دهید.

تمام سفرهای مهم کاربر را پوشش دهد

مهم است که تمام سفرهای مهم کاربر در نسل نمایه پایه خود را به طور دقیق پوشش دهید. هر سفر کاربر که پوشش داده نمی شود توسط نمایه های پایه بهبود نمی یابد. مؤثرترین نمایه‌های پایه شامل تمام سفرهای معمول کاربران راه‌اندازی و همچنین سفرهای کاربر درون برنامه‌ای حساس به عملکرد مانند فهرست‌های پیمایشی است.