عملکرد برنامه را با نمایه های پایه بهبود دهید

1. قبل از شروع

این کد لبه نحوه تولید نمایه های خط پایه برای بهینه سازی عملکرد برنامه خود و نحوه تأیید مزایای عملکرد استفاده از پروفایل های پایه را نشان می دهد.

آنچه شما نیاز دارید

کاری که خواهی کرد

  • پروژه را برای استفاده از ژنراتورهای Baseline Profiles تنظیم کنید.
  • برای بهینه‌سازی راه‌اندازی برنامه و عملکرد پیمایش، نمایه‌های خط پایه ایجاد کنید.
  • بهبود عملکرد را با کتابخانه Jetpack Macrobenchmark تأیید کنید.

چیزی که یاد خواهید گرفت

  • نمایه‌های پایه و نحوه بهبود عملکرد برنامه.
  • نحوه تولید پروفایل های پایه
  • دستاوردهای عملکردی پروفایل های پایه.

2. راه اندازی

برای شروع، با استفاده از دستور زیر، مخزن Github را از خط فرمان کلون کنید:

$ git clone https://github.com/android/codelab-android-performance.git

همچنین می توانید دو فایل فشرده را دانلود کنید:

پروژه را در اندروید استودیو باز کنید

  1. در پنجره Welcome to Android Studio، را انتخاب کنید 61d0a4432ef6d396.png یک پروژه موجود را باز کنید .
  2. پوشه [Download Location]/codelab-android-performance/baseline-profiles انتخاب کنید. مطمئن شوید که دایرکتوری baseline-profiles را انتخاب کرده اید.
  3. وقتی Android Studio پروژه را وارد می‌کند، مطمئن شوید که می‌توانید ماژول app را اجرا کنید تا نمونه برنامه‌ای را که بعداً با آن کار می‌کنید بسازید.

برنامه نمونه

در این کد لبه، شما با برنامه نمونه JetSnack کار می کنید. این یک برنامه مجازی سفارش میان وعده است که از Jetpack Compose استفاده می کند.

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

23633b02ac7ce1bc.png

3. نمایه های پایه چیست؟

نمایه‌های خط پایه سرعت اجرای کد را تا حدود 30 درصد از اولین راه‌اندازی با اجتناب از مراحل تفسیر و کامپایل به‌موقع (JIT) برای مسیرهای کد گنجانده شده بهبود می‌بخشند. با ارسال نمایه خط پایه در یک برنامه یا کتابخانه، Android Runtime (ART) می‌تواند مسیرهای کد گنجانده شده را از طریق جمع‌آوری پیش از زمان (AOT) بهینه کند، و بهبود عملکرد را برای هر کاربر جدید و در هر به‌روزرسانی برنامه ارائه کند. این بهینه‌سازی هدایت‌شده با نمایه (PGO) به برنامه‌ها اجازه می‌دهد راه‌اندازی را بهینه کنند، جابجایی تعامل را کاهش دهند و عملکرد کلی زمان اجرا را برای کاربران نهایی از اولین راه‌اندازی بهبود بخشند.

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

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

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

4. ماژول تولید کننده پروفایل پایه را راه اندازی کنید

شما می توانید پروفایل های پایه را با یک کلاس تست ابزار دقیق ایجاد کنید که نیاز به یک ماژول Gradle جدید برای اضافه شدن به پروژه شما دارد. ساده ترین راه برای افزودن آن به پروژه خود با ماژول Android Studio است که با Android Studio Hedgehog یا بالاتر ارائه می شود.

با کلیک راست روی پروژه یا ماژول خود در پانل پروژه ، پنجره جدید ماژول ویزارد را باز کنید و New > Module را انتخاب کنید.

232b04efef485e9c.png

از پنجره باز شده، Baseline Profile Generator را از قسمت Templates انتخاب کنید.

b191fe07969e8c26.png

علاوه بر پارامترهای معمول مانند نام ماژول، نام بسته، زبان یا زبان پیکربندی ساخت، دو ورودی وجود دارد که برای یک ماژول جدید معمول نیستند: برنامه هدف و استفاده از دستگاه مدیریت شده Gradle .

