תיעוד מדדי מאקרובנצ'מרק

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

קטע הקוד הבא מתעד את מדדי זמן רינדור הפריים ומדדי המקטע של המעקב המותאם אישית בממשק פריסה עצלה של Jetpack Compose:

@OptIn(ExperimentalMetricApi::class)
    @Test
    fun scrollComposeList() {
        benchmarkRule.measureRepeated(
            // [START_EXCLUDE]
            packageName = TARGET_PACKAGE,
            metrics = listOf(
                FrameTimingMetric(),
                // Measure power usage. This is supported on Pixel 6 and later.
                PowerMetric(PowerMetric.Type.Power(
                    mapOf(
                        PowerCategory.CPU to PowerCategoryDisplayLevel.TOTAL,
                        PowerCategory.DISPLAY to PowerCategoryDisplayLevel.TOTAL,
                        PowerCategory.GPU to PowerCategoryDisplayLevel.TOTAL,
                        PowerCategory.NETWORK to PowerCategoryDisplayLevel.TOTAL,
                    )
                )),
                // Measure custom trace sections by name EntryRow (which is added to the EntryRow composable).
                // Mode.Sum measures combined duration and also how many times it occurred in the trace.
                // This way, you can estimate whether a composable recomposes more than it should.
                TraceSectionMetric("EntryRowCustomTrace", TraceSectionMetric.Mode.Sum),
                // This trace section takes into account the SQL wildcard character %,
                // which can find trace sections without the full name.
                // This way, you can measure composables produced by the composition tracing
                // and measure how long they took and how many times they recomposed.
                // WARNING: This metric only shows results when running with composition tracing, otherwise it won't be visible in the outputs.
                TraceSectionMetric("%EntryRow%", TraceSectionMetric.Mode.Sum),
            ),
            // Try switching to different compilation modes to see the effect
            // it has on frame timing metrics.
            compilationMode = CompilationMode.None(),
            startupMode = StartupMode.WARM, // restarts activity each iteration
            iterations = DEFAULT_ITERATIONS,
            // [END_EXCLUDE]
            setupBlock = {
                uiAutomator {
                    // Before starting to measure, navigate to the UI to be measured.
                    startIntent(Intent("$packageName.COMPOSE_ACTIVITY"))
                }
            }
        ) {
            uiAutomator {
                onElement { isScrollable }.fling(Direction.DOWN)
            }
        }
    }

בדוגמה הבאה, EntryRowCustomTrace מייצג קטע נתוני מעקב בהתאמה אישית שמוגדר בתוך שכבות הפריטים הקומפוזביליים באמצעות wrapper הבלוקים הרגיל של Kotlin trace(sectionName) { ... }. כדי לספק נתונים עבור TraceSectionMetric, צריך לעטוף את רכיבי ממשק המשתמש של היעד בתוך בסיס הקוד של האפליקציה בסביבת הייצור באמצעות מעטפת הבלוקים הסטנדרטית של Jetpack runtime‏ trace:

@Composable
private fun EntryRow(entry: Entry, modifier: Modifier = Modifier) = trace("EntryRowCustomTrace") {
    Card(modifier = modifier) {
        Row(verticalAlignment = Alignment.CenterVertically) {
            Text(
                text = entry.contents,
                modifier = Modifier
                    .padding(16.dp)
                    .wrapContentSize()
            )

            Spacer(modifier = Modifier.weight(1f))

            Checkbox(
                checked = false,
                onCheckedChange = {},
                modifier = Modifier.padding(16.dp)
            )
        }
    }
}

תוצאות הערכת הביצועים מוצגות ישירות בכרטיסיית הטרמינל Benchmark ב-Android Studio, כמו שמוצג באיור 1. אם מגדירים כמה מדדים, כל הנקודות המחושבות שלהם על הגרף משולבות בחלון הסיכום.

תוצאות של TraceSectionMetric ו-FrameTimingMetric.
תרשים 1. תוצאות משולבות של TraceSectionMetric ו-FrameTimingMetric במסוף לפריסה מודרנית של Compose.

בהמשך מפורטים השלבים StartupTimingMetric,‏ FrameTimingMetric,‏ TraceSectionMetric ו-PowerMetric. כדי לראות את הרשימה המלאה של מדדי ההשוואה לשוק, עיינו במחלקות המשנה של Metric במאמרי העזרה של ה-API.

StartupTimingMetric

‫StartupTimingMetric מתעד מדדי תזמון של הפעלת האפליקציה עם הערכים הבאים:

  • timeToInitialDisplayMs: משך הזמן מהרגע שהמערכת מקבלת Intent להפעלה ועד שהיא מעבדת את הפריים הראשון של מסך היעד.
  • ‫timeToFullDisplayMs: משך הזמן מרגע שהמערכת מקבלת כוונה להפעלה ועד שהאפליקציה מדווחת על ציור מלא באמצעות מנגנוני הדיווח הפנימיים של הפלטפורמה. המדידה מסתיימת כשהמסגרת הראשונה נטענת אחרי האות לטעינה מלאה, או כשהיא מכילה את האות הזה.

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

