כתיבה של נקודת מאקרו בנצ'מרק

משתמשים בספריית Macrobenchmark כדי לבדוק תרחישים נרחבים יותר של שימוש באפליקציה, כולל הפעלת האפליקציה ושינויים מורכבים בממשק המשתמש, כמו גלילה ב-LazyColumn או הפעלת אנימציות. אם אתם רוצים לבדוק חלקים קטנים יותר בקוד, עיינו במאמר Microbenchmark. בדף הזה מוסבר איך להגדיר את ספריית Macrobenchmark.

הספרייה מפיקה תוצאות של השוואת ביצועים למסוף Android Studio ולקובץ JSON עם פרטים נוספים. הוא גם מספק קובצי מעקב שאפשר לטעון ולנתח ב-Android Studio.

משתמשים בספריית Macrobenchmark בסביבת אינטגרציה רציפה (CI), כמו שמתואר במאמר בדיקת ביצועים באינטגרציה רציפה.

אתם יכולים להשתמש ב-Macrobenchmark כדי ליצור פרופילי Baseline. קודם מגדירים את ספריית Macrobenchmark, ואז אפשר ליצור פרופיל Baseline.

‫Macrobenchmark הוא הכלי המומלץ לבדיקת ממשקי משתמש שנוצרו באמצעות Jetpack פיתוח נייטיב. במאמר בנושא יכולת פעולה הדדית מוסבר איך לכתוב בדיקות של UI Automator שמקיימות אינטראקציה עם צומתי פיתוח נייטיב.

הגדרת הפרויקט

מומלץ להשתמש ב-Macrobenchmark עם הגרסה העדכנית של Android Studio.

הגדרת המודול Macrobenchmark

כדי להשתמש ב-Macrobenchmark, צריך מודול com.android.test – נפרד מקוד האפליקציה – שאחראי על הרצת הבדיקות שמודדות את האפליקציה.

ב-Android Studio יש תבנית שמאפשרת להגדיר בקלות את מודול Macrobenchmark. תבנית המודול להשוואה יוצרת באופן אוטומטי מודול בפרויקט כדי למדוד את האפליקציה שנוצרה על ידי מודול האפליקציה, כולל השוואה לדוגמה של זמן ההפעלה.

כדי להשתמש בתבנית המודול ליצירת מודול חדש:

  1. לוחצים לחיצה ימנית על הפרויקט או המודול בחלונית Project ב-Android Studio, ובוחרים באפשרות New > Module.

  2. בחלונית תבניות, בוחרים באפשרות השוואה לשוק. אתם יכולים להתאים אישית את אפליקציית היעד – כלומר האפליקציה שרוצים לבדוק – וגם את שם החבילה והמודול של המודול החדש של Macrobenchmark.

  3. לוחצים על סיום.

תבנית של מודול להערכת ביצועים.
תרשים 1. תבנית של המודול Benchmark.

הגדרת האפליקציה

כדי להשוות בין ביצועים של אפליקציה (שנקראת היעד של Macrobenchmark), האפליקציה צריכה להיות profileable, מה שמאפשר לקרוא מידע מפורט על המעקב בלי להשפיע על הביצועים. אשף המודולים מוסיף את התג <profileable> באופן אוטומטי לקובץ AndroidManifest.xml של האפליקציה.

מוודאים שאפליקציית היעד כוללת את ProfilerInstaller בגרסה 1.3 ואילך, שנדרשת לספריית Macrobenchmark כדי לאפשר תיעוד ואיפוס של פרופילים וניקוי של מטמון ה-shader.

הגדירו את האפליקציה שאתם בודקים כך שתהיה דומה ככל האפשר לגרסת ההפצה או לסביבת הייצור. צריך להגדיר אותה כלא ניתנת לניפוי באגים, ורצוי להפעיל את ההקטנה (minification) כדי לשפר את הביצועים. בדרך כלל עושים את זה על ידי יצירת עותק של גרסת ה-release, שפועלת באותו אופן אבל חתומה באופן מקומי באמצעות מפתחות ניפוי באגים. לחלופין, אתם יכולים להשתמש ב-initWith כדי להורות ל-Gradle לעשות את זה בשבילכם:

Kotlin