برنامه هدف ماژول برنامه ای است که برای تولید نمایه های خط پایه برای آن استفاده می شود. اگر بیش از یک ماژول برنامه در پروژه خود دارید، انتخاب کنید که می خواهید ژنراتورها را برای کدام اجرا کنید.

چک باکس Use Gradle Managed Device ، ماژول را تنظیم می کند تا ژنراتورهای Baseline Profile را در شبیه سازهای Android مدیریت شده به طور خودکار اجرا کند. می‌توانید در Scale your tests with Gradle Managed Devices درباره دستگاه‌های مدیریت‌شده Gradle اطلاعات بیشتری کسب کنید. اگر علامت آن را بردارید، ژنراتورها از هر دستگاه متصل استفاده می کنند.

هنگامی که تمام جزئیات مربوط به ماژول جدید را تعریف کردید، روی Finish کلیک کنید تا به ساخت ماژول ادامه دهید.

تغییرات ایجاد شده توسط جادوگر ماژول

جادوگر ماژول چندین تغییر در پروژه شما ایجاد می کند.

یک ماژول Gradle به نام baselineprofile یا نامی که در جادوگر انتخاب می کنید اضافه می کند.

این ماژول از افزونه com.android.test استفاده می کند که به Gradle می گوید آن را در برنامه خود قرار ندهد، بنابراین فقط می تواند حاوی کد تست یا معیار باشد. همچنین از افزونه androidx.baselineprofile استفاده می‌کند که امکان تولید خودکار پروفایل‌های پایه را فراهم می‌کند.

جادوگر همچنین تغییراتی را در ماژول برنامه هدفی که انتخاب می کنید ایجاد می کند. به طور خاص، افزونه androidx.baselineprofile را اعمال می کند، وابستگی androidx.profileinstaller را اضافه می کند، و وابستگی baselineProfile را به ماژول جدید ایجاد شده build.gradle(.kts) اضافه می کند:

plugins {
  id("androidx.baselineprofile")
}

dependencies {
  // ...
  implementation("androidx.profileinstaller:profileinstaller:1.3.0")
  "baselineProfile"(project(mapOf("path" to ":baselineprofile")))
}

افزودن وابستگی androidx.profileinstaller به شما امکان می دهد کارهای زیر را انجام دهید:

  • به صورت محلی دستاوردهای عملکرد نمایه های پایه تولید شده را تأیید کنید.
  • از نمایه‌های پایه در Android 7 (سطح API 24) و Android 8 (سطح Api 26) استفاده کنید که از نمایه‌های Cloud پشتیبانی نمی‌کنند.
  • از نمایه‌های خط پایه در دستگاه‌هایی که خدمات Google Play را ندارند، استفاده کنید.

وابستگی baselineProfile(project(":baselineprofile")) به Gradle اجازه می دهد بداند که از کدام ماژول باید پروفایل های پایه تولید شده را بگیرد.

اکنون که مجموعه پروژه را دارید، یک کلاس تولید کننده پروفایل های پایه بنویسید.

5. یک مولد پروفایل پایه بنویسید

معمولاً برای سفرهای کاربر معمولی برنامه خود، نمایه های خط پایه ایجاد می کنید.

جادوگر ماژول یک کلاس آزمایشی پایه BaselineProfileGenerator ایجاد می کند که می تواند نمایه Baseline را برای راه اندازی برنامه شما ایجاد کند و به شکل زیر است:

@RunWith(AndroidJUnit4::class)
@LargeTest
class BaselineProfileGenerator {

    @get:Rule
    val rule = BaselineProfileRule()

    @Test
    fun generate() {
        rule.collect("com.example.baselineprofiles_codelab") {
            // This block defines the app's critical user journey. This is where you
            // optimize for app startup. You can also navigate and scroll
            // through your most important UI.

            // Start default activity for your app.
            pressHome()
            startActivityAndWait()

            // TODO Write more interactions to optimize advanced journeys of your app.
            // For example:
            // 1. Wait until the content is asynchronously loaded.
            // 2. Scroll the feed content.
            // 3. Navigate to detail screen.

            // Check UiAutomator documentation for more information about how to interact with the app.
            // https://d.android.com/training/testing/other-components/ui-automator
        }
    }
}

