نوشتن Microbenchmark

برای آشنایی با نحوه استفاده از کتابخانه Microbenchmark با افزودن تغییرات به کد برنامه، به بخش شروع سریع مراجعه کنید. برای آشنایی با نحوه تکمیل راه‌اندازی کامل با تغییرات پیچیده‌تر در پایگاه کد، به بخش راه‌اندازی کامل پروژه مراجعه کنید.

راه‌اندازی سریع

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

برای انجام محک‌زنی یک‌باره، کارهای زیر را انجام دهید:

  1. کتابخانه را به فایل build.gradle یا build.gradle.kts واحدتان اضافه کنید:

    کاتلین

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

    شیک

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

    به‌جای وابستگی androidTestImplementation، از وابستگی implementation استفاده کنید. اگر از androidTestImplementation استفاده کنید، محک‌ها اجرا نمی‌شوند زیرا مانیفست کتابخانه در مانیفست برنامه ادغام نشده است.

  2. نوع ساخت debug را به‌روز کنید تا قابل‌اشکال‌زدایی نباشد:

    کاتلین

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

    شیک

    android {
        ...
        buildTypes {
            debug {
                debuggable false
            }
        }
    }
  3. تغییر دادن testInstrumentationRunner به AndroidBenchmarkRunner:

    کاتلین

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

    شیک

    android {
        ...
        defaultConfig {
            testInstrumentationRunner "androidx.benchmark.junit4.AndroidBenchmarkRunner"
        }
    }
  4. برای افزودن محک، نمونه‌ای از BenchmarkRule را در فایل آزمایشی در دایرکتوری androidTest اضافه کنید. برای اطلاعات بیشتر درباره نوشتن محک‌ها، به ایجاد کلاس Microbenchmark مراجعه کنید.

    تکه‌کد زیر نحوه افزودن محک به آزمایش Instrumented را نشان می‌دهد:

    کاتلین

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

    جاوا

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

برای آشنایی با نحوه نوشتن محک، به ایجاد کلاس Microbenchmark بروید.

راه‌اندازی کامل پروژه

برای راه‌اندازی محک‌زنی منظم به‌جای محک‌زنی یک‌باره، محک‌ها را در واحد خودشان جدا کنید. این کار کمک می‌کند تا مطمئن شوید پیکربندی آن‌ها، مثلاً تنظیم debuggable روی false، از آزمایش‌های معمول جدا است.

چون Microbenchmark کد شما را مستقیماً اجرا می‌کند، کدی را که می‌خواهید محک‌زنی کنید در یک واحد Gradle جداگانه قرار دهید و وابستگی به آن واحد را همان‌طور که در شکل ۱ نشان داده شده است تنظیم کنید.

ساختار برنامه
شکل ۱. ساختار برنامه با واحدهای Gradle :app، :microbenchmark، و :benchmarkable، که به Microbenchmarks اجازه می‌دهد کد را در واحد :benchmarkable محک‌زنی کند.

برای افزودن یک ماژول Gradle جدید، می‌توانید از دستیار ماژول در Android Studio استفاده کنید. جادوگر ماژولی ایجاد می‌کند که برای محک‌زنی ازپیش پیکربندی شده است، و فهرست راهنمای محک‌زنی به آن اضافه شده و debuggable روی false تنظیم شده است.

  1. روی پروژه یا واحدتان در پانل پروژه در Android Studio کلیک راست کنید و روی جدید > واحد کلیک کنید.

  2. معیار را در قاب الگوها انتخاب کنید.

  3. Microbenchmark را به‌عنوان نوع پیمانه محک انتخاب کنید.

  4. برای نام پیمانه، «microbenchmark» را تایپ کنید.

  5. روی پایان کلیک کنید.

پیکربندی واحد کتابخانه جدید
شکل ۲. افزودن پیمانه Gradle جدید در «استودیو Android» Bumblebee.

پس‌از ایجاد ماژول، فایل build.gradle یا build.gradle.kts آن را تغییر دهید و androidTestImplementation را به ماژول حاوی کد برای محک‌زنی اضافه کنید:

کاتلین

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

شیک

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

ایجاد کلاس Microbenchmark

محک‌ها آزمایش‌های ابزارگری استاندارد هستند. برای ایجاد محک، از کلاس BenchmarkRule ارائه‌شده توسط کتابخانه استفاده کنید. برای محک زدن فعالیت‌ها، از ActivityScenario یا ActivityScenarioRule استفاده کنید. برای محک‌زنی کد میانای کاربر، از @UiThreadTest استفاده کنید.

کد زیر نمونه‌ای از محک را نشان می‌دهد:

کاتلین

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

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

جاوا

@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{} اندازه‌گیری شود غیرفعال کنید. این بخش‌ها معمولاً نشان‌دهنده کدی هستند که باید در هر تکرار محک اجرا کنید.

کاتلین

// 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)
}
    

جاوا

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 دیگری بااستفاده از کنش حاشیه کنار کلاس یا روش آزمایشی خود اجرا کنید، همان‌طور که در شکل ۳ نشان داده شده است.

اجرای محک‌زنی خرد
شکل ۳. اجرای آزمایش Microbenchmark بااستفاده از کنش حاشیه در کنار کلاس آزمایش.

یا از خط فرمان، connectedCheck را اجرا کنید تا همه آزمایش‌ها از واحد Gradle مشخص‌شده اجرا شود:

./gradlew benchmark:connectedCheck

یا یک آزمایش تکی:

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

نتایج محک‌زنی

پس‌از اجرای موفقیت‌آمیز Microbenchmark، سنجه‌ها مستقیماً در Android Studio نمایش داده می‌شوند و گزارش کامل محک با سنجه‌های اضافی و اطلاعات دستگاه در قالب JSON دردسترس است.

نتایج محک‌زنی خرد
شکل ۴. نتایج محک‌زنی خرد.

گزارش‌های 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 قرار دارد.

خطاهای پیکربندی

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

  • «قابل‌اشکال‌زدایی» روی false تنظیم شده است.
  • از دستگاه فیزیکی استفاده می‌شود—شبیه‌سازها پشتیبانی نمی‌شوند.
  • اگر دستگاه ریشه‌یابی شده باشد، ساعت‌ها قفل می‌شوند.
  • میزان شارژ باتری دستگاه حداقل ۲۵٪ باشد.

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

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

می‌توانید این را از اسکریپت Gradle خود تنظیم کنید، همان‌طور که در مثال زیر نشان داده شده است:

کاتلین

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

شیک

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

همچنین می‌توانید خطاها را از خط فرمان سرکوب کنید:

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

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