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

בקטע מדריך למתחילים מוסבר איך להשתמש בספריית Microbenchmark על ידי הוספת שינויים לקוד האפליקציה. בקטע Full project setup (הגדרת הפרויקט המלא) מוסבר איך לבצע הגדרה מלאה, כולל שינויים מורכבים יותר ב-codebase.

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

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

כדי לבצע השוואה חד-פעמית:

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

במאמר יצירת כיתה ב-Microbench מוסבר איך כותבים נקודת השוואה.

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

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

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

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

כדי להוסיף מודול 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 שסופקה בספרייה. כדי ליצור נקודות השוואה לפעילויות, משתמשים ב-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.

הפעלת בדיקת מיקרו-ביצועים
איור 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_.