این کلاس از یک قانون تست BaselineProfileRule استفاده می کند و شامل یک روش تست برای تولید پروفایل است. نقطه ورود برای تولید پروفایل تابع collect() است. این فقط به دو پارامتر نیاز دارد:

  • packageName : بسته برنامه شما.
  • profileBlock : آخرین پارامتر لامبدا.

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

به طور پیش‌فرض، کلاس مولد ایجاد شده حاوی تعاملاتی برای شروع Activity پیش‌فرض شما است و منتظر می‌ماند تا اولین فریم برنامه شما با استفاده از متد startActivityAndWait() رندر شود.

مولد را با سفرهای سفارشی گسترش دهید

می‌توانید ببینید که کلاس تولید شده شامل برخی TODO برای نوشتن تعاملات بیشتر برای بهینه‌سازی سفرهای پیشرفته برنامه شما است. این توصیه می شود تا بتوانید عملکرد را فراتر از شروع برنامه بهینه کنید.

در برنامه نمونه ما، می توانید این سفرها را با انجام موارد زیر شناسایی کنید:

  1. برنامه را شروع کنید. این قبلاً تا حدی توسط کلاس تولید شده پوشش داده شده است.
  2. صبر کنید تا محتوا به صورت ناهمزمان بارگیری شود.
  3. لیست میان وعده ها را اسکرول کنید.
  4. به جزئیات میان وعده بروید.

ژنراتور را تغییر دهید تا حاوی توابع مشخص شده باشد که سفرهای معمولی را در قطعه زیر پوشش می دهد:

// ...
rule.collect("com.example.baselineprofiles_codelab") {
    // This block defines the app's critical user journey. This is where you
    // optimize for app startup. You can also navigate and scroll
    // through your most important UI.

    // Start default activity for your app.
    pressHome()
    startActivityAndWait()

    // TODO Write more interactions to optimize advanced journeys of your app.
    // For example:
    // 1. Wait until the content is asynchronously loaded.
    waitForAsyncContent()
    // 2. Scroll the feed content.
    scrollSnackListJourney()
    // 3. Navigate to detail screen.
    goToSnackDetailJourney()

    // Check UiAutomator documentation for more information about how to interact with the app.
    // https://d.android.com/training/testing/other-components/ui-automator
}
// ...

اکنون، برای هر سفر ذکر شده تعامل بنویسید. می توانید آن را به عنوان تابع افزونه MacrobenchmarkScope بنویسید تا به پارامترها و عملکردهایی که ارائه می دهد دسترسی داشته باشید. نوشتن آن به این شکل به شما امکان می‌دهد از تعاملات با معیارها برای تأیید دستاوردهای عملکرد مجدد استفاده کنید.

منتظر محتوای ناهمزمان باشید

بسیاری از برنامه‌ها در هنگام راه‌اندازی برنامه دارای نوعی بارگیری ناهمزمان هستند که به عنوان حالت کاملاً نمایش داده شده نیز شناخته می‌شود، که به سیستم می‌گوید چه زمانی محتوا بارگیری و ارائه می‌شود و کاربر می‌تواند با آن تعامل داشته باشد. منتظر وضعیت در مولد ( waitForAsyncContent ) با این فعل و انفعالات باشید:

  1. لیست خوراکی های خوراکی را پیدا کنید.
  2. صبر کنید تا برخی از موارد موجود در لیست روی صفحه قابل مشاهده باشند.
fun MacrobenchmarkScope.waitForAsyncContent() {
   device.wait(Until.hasObject(By.res("snack_list")), 5_000)
   val contentList = device.findObject(By.res("snack_list"))
   // Wait until a snack collection item within the list is rendered.
   contentList.wait(Until.hasObject(By.res("snack_collection")), 5_000)
}

سفر لیست پیمایش

برای سفر فهرست اسنک اسکرول ( scrollSnackListJourney )، می توانید این تعاملات را دنبال کنید:

  1. عنصر UI لیست میان وعده را پیدا کنید.
  2. حاشیه های اشاره را طوری تنظیم کنید که ناوبری سیستم را فعال نکند.
  3. لیست را اسکرول کنید و منتظر بمانید تا رابط کاربری ثابت شود.