buildTypes {
    getByName("release") {
        isMinifyEnabled = true
        isShrinkResources = true
        proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
    }

    create("benchmark") {
        initWith(getByName("release"))
        signingConfig = signingConfigs.getByName("debug")
    }
}

מגניב

buildTypes {
    release {
        isMinifyEnabled = true
        isShrinkResources = true
        proguardFiles(
            getDefaultProguardFile("proguard-android-optimize.txt"),
            "keep-rules.pro"
        )
        // In real app, this would use its own release keystore
        signingConfig = signingConfigs.getByName("debug")
        baselineProfile.automaticGenerationDuringBuild = true
    }
}

כדי לוודא שהרצת נקודת ההשוואה גם יוצרת וגם בודקת את הגרסה הנכונה של האפליקציה, כמו שמוצג באיור 2, צריך לבצע את הפעולות הבאות:

  1. מבצעים סנכרון של Gradle.
  2. פותחים את החלונית Build Variants.
  3. בוחרים את גרסת ההשוואה לשוק של האפליקציה ושל מודול Macrobenchmark.
בוחרים את גרסת ההשוואה לשוק.
איור 2. בוחרים את הווריאציה להשוואה.

(אופציונלי) הגדרת אפליקציה עם כמה מודולים

אם באפליקציה יש יותר ממודול Gradle אחד, צריך לוודא שסקריפטים של ה-build יודעים איזה וריאנט build לקמפל. מוסיפים את המאפיין matchingFallbacks לסוג ה-build‏ benchmark של המודולים :macrobenchmark ו-:app. שאר המודולים של Gradle יכולים להישאר עם אותה הגדרה כמו קודם.

Kotlin

create("benchmark") {
    initWith(getByName("release"))
    signingConfig = signingConfigs.getByName("debug")

    matchingFallbacks += listOf("release")
 }

מגניב

benchmark {
    initWith buildTypes.release
    signingConfig signingConfigs.debug

    matchingFallbacks = ['release']
 }

בלי זה, סוג ה-build החדש שנוסף, benchmark, גורם ל-build להיכשל ומוצגת הודעת השגיאה הבאה:

> Could not resolve project :shared.
     Required by:
         project :app
      > No matching variant of project :shared was found.
      ...

כשבוחרים את וריאנט ה-build בפרויקט, בוחרים באפשרות benchmark למודולים :app ו-:macrobenchmark, ובאפשרות release לכל מודול אחר באפליקציה, כמו שמוצג באיור 3:

וריאנטים של השוואה לשוק לפרויקט עם כמה מודולים, עם סוגים נבחרים של גרסאות build להפצה ולהשוואה לשוק.
איור 3. וריאנטים של השוואה לשוק לפרויקט עם כמה מודולים, עם סוגים נבחרים של גרסאות build להפצה ולהשוואה לשוק.

מידע נוסף זמין במאמר פתרון שגיאות build שקשורות להתאמת וריאנטים.

(אופציונלי) הגדרת גרסאות המוצר

אם הגדרתם כמה גרסאות מוצר באפליקציה, צריך להגדיר את המודול :macrobenchmark כדי שהוא ידע איזו גרסת מוצר של האפליקציה לבנות ולבדוק.

בדוגמאות שבדף הזה נעשה שימוש בשתי גרסאות המוצר במודול :app, demo ו-production, כמו שמוצג בקטע הקוד הבא:

Kotlin

flavorDimensions += "environment"
productFlavors {
    create("demo") {
        dimension = "environment"
        // ...
    }
    create("production") {
        dimension = "environment"
        // ...
    }
}

מגניב

flavorDimensions 'environment'
productFlavors {
    demo {
        dimension 'environment'
        // ...
    }

    production {
        dimension 'environment'
        // ...
    }
}

בלי ההגדרה הזו, יכול להיות שתופיע שגיאת build דומה לזו שנגרמת על ידי כמה מודולים של Gradle:

