כתיבת נתוני מיקרובנצ'מרק

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

מדריך למתחילים

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

כדי לבצע בדיקת ביצועים חד-פעמית:

  1. מוסיפים את הספרייה לקובץ build.gradle או build.gradle.kts של המודול:

    Kotlin

    dependencies {
        implementation("androidx.benchmark:benchmark-junit4:1.2.4")
    }
    

    Groovy

    dependencies {
        implementation 'androidx.benchmark:benchmark-junit4:1.2.4'
    }
    

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

  2. מעדכנים את סוג ה-build של debug כך שלא ניתן יהיה לנפות באגים בו:

    Kotlin

    android {
        ...
        buildTypes {
            debug {
                isDebuggable = false
            }
        }
    }
    

    Groovy

    android {
        ...
        buildTypes {
            debug {
                debuggable false
            }
        }
    }
    
  3. משנים את הערך של testInstrumentationRunner ל-AndroidBenchmarkRunner:

    Kotlin

    android {
        ...
        defaultConfig {
            testInstrumentationRunner = "androidx.benchmark.junit4.AndroidBenchmarkRunner"
        }
    }
    

    Groovy

    android {
        ...
        defaultConfig {
            testInstrumentationRunner "androidx.benchmark.junit4.AndroidBenchmarkRunner"
        }
    }
    
  4. מוסיפים מופע של BenchmarkRule בקובץ בדיקה בספרייה androidTest כדי להוסיף את מדד הביצועים. מידע נוסף על כתיבת מדדי ביצועים זמין במאמר יצירת כיתה של מדדי ביצועים מיקרו.

    בקטע הקוד הבא מוצג איך מוסיפים בדיקת ביצועים למבחן עם כלי למדידת ביצועים:

    Kotlin

    @RunWith(AndroidJUnit4::class)
    class SampleBenchmark {
        @get:Rule
        val benchmarkRule = BenchmarkRule()
    
        @Test
        fun benchmarkSomeWork() {
            benchmarkRule.measureRepeated {
                doSomeWork()
            }
        }
    }
    

    Java

    @RunWith(AndroidJUnit4.class)
    class SampleBenchmark {
        @Rule
        public BenchmarkRule benchmarkRule = new BenchmarkRule();
    
        @Test
        public void benchmarkSomeWork() {
                BenchmarkRuleKt.measureRepeated(
                    (Function1<BenchmarkRule.Scope, Unit>) scope -> doSomeWork()
                );
           }
        }
    }
    

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

הגדרה מלאה של הפרויקט

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

מכיוון ש-Microbenchmark מפעיל את הקוד ישירות, צריך להציב את הקוד שרוצים למדוד במודול נפרד ב-Gradle ולהגדיר את התלות באותו מודול, כפי שמוצג באיור 1.

מבנה האפליקציה
איור 1. מבנה אפליקציה עם מודולים של Gradle‏ :app,‏ :microbenchmark ו-:benchmarkable, שמאפשרים לבצע בדיקות ביצועים של קוד במודול :benchmarkable באמצעות Microbenchmarks.

כדי להוסיף מודול Gradle חדש, אפשר להשתמש באשף המודול ב-Android Studio. האשף יוצר מודול שמוגדר מראש לבדיקת ביצועים, עם הוספה של ספריית ביצועים והגדרה של debuggable כ-false.

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

  2. בוחרים באפשרות Benchmark בחלונית Templates.

  3. בוחרים באפשרות Microbenchmark כסוג המודול של מדד הביצועים.

  4. מקלידים microbenchmark בשם המודול.

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

הגדרת מודול ספרייה חדש
איור 2. הוספת מודול Gradle חדש ב-Android Studio Bumblebee

אחרי שיוצרים את המודול, משנים את הקובץ build.gradle או build.gradle.kts שלו ומוסיפים את androidTestImplementation למודול שמכיל את הקוד ליצירת נקודת השוואה:

Kotlin

dependencies {
    // The module name might be different.
    androidTestImplementation(project(":benchmarkable"))
}

Groovy

dependencies {
    // The module name might be different.
    androidTestImplementation project(':benchmarkable')
}

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

מדדי ביצועים הם בדיקות סטנדרטיות של אינסטרומנטציה. כדי ליצור מדד ביצועים, משתמשים בכיתה BenchmarkRule שסופקת על ידי הספרייה. כדי ליצור נכסי benchmark לפעילויות, משתמשים ב-ActivityScenario או ב-ActivityScenarioRule. כדי לבצע בדיקת ביצועים של קוד ממשק משתמש, משתמשים ב-@UiThreadTest.

הקוד הבא מציג מדד ביצועים לדוגמה:

Kotlin