fun MacrobenchmarkScope.scrollSnackListJourney() {
   val snackList = device.findObject(By.res("snack_list"))
   // Set gesture margin to avoid triggering gesture navigation.
   snackList.setGestureMargin(device.displayWidth / 5)
   snackList.fling(Direction.DOWN)
   device.waitForIdle()
}

رفتن به جزئیات سفر

آخرین سفر ( goToSnackDetailJourney ) این تعاملات را اجرا می کند:

  1. لیست میان وعده ها و همه میان وعده هایی که می توانید با آنها کار کنید را پیدا کنید.
  2. یک مورد را از لیست انتخاب کنید.
  3. روی مورد کلیک کنید و صبر کنید تا صفحه جزئیات بارگیری شود. می‌توانید از این واقعیت استفاده کنید که لیست تنقلات دیگر روی صفحه نمایش نخواهد بود.
fun MacrobenchmarkScope.goToSnackDetailJourney() {
    val snackList = device.findObject(By.res("snack_list"))
    val snacks = snackList.findObjects(By.res("snack_item"))
    // Select snack from the list based on running iteration.
    val index = (iteration ?: 0) % snacks.size
    snacks[index].click()
    // Wait until the screen is gone = the detail is shown.
    device.wait(Until.gone(By.res("snack_list")), 5_000)
}

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

6. دستگاهی را برای راه اندازی ژنراتور آماده کنید

برای ایجاد نمایه‌های پایه، توصیه می‌کنیم از شبیه‌ساز مانند دستگاه مدیریت‌شده Gradle یا دستگاهی که Android 13 (API 33) یا بالاتر را اجرا می‌کند، استفاده کنید.

برای تکرارپذیر کردن فرآیند و تولید خودکار پروفایل های پایه، می توانید از دستگاه های مدیریت شده Gradle استفاده کنید. Gradle Managed Devices به شما امکان می‌دهد آزمایش‌هایی را روی یک شبیه‌ساز اندروید اجرا کنید بدون اینکه نیازی به راه‌اندازی دستی و خراب کردن آن داشته باشید. می‌توانید در Scale your tests with Gradle Managed Devices درباره دستگاه‌های مدیریت‌شده Gradle اطلاعات بیشتری کسب کنید.

برای تعریف یک دستگاه مدیریت شده Gradle، همانطور که در قطعه زیر نشان داده شده است، تعریف آن را به فایل :baselineprofile module build.gradle.kts اضافه کنید:

android {
  // ...

  testOptions.managedDevices.devices {
    create<ManagedVirtualDevice>("pixel6Api31") {
        device = "Pixel 6"
        apiLevel = 31
        systemImageSource = "aosp"
    }
  } 
}

در این حالت از اندروید 11 (سطح API 31) استفاده می کنیم و تصویر سیستم aosp قابلیت دسترسی روت را دارد.

سپس، پلاگین Gradle Profile Baseline را برای استفاده از دستگاه مدیریت شده Gradle تعریف شده پیکربندی کنید. برای انجام این کار، نام دستگاه را به ویژگی managedDevices اضافه کنید و useConnectedDevices همانطور که در قطعه زیر نشان داده شده است غیرفعال کنید:

android {
  // ...
}

baselineProfile {
   managedDevices += "pixel6Api31"
   useConnectedDevices = false
}

dependencies {
  // ...
}

سپس نمایه خط پایه را ایجاد کنید.

7. نمایه خط پایه را ایجاد کنید

پس از آماده شدن دستگاه، می توانید نمایه خط پایه را ایجاد کنید. پلاگین Baseline Profile Gradle وظایف Gradle را ایجاد می کند تا کل فرآیند اجرای کلاس تست ژنراتور و اعمال پروفایل های پایه تولید شده در برنامه شما را خودکار کند.

جادوگر ماژول جدید پیکربندی اجرا را ایجاد کرد تا بتواند به سرعت وظیفه Gradle را با تمام پارامترهای لازم برای اجرا بدون نیاز به جابجایی بین ترمینال و Android Studio اجرا کند.

برای اجرای آن، پیکربندی اجرای Generate Baseline Profile را پیدا کرده و روی دکمه Run کلیک کنید 599be5a3531f863b.png .