Could not determine the dependencies of task ':macrobenchmark:connectedBenchmarkAndroidTest'.
> Could not determine the dependencies of null.
   > Could not resolve all task dependencies for configuration ':macrobenchmark:benchmarkTestedApks'.
      > Could not resolve project :app.
        Required by:
            project :macrobenchmark
         > The consumer was configured to find a runtime of a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'benchmark', attribute 'com.android.build.api.attributes.AgpVersionAttr' with value '7.3.0'. However we cannot choose between the following variants of project :app:
             -   demoBenchmarkRuntimeElements
             -   productionBenchmarkRuntimeElements
           All of them match the consumer attributes:
           ...

בשני הקטעים הבאים מתוארות דרכים להגדרת השוואה לשוק עם כמה גרסאות מוצר.

שימוש ב-missingDimensionStrategy

אם מציינים את missingDimensionStrategy ב-defaultConfig של המודול :macrobenchmark, מערכת ה-build תשתמש בקבוצת הגרסאות כברירת מחדל. ציינו באילו מאפיינים להשתמש אם אתם לא מוצאים אותם במודול. בדוגמה הבאה, הטעם production משמש כמאפיין ברירת המחדל:

Kotlin

defaultConfig {
    missingDimensionStrategy("environment", "production")
}

מגניב

defaultConfig {
    missingDimensionStrategy "environment", "production"
}

כך המודול :macrobenchmark יכול לבנות ולמדוד רק את גרסת המוצר שצוינה. זה שימושי אם אתם יודעים שרק לגרסת מוצר אחת יש את ההגדרות המתאימות למדידה.

הגדרת גרסאות מוצר במודול ‎ :macrobenchmark

אם רוצים ליצור גרסאות מוצר אחרות ולהשוות ביניהן, צריך להגדיר אותן במודול :macrobenchmark. מציינים אותו באופן דומה למודול :app, אבל מקצים רק את productFlavors ל-dimension. לא צריך להגדיר שום דבר אחר:

Kotlin

flavorDimensions += "environment"
productFlavors {
    create("demo") {
        dimension = "environment"
    }
    create("production") {
        dimension = "environment"
    }
}

מגניב

flavorDimensions 'environment'
productFlavors {
    demo {
        dimension 'environment'
    }

    production {
        dimension 'environment'
    }
}

אחרי שמגדירים ומסנכרנים את הפרויקט, בוחרים את וריאנט ה-build הרלוונטי בחלונית Build Variants, כמו שמוצג באיור 4:

גרסאות השוואה לפרויקט עם גרסאות מוצר, שבהן נבחרו productionBenchmark ו-release.
איור 4. ‫Benchmark variants for the project with product flavors showing "productionBenchmark" and "release" selected.

מידע נוסף זמין במאמר פתרון שגיאות build שקשורות להתאמת וריאנטים.

יצירת כיתה של Macrobenchmark

בדיקות השוואה מסופקות דרך ה-API של הכלל MacrobenchmarkRule JUnit4 בספריית Macrobenchmark. הוא מכיל את השיטה measureRepeated שמאפשרת לציין תנאים שונים להרצה ולבדיקה של אפליקציית היעד.

צריך לציין לפחות את ה-packageName של אפליקציית היעד, מה metrics רוצים למדוד, וכמה iterations הבדיקה צריכה לפעול.

Kotlin

@LargeTest
@RunWith(AndroidJUnit4::class)
class SampleStartupBenchmark {
    @get:Rule
    val benchmarkRule = MacrobenchmarkRule()

    @Test
    fun startup() = benchmarkRule.measureRepeated(
        packageName = TARGET_PACKAGE,
        metrics = listOf(StartupTimingMetric()),
        iterations = DEFAULT_ITERATIONS,
    ) {
        // starts default launch activity
        uiAutomator { startApp(TARGET_PACKAGE) }
    }
}

כדי לראות את כל האפשרויות להתאמה אישית של נקודת ההשוואה, עיינו בקטע התאמה אישית של נקודות ההשוואה.

השוואה בין אינטראקציות בממשק המשתמש של Compose

בדוגמה הקודמת נמדדת הפעלת האפליקציה, אבל לעיתים קרובות תרצו למדוד את הביצועים של ממשק המשתמש, כמו פריימים שהוחמצו (jank) בזמן הגלילה.

כדי לבצע השוואה של אפליקציית Jetpack Compose, למשל כדי למדוד את FrameTimingMetric בזמן גלילה ב-LazyColumn, אתם יכולים להשתמש ב-UI Automator כדי ליצור אינטראקציה ישירות עם צומתי ה-Compose, כמו שמוצג בקטע הקוד הבא.