@RunWith(AndroidJUnit4::class)
class SampleBenchmark {
    @get:Rule
    val benchmarkRule = BenchmarkRule()

    @Test
    fun benchmarkSomeWork() {
        benchmarkRule.measureRepeated {
            doSomeWork()
        }
    }
}
    

Java

@RunWith(AndroidJUnit4.class)
class SampleBenchmark {
    @Rule
    public BenchmarkRule benchmarkRule = new BenchmarkRule();

    @Test
    public void benchmarkSomeWork() {
        final BenchmarkState state = benchmarkRule.getState();
        while (state.keepRunning()) {
            doSomeWork();
        }
    }
}
    

השבתת התזמון להגדרה

אפשר להשבית את התזמון של קטעי קוד שאתם לא רוצים למדוד באמצעות הבלוק runWithTimingDisabled{}. בדרך כלל, הקטעים האלה מייצגים קוד שצריך להריץ בכל חזרה של מדד הביצועים.

Kotlin

// using random with the same seed, so that it generates the same data every run
private val random = Random(0)

// create the array once and just copy it in benchmarks
private val unsorted = IntArray(10_000) { random.nextInt() }

@Test
fun benchmark_quickSort() {
    // ...
    benchmarkRule.measureRepeated {
        // copy the array with timing disabled to measure only the algorithm itself
        listToSort = runWithTimingDisabled { unsorted.copyOf() }

        // sort the array in place and measure how long it takes
        SortingAlgorithms.quickSort(listToSort)
    }

    // assert only once not to add overhead to the benchmarks
    assertTrue(listToSort.isSorted)
}
    

Java

private final int[] unsorted = new int[10000];

public SampleBenchmark() {
    // Use random with the same seed, so that it generates the same data every
    // run.
    Random random = new Random(0);

    // Create the array once and copy it in benchmarks.
    Arrays.setAll(unsorted, (index) -> random.nextInt());
}

@Test
public void benchmark_quickSort() {
    final BenchmarkState state = benchmarkRule.getState();

    int[] listToSort = new int[0];

    while (state.keepRunning()) {
        
        // Copy the array with timing disabled to measure only the algorithm
        // itself.
        state.pauseTiming();
        listToSort = Arrays.copyOf(unsorted, 10000);
        state.resumeTiming();
        
        // Sort the array in place and measure how long it takes.
        SortingAlgorithms.quickSort(listToSort);
    }

    // Assert only once, not to add overhead to the benchmarks.
    assertTrue(SortingAlgorithmsKt.isSorted(listToSort));
}
    

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

הרצת נקודת ההשוואה

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

הפעלת בדיקת Microbenchmark
איור 3. מריצים בדיקת Microbenchmark באמצעות הפעולה בסרגל

לחלופין, אפשר להריץ את הפקודה connectedCheck משורת הפקודה כדי להריץ את כל הבדיקות מהמודול שצוין ב-Gradle:

./gradlew benchmark:connectedCheck

או בדיקה אחת:

./gradlew benchmark:connectedCheck -P android.testInstrumentationRunnerArguments.class=com.example.benchmark.SampleBenchmark#benchmarkSomeWork

תוצאות של בנצ'מרק

אחרי הרצת Microbenchmark מוצלחת, המדדים מוצגים ישירות ב-Android Studio, ודוח מלא של מדדי ביצועים עם מדדים נוספים ומידע על המכשיר זמין בפורמט JSON.

תוצאות של מיקרו-בנצ&#39;מרק
איור 4. תוצאות של בדיקות מיקרו-בנצ'מרק.

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

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

כברירת מחדל, דוח ה-JSON נכתב בדיסק במכשיר בתיקיית המדיה המשותפת החיצונית של קובץ ה-APK לבדיקה, שנמצאת בדרך כלל ב-/storage/emulated/0/Android/media/**app_id**/**app_id**-benchmarkData.json.

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

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

  • הערך של Debuggable מוגדר כ-false.
  • נעשה שימוש במכשיר פיזי – אין תמיכה באמולטורים.
  • השעונים ננעלים אם המכשיר עבר תהליך רוט (Root).
  • רמת הטעינה של הסוללה במכשיר צריכה להיות לפחות 25%.

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

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

אפשר להגדיר את זה בסקריפט של Gradle, כמו בדוגמה הבאה:

Kotlin

android {
    defaultConfig {
       …
      testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "DEBUGGABLE,LOW-BATTERY"
    }
}

Groovy

android {
    defaultConfig {
       …
      testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "DEBUGGABLE,LOW-BATTERY"
    }
}

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

$ ./gradlew :benchmark:connectedCheck -P andoidtestInstrumentationRunnerArguments.androidx.benchmark.supperssErrors=DEBUGGABLE,LOW-BATTERY

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