6911ecf1307a213f.png

این کار تصویر شبیه ساز را که قبلاً تعریف شده بود شروع می کند. فعل و انفعالات را از کلاس تست BaselineProfileGenerator چندین بار اجرا کنید و پس از آن شبیه ساز را خراب کنید و خروجی را در اختیار Android Studio قرار دهید.

هنگامی که ژنراتور با موفقیت به پایان رسید، پلاگین Gradle به طور خودکار baseline-prof.txt تولید شده را در برنامه هدف شما ( :app module) در پوشه src/release/generated/baselineProfile/ قرار می دهد.

fa0f52de5d2ce5e8.png

(اختیاری) ژنراتور را از خط فرمان اجرا کنید

یا می توانید ژنراتور را از خط فرمان اجرا کنید. می‌توانید از وظیفه ایجاد شده توسط دستگاه مدیریت شده Gradle استفاده کنید— :app:generateBaselineProfile . این دستور تمام تست های پروژه تعریف شده توسط وابستگی baselineProfile(project(:baselineProfile)) را اجرا می کند. از آنجایی که ماژول همچنین حاوی معیارهایی برای تأیید بعدی دستاوردهای عملکرد است، این تست ها با هشدار در مورد اجرای معیارها در یک شبیه ساز شکست می خورند.

android
   .testInstrumentationRunnerArguments
   .androidx.benchmark.enabledRules=BaselineProfile

برای این کار، می‌توانید تمام ژنراتورهای Baseline Profiles را با آرگومان اجراکننده ابزار دقیق زیر فیلتر کنید و همه معیارها نادیده گرفته شوند:

کل دستور به صورت زیر است:

./gradlew :app:generateBaselineProfile -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile

برنامه خود را با نمایه های پایه توزیع کنید

هنگامی که نمایه خط پایه ایجاد شد و در کد منبع برنامه شما کپی شد، نسخه تولیدی برنامه خود را همانطور که معمولا انجام می دهید بسازید. برای توزیع نمایه های پایه بین کاربران خود نیازی به انجام کار اضافی ندارید. آنها در حین ساخت توسط پلاگین Android Gradle انتخاب می شوند و در AAB یا APK شما گنجانده می شوند. سپس بیلد را در گوگل پلی آپلود کنید.

هنگامی که کاربران برنامه را نصب می‌کنند یا برنامه را از نسخه قبلی به‌روزرسانی می‌کنند، نمایه پایه نیز نصب می‌شود که در نتیجه عملکرد بهتری از اولین اجرای برنامه ایجاد می‌شود.

مرحله بعدی نشان می دهد که چگونه می توان بررسی کرد که عملکرد برنامه با نمایه های پایه چقدر بهبود می یابد.

8. (اختیاری) ایجاد پروفایل های پایه را سفارشی کنید

پلاگین Baseline Profiles Gradle شامل گزینه هایی برای سفارشی کردن نحوه تولید پروفایل ها برای رفع نیازهای خاص شما است. می توانید رفتار را با بلوک پیکربندی baselineProfile { } در اسکریپت های ساخت تغییر دهید.

بلوک پیکربندی در ماژول :baselineprofile بر نحوه اجرای ژنراتورها با امکان افزودن managedDevices و تصمیم گیری در مورد استفاده از useConnectedDevices یا Gradle Managed تاثیر می گذارد.

بلوک پیکربندی در ماژول :app target تصمیم می‌گیرد که پروفایل‌ها کجا ذخیره شوند یا چگونه تولید شوند. می توانید پارامترهای زیر را تغییر دهید:

  • automaticGenerationDuringBuild : در صورت فعال بودن، می توانید نمایه Baseline را هنگام ساخت بیلد انتشار تولید ایجاد کنید. این هنگام ساختن CI قبل از ارسال برنامه مفید است.
  • saveInSrc : مشخص می کند که آیا نمایه های خط پایه تولید شده در پوشه src/ ذخیره می شود. همچنین می توانید از پوشه :baselineprofile build به فایل دسترسی داشته باشید.
  • baselineProfileOutputDir : محل ذخیره پروفایل های پایه تولید شده را مشخص می کند.
  • mergeIntoMain : به طور پیش‌فرض، پروفایل‌های پایه در هر نوع ساخت (طعم محصول و نوع ساخت) تولید می‌شوند. اگر می‌خواهید همه پروفایل‌ها را در src/main ادغام کنید، می‌توانید با فعال کردن این پرچم این کار را انجام دهید.
  • filter : می‌توانید کلاس‌ها یا روش‌هایی را فیلتر کنید که شامل یا حذف از پروفایل‌های پایه تولید شده باشد. این می‌تواند برای توسعه‌دهندگان کتابخانه‌ای که می‌خواهند فقط کدهای موجود در کتابخانه گنجانده شود، مفید باشد.