@LargeTest
@RunWith(AndroidJUnit4::class)
class ComposeScrollBenchmark {
    @get:Rule
    val benchmarkRule = MacrobenchmarkRule()

    @Test
    fun scrollLazyColumn() = benchmarkRule.measureRepeated(
        packageName = "com.example.compose.app",
        metrics = listOf(FrameTimingMetric()),
        iterations = 5,
        startupMode = StartupMode.WARM,
        setupBlock = { pressHome() }
    ) {
        startActivityAndWait()

        // Find the Compose node using the testTag defined in your app
        val lazyColumn = device.findObject(By.res("my_lazy_column"))

        // Simulate a scroll gesture to measure FrameTimingMetric
        lazyColumn.setGestureMargin(device.displayWidth / 5)
        lazyColumn.fling(Direction.DOWN)
    }
}

הרצת הערכת הביצועים

מריצים את הבדיקה מתוך Android Studio כדי למדוד את ביצועי האפליקציה במכשיר. אפשר להריץ את נקודות ההשוואה באותו אופן שבו מריצים כל @Test אחר, באמצעות פעולת המרזב לצד מחלקת הבדיקה או השיטה, כפי שמוצג באיור 5.

מריצים את Macrobenchmark באמצעות הפעולה שמופיעה בשולי עורך הקוד לצד מחלקת הבדיקה.
איור 5. מריצים את Macrobenchmark באמצעות הפעולה שמופיעה בשולי עורך הקוד לצד מחלקת הבדיקה.

אפשר גם להריץ את כל בדיקות ההשוואה במודול Gradle משורת הפקודה, באמצעות הפקודה connectedCheck:

./gradlew :macrobenchmark:connectedCheck

כדי להריץ בדיקה אחת, מריצים את הפקודה הבאה:

./gradlew :macrobenchmark:connectedCheck -P android.testInstrumentationRunnerArguments.class=com.example.macrobenchmark.startup.SampleStartupBenchmark#startup

במאמר השוואה באינטגרציה רציפה (CI) מוסבר איך להריץ ולנטר נקודות השוואה באינטגרציה רציפה.

תוצאות ההערכה

אחרי הרצה מוצלחת של בדיקת ביצועים, המדדים מוצגים ישירות ב-Android Studio ומופקים לשימוש ב-CI בקובץ JSON. כל איטרציה שנמדדת מתעדת עקבות מערכת נפרדים. כדי לפתוח את תוצאות המעקב, לוחצים על הקישורים בחלונית Test Results, כמו שמוצג באיור 6:

תוצאות ההפעלה של Macrobenchmark.
איור 6. תוצאות ההפעלה של Macrobenchmark.

כשהמעקב נטען, Android Studio מבקש לבחור את התהליך לניתוח. הבחירה מאוכלסת מראש בתהליך של אפליקציית היעד, כפי שמוצג באיור 7:

בחירת תהליך המעקב ב-Studio.
איור 7. בחירת תהליך המעקב ב-Studio.

אחרי שקובץ המעקב נטען, ב-Studio מוצגות התוצאות בכלי CPU Profiler:

מעקב ב-Studio.
איור 8. Studio trace.

דוחות JSON וכל עקבות הפרופילים מועתקים גם הם באופן אוטומטי מהמכשיר למארח. הם נכתבים במכונה המארחת במיקום הבא:

project_root/module/build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/device_id/

גישה ידנית לקובצי מעקב

אם רוצים להשתמש בכלי Perfetto כדי לנתח קובץ מעקב, צריך לבצע שלבים נוספים. בעזרת Perfetto אפשר לבדוק את כל התהליכים שמתרחשים במכשיר במהלך המעקב, ואילו ב-CPU Profiler ב-Android Studio אפשר לבדוק רק תהליך אחד.

אם מפעילים את הבדיקות מ-Android Studio או משורת הפקודה של Gradle, קובצי המעקב מועתקים באופן אוטומטי מהמכשיר למארח. הם נכתבים במכונה המארחת במיקום הבא:

project_root/module/build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/device_id/TrivialStartupBenchmark_startup[mode=COLD]_iter002.perfetto-trace

כשהקובץ של המעקב נמצא במערכת המארחת, אפשר לפתוח אותו ב-Android Studio באמצעות File > Open (קובץ > פתיחה) בתפריט. כאן מוצגת התצוגה של כלי הפרופיילינג שהוצגה בקטע הקודם.

שגיאות בהגדרה

אם האפליקציה לא מוגדרת כמו שצריך – ניתן לבצע בה ניפוי באגים או שלא ניתן ליצור לה פרופיל – Macrobenchmark מחזיר שגיאה במקום לדווח על מדידה שגויה או חלקית. אפשר להסתיר את השגיאות האלה באמצעות הארגומנט androidx.benchmark.suppressErrors.

בנוסף, Macrobenchmark מחזירה שגיאות כשמנסים למדוד אמולטור או מכשיר עם סוללה חלשה, מה שעלול לפגוע בזמינות הליבה ובמהירות השעון.

התאמה אישית של נקודות ההשוואה

הפונקציה measureRepeated מקבלת פרמטרים שונים שמשפיעים על המדדים שהספרייה אוספת, על האופן שבו האפליקציה מופעלת ומקומפלת, או על מספר החזרות שהבנצ'מרק מריץ.

תיעוד המדדים

המדדים הם סוג המידע העיקרי שמופק מההשוואות לשוק. המדדים הבאים זמינים:

מידע נוסף על מדדים זמין במאמר תיעוד מדדי Macrobenchmark.

שיפור נתוני המעקב באמצעות אירועים בהתאמה אישית

כדאי להוסיף לאפליקציה אירועי מעקב מותאמים אישית, שמופיעים עם שאר דוח המעקב ויכולים לעזור לכם לזהות בעיות ספציפיות לאפליקציה. מידע נוסף על יצירת אירועי מעקב מותאמים אישית זמין במאמר הגדרת אירועים מותאמים אישית.

CompilationMode

ב-Macrobenchmark אפשר לציין CompilationMode, שמגדיר כמה מהאפליקציה צריך לעבור הידור מראש מקוד בייט של DEX (פורמט קוד הבייט ב-APK) לשפת מכונה (בדומה ל-C++‎ שעבר הידור מראש).

כברירת מחדל, בדיקות Macrobenchmark פועלות עם CompilationMode.DEFAULT, שמתקין פרופיל Baseline – אם הוא זמין – ב-Android 7 (רמת API‏ 24) ואילך. אם אתם משתמשים ב-Android 6 (רמת API‏ 23) או בגרסה קודמת, מצב הקימפול מקמפל את ה-APK באופן מלא כהתנהגות ברירת המחדל של המערכת.

אתם יכולים להתקין פרופיל Baseline אם אפליקציית היעד מכילה גם פרופיל Baseline וגם את הספרייה ProfileInstaller.

ב-Android מגרסה 7 ואילך, אפשר להתאים אישית את CompilationMode כדי להשפיע על כמות ההידור המקדים במכשיר, כדי לדמות רמות שונות של הידור מראש (AOT) או שמירה במטמון של JIT. למידע נוסף: CompilationMode.Full, CompilationMode.Partial, CompilationMode.None ו-CompilationMode.Ignore.

התכונה הזו מבוססת על פקודות קומפילציה של ART. כל בדיקה מוחקת את נתוני הפרופיל לפני שהיא מתחילה, כדי לוודא שאין הפרעות בין הבדיקות.

StartupMode

כדי להפעיל פעילות, אפשר להעביר מצב הפעלה מוגדר מראש: COLD,‏ WARM או HOT. הפרמטר הזה משנה את אופן ההפעלה של הפעילות ואת מצב התהליך בתחילת הבדיקה.

מידע נוסף על סוגי ההפעלה זמין במאמר זמן ההפעלה של האפליקציה.

דוגמאות

פרויקט לדוגמה זמין ב-Macrobenchmark Sample במאגר ב-GitHub.

שליחת משוב

כדי לדווח על בעיות או להגיש בקשות להוספת תכונות ל-Jetpack Macrobenchmark, אפשר להשתמש בכלי הציבורי למעקב אחר בעיות.