בארכיטקטורה שמתמקדת ב-Compose, אל תנסו להפעיל את activity.reportFullyDrawn באופן ידני. במקום זאת, השתמשו בכלי העזר האסינכרוניים הבטוחים לפיתוח נייטיב ReportDrawn,‏ ReportDrawnWhen או ReportDrawnAfter בתוך רכיבי ה-Composable של המסך כדי לאותת באופן אוטומטי ל-Macrobenchmark כשנתוני הרשת האסינכרוניים או מצבי ממשק המשתמש המורכבים מסיימים את הרינדור.

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

FrameTimingMetric

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

  • ‫frameOverrunMs: משך הזמן שבו פריים מסוים חורג מהמועד האחרון שלו. מספרים חיוביים מציינים פריים שהושמט, וגם קפיצות או קטיעות שאפשר לראות. מספרים שליליים מציינים כמה מהר יותר הושלם פריים ביחס למועד האחרון של חומרת תת-המערכת. הערה: המדד הזה זמין רק ב-Android 12 (רמת API ‏31) ואילך.
  • ‫frameDurationCpuMs: משך הזמן שבו הפריימים נוצרו באופן פעיל ב-CPU, גם בשרשור ה-UI של האפליקציה הראשית וגם ב-Compose RenderThread.

המדידות האלה נאספות בהתפלגות של האחוזונים ה-50, ה-90, ה-95 וה-99:

frameDurationCpuMs P50 3.5, P90 6.0, P95 6.4, P99 11.0
frameOverrunMs P50 -11.6, P90 -7.2, P95 -7.1, P99 -1.2

כשמבצעים אופטימיזציה של היררכיות הפריסה של Jetpack Compose, צריך לבדוק את הפריימים עם הביצועים הכי גרועים (הגבולות של P95 ו-P99). אם הערך של frameOverrunMs עולה למספרים שלמים חיוביים באחוזונים הגבוהים, זה מצביע על כך שההרכבות מחדש מעכבות את ה-thread הראשי במהלך אנימציות גלילה כבדות.

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

TraceSectionMetric

TraceSectionMetric מתעד את מספר הפעמים שבהן קטע מעקב ספציפי מתרחש ואת משך הזמן המוחלט שלוקח להפעיל אותו. למעקב אחר זמן, הפלט הוא הזמן המינימלי, החציוני והמקסימלי באלפיות השנייה. הקטע של מעקב היעד מוגדר על ידי הקריאה להפעלת הפונקציה trace(sectionName) או על ידי גבולות הבלוק ברמה נמוכה יותר בין Trace.beginSection(sectionName) לבין Trace.endSection() או הווריאציות האסינכרוניות שלהם.

EntryRowCustomTraceCount min 20.0, median 28.0, max 50.0
EntryRowCustomTraceSumMs min 34.9, median 44.4, max 66.6

כברירת מחדל, המדד מציג רק קטעי מעקב שנאספו ישירות מהקבצים הבינאריים של חבילת האפליקציה שלכם. כדי לכלול תהליכים שמקורם מחוץ לגבולות החבילה של האפליקציה, צריך להגדיר את המאפיין targetPackageOnly = false.

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

כדי ליצור מעקבים ידניים של כלי הניתוח, מספיק להוסיף את יחסי התלות של androidx.compose.runtime:runtime-tracing לאפליקציית היעד. עם זאת, כדי לתעד את המעקבים האלה באופן פרוגרמטי בהרצה של Macrobenchmark, נדרשת הגדרה נוספת במודול של נקודת ההשוואה.

הוראות מפורטות להגדרה מופיעות במאמר תיעוד מעקב באמצעות Jetpack Macrobenchmark.

PowerMetric

‫PowerMetric מתעד את השינוי בהספק או באנרגיה במהלך הרצת Macrobenchmark. כל קטגוריה שנבחרה מחולקת לרכיבי החומרה שניתן למדוד, והקטגוריות שלא נבחרו מקובצות בקטגוריה "לא נבחרו".

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

המדד מפיק שתי מדידות לכל קטגוריה:

  • ‫power<category>Uw: כמות האנרגיה שנצרכה במהלך הבדיקה בקטגוריה הזו (נמדדת במיקרו-ואט).
  • ‫energy<category>Uws: כמות האנרגיה הכוללת שהועברה ליחידת זמן במהלך הבדיקה בקטגוריה הזו (נמדדת במיקרו-ואט לשנייה).

הקטגוריות כוללות:

  • CPU
  • DISPLAY
  • GPU
  • GPS
  • MEMORY
  • MACHINE_LEARNING
  • NETWORK
  • UNCATEGORIZED

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

powerCategoryCpuUw min 300.2, median 346.1, max 519.6
powerCategoryDisplayUw min 319.8, median 325.8, max 329.7
powerCategoryGpuUw min 18.8, median 23.3, max 36.9
powerCategoryNetworkUw min 97.3, median 123.3, max 681.3
powerTotalUw min 1234.8, median 1316.6, max 2112.4
powerUnselectedUw       min  483.3,  median  512.6,  max  561.7

ניתוח של תת-מערכות ליבה

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

הפלט של הטרמינל ממפה את ההגדרה שביקשתם:

  • ‫powerCategoryCpuUw: כמות החשמל שהמעבד צורך במהלך הבדיקה.
  • ‫powerCategoryGpuUw: כמות האנרגיה שצרך ה-GPU במהלך הבדיקה.
  • ‫powerUnselectedUw: צריכת החשמל המצטברת של כל קטגוריות החומרה הזמינות שלא ביקשתם במפורש במפת האתחול.

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

מקורות מידע נוספים

צפייה בתוכן