9. بهبود عملکرد راه اندازی را تأیید کنید

پس از اینکه نمایه خط پایه را ایجاد کردید و آن را به برنامه خود اضافه کردید، بررسی کنید که تأثیری را که می خواهید بر عملکرد برنامه شما داشته باشد.

جادوگر ماژول جدید یک کلاس معیار به نام StartupBenchmarks ایجاد می کند. این شامل یک معیار برای اندازه گیری زمان راه اندازی برنامه است و آن را با زمانی که برنامه از نمایه های خط پایه استفاده می کند مقایسه می کند.

کلاس به صورت زیر است:

@RunWith(AndroidJUnit4::class)
@LargeTest
class StartupBenchmarks {

    @get:Rule
    val rule = MacrobenchmarkRule()

    @Test
    fun startupCompilationNone() =
        benchmark(CompilationMode.None())

    @Test
    fun startupCompilationBaselineProfiles() =
        benchmark(CompilationMode.Partial(BaselineProfileMode.Require))

    private fun benchmark(compilationMode: CompilationMode) {
        rule.measureRepeated(
            packageName = "com.example.baselineprofiles_codelab",
            metrics = listOf(StartupTimingMetric()),
            compilationMode = compilationMode,
            startupMode = StartupMode.COLD,
            iterations = 10,
            setupBlock = {
                pressHome()
            },
            measureBlock = {
                startActivityAndWait()

                // TODO Add interactions to wait for when your app is fully drawn.
                // The app is fully drawn when Activity.reportFullyDrawn is called.
                // For Jetpack Compose, you can use ReportDrawn, ReportDrawnWhen and ReportDrawnAfter
                // from the AndroidX Activity library.

                // Check the UiAutomator documentation for more information on how to
                // interact with the app.
                // https://d.android.com/training/testing/other-components/ui-automator
            }
        )
    }
}

از MacrobenchmarkRule استفاده می کند که می تواند معیارهایی را برای برنامه شما اجرا کند و معیارهای عملکرد را جمع آوری کند. نقطه ورود برای نوشتن یک معیار، تابع measureRepeated از قانون است.

به چندین پارامتر نیاز دارد:

  • packageName: کدام برنامه را اندازه گیری کنیم.
  • metrics : چه نوع اطلاعاتی را می خواهید در طول معیار اندازه گیری کنید.
  • iterations : چند بار معیار تکرار می شود.
  • startupMode : چگونه می‌خواهید برنامه شما با شروع معیار شما شروع شود .
  • setupBlock : چه تعاملاتی با برنامه شما باید قبل از اندازه گیری اتفاق بیفتد.
  • measureBlock : تعاملات با برنامه شما که می خواهید در طول معیار اندازه گیری کنید.

کلاس تست همچنین شامل دو تست است: startupCompilationeNone() و startupCompilationBaselineProfiles() که تابع benchmark() را با compilationMode مختلف فراخوانی می کند.

CompilationMode

پارامتر CompilationMode نحوه از پیش کامپایل شدن برنامه در کد ماشین را مشخص می کند. گزینه های زیر را دارد:

  • DEFAULT : تا حدی برنامه را با استفاده از نمایه‌های خط پایه در صورت موجود بودن، از پیش کامپایل می‌کند. در صورتی که هیچ پارامتر compilationMode اعمال نشود از این مورد استفاده می شود.
  • None() : وضعیت کامپایل برنامه را بازنشانی می کند و برنامه را از قبل کامپایل نمی کند. جمع‌آوری به موقع (JIT) همچنان در طول اجرای برنامه فعال است.
  • Partial() : برنامه را با نمایه های پایه یا اجرای گرم کردن یا هر دو از قبل کامپایل می کند.
  • Full() : کل کد برنامه را از قبل کامپایل می کند. این تنها گزینه در اندروید 6 (API 23) و پایین تر است.

اگر می خواهید بهینه سازی عملکرد برنامه خود را شروع کنید، می توانید حالت کامپایل DEFAULT را انتخاب کنید، زیرا عملکرد مشابه زمانی است که برنامه از Google Play نصب شده است. اگر می‌خواهید مزایای عملکرد ارائه شده توسط Baseline Profiles را با هم مقایسه کنید، می‌توانید این کار را با مقایسه نتایج حالت کامپایل None و Partial انجام دهید.

معیار را تغییر دهید تا منتظر محتوا باشید

معیارها با نوشتن تعاملات با برنامه شما، مشابه ژنراتورهای Baseline Profiles نوشته می‌شوند. به طور پیش‌فرض، بنچمارک‌های ایجاد شده فقط منتظر می‌مانند تا اولین فریم رندر شود - مشابه کاری که BaselineProfileGenerator انجام داد - بنابراین توصیه می‌کنیم آن را بهبود ببخشید تا منتظر محتوای ناهمزمان باشید.

می توانید این کار را با استفاده مجدد از توابع پسوندی که برای ژنراتور می نویسید انجام دهید. از آنجایی که این معیار زمان‌بندی راه‌اندازی را نشان می‌دهد - با استفاده از StartupTimingMetric() - توصیه می‌کنیم فقط انتظار برای محتوای ناهمزمان را در اینجا قرار دهید و سپس یک معیار جداگانه برای سایر سفرهای کاربر تعریف‌شده در ژنراتور بنویسید.

// ...
measureBlock = {
   startActivityAndWait()

   // The app is fully drawn when Activity.reportFullyDrawn is called.
   // For Jetpack Compose, you can use ReportDrawn, ReportDrawnWhen and ReportDrawnAfter
   // from the AndroidX Activity library.
   waitForAsyncContent() // <------- Added to wait for async content.

   // Check the UiAutomator documentation for more information on how to
   // interact with the app.
   // https://d.android.com/training/testing/other-components/ui-automator
}

بنچمارک ها را اجرا کنید

شما می توانید معیارها را به همان روشی که تست های ابزاری را اجرا می کنید اجرا کنید. می توانید تابع تست یا کل کلاس را با نماد ناودان در کنار آن اجرا کنید.

587b04d1a76d1e9d.png

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

94e0da86b6f399d5.png

هنگامی که معیار را اجرا می کنید، برنامه شما بازسازی می شود و سپس بنچمارک های شما را اجرا می کند. معیارها بر اساس iterations که تعریف کرده اید چندین بار شروع، متوقف و حتی برنامه شما را دوباره نصب می کنند.

پس از تکمیل بنچمارک ها، می توانید زمان بندی را در خروجی اندروید استودیو همانطور که در تصویر زیر نشان داده شده است مشاهده کنید:

282f90d5f6ff5196.png

از اسکرین شات، می توانید ببینید که زمان راه اندازی برنامه برای هر CompilationMode متفاوت است. مقادیر میانه در جدول زیر نشان داده شده است:

timeToInitialDisplay [ms]

timeToFullDisplay [ms]

هیچ کدام

202.2

818.8

نمایه های پایه

193.7

637.9

بهبود

4%

28%

تفاوت بین حالت‌های کامپایل برای timeToFullDisplay 180 میلی‌ثانیه است که فقط با داشتن یک نمایه پایه، 28 درصد بهبود یافته است. CompilationNone بدتر عمل می کند، زیرا دستگاه باید بیشترین کامپایل JIT را در هنگام راه اندازی برنامه انجام دهد. CompilationBaselineProfiles بهتر عمل می کند زیرا کامپایل جزئی با Baseline Profiles AOT کدی را که کاربر به احتمال زیاد استفاده می کند کامپایل می کند و کد غیر بحرانی را از پیش کامپایل نشده باقی می گذارد بنابراین نیازی به بارگیری فوری نیست.

10. (اختیاری) بهبود عملکرد پیمایش را تأیید کنید

مشابه مرحله قبل، می توانید عملکرد اسکرول را اندازه گیری و تأیید کنید. ابتدا یک کلاس تست ScrollBenchmarks با قانون محک و دو روش تست که از حالت های کامپایل متفاوتی استفاده می کنند ایجاد کنید:

@LargeTest
@RunWith(AndroidJUnit4::class)
class ScrollBenchmarks {

   @get:Rule
   val rule = MacrobenchmarkRule()

   @Test
   fun scrollCompilationNone() = scroll(CompilationMode.None())

   @Test
   fun scrollCompilationBaselineProfiles() = scroll(CompilationMode.Partial())

   private fun scroll(compilationMode: CompilationMode) {
       // TODO implement
   }
}

از درون روش scroll ، از تابع measureRepeated با پارامترهای مورد نیاز استفاده کنید. برای پارامتر metrics ، از FrameTimingMetric استفاده کنید، که مدت زمان تولید فریم های رابط کاربری را اندازه می گیرد:

private fun scroll(compilationMode: CompilationMode) {
   rule.measureRepeated(
       packageName = "com.example.baselineprofiles_codelab",
       metrics = listOf(FrameTimingMetric()),
       compilationMode = compilationMode,
       startupMode = StartupMode.WARM,
       iterations = 10,
       setupBlock = {
           // TODO implement
       },
       measureBlock = {
           // TODO implement
       }
   )
}

این بار، باید تعاملات را بین setupBlock و measureBlock بیشتر تقسیم کنید تا فقط مدت زمان فریم را در اولین طرح بندی و پیمایش محتوا اندازه گیری کنید. بنابراین، توابعی که صفحه پیش‌فرض را شروع می‌کنند در setupBlock و توابع افزونه‌ای که قبلاً ایجاد شده‌اند waitForAsyncContent() و scrollSnackListJourney() را در measureBlock قرار دهید:

private fun scroll(compilationMode: CompilationMode) {
   rule.measureRepeated(
       packageName = "com.example.baselineprofiles_codelab",
       metrics = listOf(FrameTimingMetric()),
       compilationMode = compilationMode,
       startupMode = StartupMode.WARM,
       iterations = 10,
       setupBlock = {
           pressHome()
           startActivityAndWait()
       },
       measureBlock = {
           waitForAsyncContent()
           scrollSnackListJourney()
       }
   )
}

هنگامی که بنچمارک آماده شد، می توانید آن را مانند قبل اجرا کنید تا نتایجی را که در تصویر زیر نشان داده شده است به دست آورید:

84aa99247226fc3a.png

FrameTimingMetric مدت زمان فریم ها را بر حسب میلی ثانیه ( frameDurationCpuMs ) در صدک 50، 90، 95 و 99 خروجی می دهد. در اندروید 12 (سطح API 31) و بالاتر، مدت زمانی که فریم های شما بیش از حد مجاز است ( frameOverrunMs ) را نیز نشان می دهد. مقدار می تواند منفی باشد، به این معنی که زمان اضافی برای تولید فریم باقی مانده است.

از نتایج، می‌توانید ببینید که CompilationBaselineProfiles به طور متوسط ​​2 میلی‌ثانیه مدت زمان فریم کوتاه‌تری دارد که ممکن است برای کاربران قابل توجه نباشد. با این حال، برای صدک های دیگر نتایج واضح تر است. برای P99، تفاوت 43.5 میلی‌ثانیه است که بیش از 3 فریم نادیده گرفته شده در دستگاهی است که با سرعت 90 فریم در ثانیه کار می‌کند. برای مثال، برای پیکسل 6، 1000 میلی‌ثانیه / 90 فریم در ثانیه = حداکثر 11 میلی‌ثانیه زمان برای رندر کردن یک فریم است.

11. تبریک می گویم

تبریک می‌گوییم، شما با موفقیت این نرم‌افزار را تکمیل کردید و عملکرد برنامه خود را با نمایه‌های خط پایه بهبود دادید!

منابع اضافی

منابع اضافی زیر را ببینید:

اسناد مرجع