+5

Cải thiện performance của Android App với Benchmarking

Giới thiệu

Chào các ae Android dev 😄. Chúng ta hãy bắt đầu bài này bằng một vài câu hỏi. Bao nhiêu trong số bạn đã xây dựng một ứng dụng được sử dụng bởi hơn 100 người? Bạn đã nhận phản hồi chưa? Mọi người có hài lòng với hiệu suất ứng dụng không?

Khi nói về hiệu suất của ứng dụng, ý của tôi là sự mượt mà khi user sử dụng app, tức là việc hoạt động của ứng dụng mà không có bất kỳ giật lag nào.

Oh, việc giật lag của một ứng dụng phụ thuộc vào thiết bị mà người dùng đang sử dụng. Phải không? Vâng, đúng vậy, nhưng bạn có thể cải thiện hiệu suất của ứng dụng của mình bằng cách cải thiện mã nguồn của bạn (bất kể cấu hình thiết bị).

Trong bài viết này, chúng ta sẽ tìm hiểu cách bạn có thể cải thiện hiệu suất ứng dụng Android bằng cách sử dụng Benchmarking. Benchmarking là gì? Chúng ta sẽ tìm hiểu về nó qua các phần bên dưới này nhé

  1. Code measurement
  2. Benchmarking là gì?
  3. Thư viện Benchmark Jetpack
  4. Tích hợp với Android Studio
  5. Cấu hình Benchmark
  6. Các yếu tố ảnh hưởng đến Benchmarking
  7. Không nên Benchmark mọi thứ

Vậy hãy chuẩn bị 1 tách cà phê vì đây sẽ là một bài viết dài và sau đó thưởng thức cà phê với một chút hương vị Benchmark. Hãy bắt đầu nhé.

Code Measurement

Ngoài phần cứng của các thiết bị, hiệu suất của một ứng dụng Android cũng phụ thuộc vào code mà bạn đã sử dụng trong ứng dụng của mình. Việc lựa chọn đúng thuật toán và cấu trúc dữ liệu luôn nên là ưu tiên của bạn. Đây là các tối ưu nhỏ mà bạn có thể thực hiện trong ứng dụng của mình với sự giúp đỡ của những thay đổi này. Vì vậy, để đảm bảo ứng dụng của bạn hoạt động tốt trên nhiều loại thiết bị, hãy đảm bảo code của bạn hiệu quả ở mọi cấp độ và điều này sẽ tối ưu hiệu suất của bạn.

Bạn không nên viết code mà ứng dụng của bạn không cần. Hơn nữa, đừng phân bổ bộ nhớ nếu bạn có thể tránh được điều đó. Một số gợi ý để viết mã hiệu quả có thể là:

  1. Đối tượng không cần thiết: Tránh tạo đối tượng không cần thiết vì khi bạn tạo nhiều đối tượng hơn trong ứng dụng của mình thì ứng dụng sẽ chiếm nhiều bộ nhớ hơn.
  2. Ưu tiên static hơn virtual: Sử dụng phương thức static thay vì virtual có thể nhanh hơn 15–20%. Vì vậy, hãy cố gắng sử dụng nó khi bạn không cần truy cập các trường của một đối tượng.
  3. Sử dụng vòng lặp for-each: Thay vì sử dụng vòng lặp for thông thường hoặc các vòng lặp khác, ưu tiên sử dụng vòng lặp for-each, tức là vòng lặp for tăng cường cho các bộ sưu tập thực hiện giao diện Iterable và cho mảng.
  4. Tránh sử dụng floating-point: Hãy cố gắng tránh sử dụng floating-point vì chúng chậm hơn gấp đôi so với số nguyên trên thiết bị Android.

Vậy đây là một số gợi ý có thể sử dụng để cải thiện hiệu suất mã. Nhưng làm thế nào tôi có thể đánh giá sự khác biệt giữa thời gian thực hiện trước đây với code cũ của tôi và thời gian thực hiện hiện tại với code được cải thiện? Vậy có cách nào để đo lường hiệu suất mã? Có cách nào để đo lường bao lâu một đoạn code chạy? Câu trả lời là có, gọi là Benchmarking. Nhưng trước khi chuyển sang Benchmarking, hãy xem một số giải pháp đơn giản để tính thời gian mà một đoạn mã chạy.

@Test
fun codeMeasurement() {
    val worker = TestListenableWorkerBuilder<MyWorker>(context).build() //jetpack workmanager library
    val start = java.lang.System.nanoTime() //for starting time of work
    worker.doWork() //do some work i.e. code to be measured
    val elapsed = (java.lang.System.nanoTime() - start) //time taken to complete the work
    Log.d("Code Measurement", "Time taken was $elapsed ns")
}

Ở ví dụ trên, chúng ta đang tính thời gian để thực hiện công việc bằng cách trừ thời gian kết thúc với thời gian bắt đầu. Nhưng vấn đề ở đây là, chúng ta sẽ nhận được kết quả khác nhau mỗi khi chạy cùng một code do các vấn đề phần cứng và phần mềm khác nhau. Vì vậy, thay vì lấy một giá trị, chúng ta có thể áp dụng một vòng lặp và tìm giá trị trung bình của chúng để có được thời gian đã trôi qua.

@Test
fun codeMeasurement() {
    val worker = TestListenableWorkerBuilder<MyWorker>(context).build() //jetpack workmanager library
    val COUNT = 5 //some iteration count
    val start = java.lang.System.nanoTime() //for starting time of work
    for (i in 0..COUNT) {
        worker.doWork() //do some work i.e. code to be measured
    }
    // include outliers
    val elapsed = (java.lang.System.nanoTime() - start) / COUNT //average time taken to complete the work
    Log.d("Code Measurement", "Time taken was $elapsed ns")
}

Ở đây, chúng ta có một số COUNT bằng 5 và chúng ta lấy giá trị trung bình của 5 giá trị sau đó tính thời gian đã trôi qua. Nhưng tại sao chỉ có 5? Tại sao không có số khác? Ngoài ra, có thể có các giá trị ngoại lệ hoặc nhiều thứ khác đang chạy ở nền và điều này sẽ ảnh hưởng đến thời gian đo lường.

Vậy từ hai ví dụ trên, chúng ta có thể kết luận rằng việc đo lường hiệu suất mã rất khó khăn vì để tìm ra thời gian trung bình chúng ta cần xác định chạy vòng lặp bao nhiêu lần, tức là giá trị của biến COUNT nên là bao nhiêu? Điều này rất phức tạp. Những bước đo lường mã này được gọi là Benchmarking. Hãy thảo luận sâu hơn về điều này.

Benchmarking là gì?

Từ phần trước, tôi nghĩ mọi người đã có ý tưởng về Benchmarking.

Chúng ta có thể nói rằng Benchmarking là một quy trình được sử dụng để đo lường tốc độ mà điện thoại của bạn có thể chạy một cái gì đó trong đó. Ý tưởng là đặt điện thoại của bạn vào áp lực đủ để bạn có thể tìm ra hiệu suất tối đa của ứng dụng trên điện thoại của bạn.

Vì vậy, ở phần trước, chúng ta đã thấy cách chúng ta có thể tìm hoặc đo lường thời gian trung bình mà một đoạn mã mất để chạy trên một thiết bị. Nhưng có một số vấn đề với nó. Chúng là:

  1. Đây thường là không chính xác vì chúng ta đang đo đạc điều sai vào thời điểm sai.
  2. Nó rất không ổn định, tức là nếu chúng ta chạy cùng một đoạn mã nhiều lần thì có khả năng chúng ta sẽ nhận được các giá trị khác nhau cho mỗi lần chạy.
  3. Nếu chúng ta lấy trung bình của tất cả các giá trị thì làm thế nào để quyết định số lần mã cụ thể đó sẽ được thực hiện trước khi lấy kết quả. Bạn không thể quyết định điều này.

Vì vậy, khó nói được chúng ta đã tiết kiệm bao nhiêu thời gian ở đây vì Benchmarking rất phức tạp. Có cách nào để tìm ra thời gian thực tế mà một đoạn mã đang mất trong quá trình thực thi không?

Và giải pháp cho vấn đề của chúng ta là Jetpack Benchmark Library

The Jetpack Benchmark Library

Tại sự kiện Google I/O'19, Android giới thiệu thư viện Jetpack Benchmark, có thể được sử dụng để loại bỏ tất cả các lỗi hoặc khó khăn mà chúng ta gặp phải khi thực hiện quá trình Benchmark một cách đơn thuần.

Thư viện Jetpack Benchmark là một công cụ để đo lường hiệu suất mã và được sử dụng để loại bỏ những sai lầm phổ biến mà chúng ta thường mắc phải khi sử dụng Benchmark. Thư viện này xử lý quá trình làm nóng, đo lường hiệu suất mã của bạn và xuất kết quả trên cửa sổ console của Android Studio.

Bây giờ, mã Benchmark mà chúng ta đã đo lường sau 5 vòng lặp sẽ được chuyển thành:

@get:Rule
val benchmarkRule = BenchmarkRule()

@Test
fun codeMeasurement() {
    val worker = TestListenableWorkerBuilder<MyWorker>(context).build() //jetpack workmanager library
    benchmarkRule.measureRepeated {
        worker.doWork()
    }
}

Tất cả những gì bạn cần làm là áp dụng BenchmarkRule và sau đó, bạn cần gọi một API, đó là measureRepeated.

Bây giờ, hãy xem xét một số ví dụ khác. Ở đây, chúng ta sẽ lấy ví dụ về databaseBenchmark. Vì vậy, trong databaseBenchmark này, đầu tiên, chúng ta sẽ khởi tạo cơ sở dữ liệu và sau đó, chúng ta sẽ xóa tất cả các bảng và chèn một số dữ liệu thử nghiệm. Sau đó, chúng ta sẽ tạo vòng lặp đo mã đo hiệu suất mã mà chúng ta quan tâm, tức là một số truy vấn cơ sở dữ liệu.

@get:Rule
val benchmarkRule = BenchmarkRule()

@Test
fun databaseBenchmark() {
    val db = Room.databaseBuilder(...).build()
    db.clearAndInsertTestData()
    benchmarkRule.measureRepeated {
        db.complexQuery()
    }
}

Nhưng có một vấn đề với code này. Truy vấn của chúng ta có thể được lưu vào bộ nhớ đệm nếu chúng ta không thay đổi giá trị trong cơ sở dữ liệu, sau đó truy vấn có thể được lưu vào bộ nhớ đệm và chúng ta sẽ nhận kết quả khác nhau so với kết quả mong muốn. Vì vậy, chúng ta có thể đặt db.clearAndInsertTestData() trong vòng lặp và bằng cách này, trong mỗi vòng lặp, chúng ta làm sạch bộ nhớ đệm và sau đó đo lường mã mà chúng ta quan tâm. Nhưng ở đây, chúng ta cũng đang đo lường nhiều hơn những gì cần thiết vì chúng ta có một câu lệnh nữa trong vòng lặp. Vì vậy, để loại bỏ điều này, chúng ta có thể áp dụng phương pháp trước đó, tức là đếm hoặc tìm thời gian thực hiện bởi db.clearAndInsertTestData() và sau đó trừ kết quả này với kết quả cuối cùng.

@get:Rule
val benchmarkRule = BenchmarkRule()

@Test
fun databaseBenchmark() {
    val db = Room.databaseBuilder(...).build()
    val pauseOffset = 0L
    benchmarkRule.measureRepeated {
        val start = java.lang.System.nanoTime()
        db.clearAndInsertTestData()
        pauseOffset += java.lang.System.nanoTime() - start
        db.complexQuery()
    }
    Log.d("Benchmark", "databaseBenchmark_offset: $pauseOffset")
}

Nếu bạn không muốn viết quá nhiều dòng mã, thì có một API có sẵn là runWithTimeDisabled, nó sẽ thực hiện cùng một công việc nhưng một cách chính xác.

@get:Rule
val benchmarkRule = BenchmarkRule()

@Test
fun databaseBenchmark() {
    val db = Room.databaseBuilder(...).build()
    benchmarkRule.measureRepeated {
        runWithTimeDisabled {
            db.clearAndInsertTestData()
        }
        db.complexQuery()
    }
}

Phải chăng nó đơn giản và rõ ràng? Vì vậy, không còn lo lắng nữa. Chỉ cần sử dụng Thư viện Jetpack Benchmark và bạn đã có thể sử dụng Benchmarking. Nhưng làm thế nào để tích hợp điều này vào Android Studio của chúng ta? Hãy xem.

Tích hợp với Android Studio

Vì Thư viện Benchmark đang ở giai đoạn alpha, chúng ta cần tải Android Studio phiên bản 3.5 trở lên. Chúng ta thích sự mô-đun hóa trong dự án của mình và theo mặc định, chúng ta có các module app và lib. Do đó, để sử dụng thư viện Benchmark, bạn cần bật module benchmark và bật mẫu Android Studio cho Benchmarking. Dưới đây là các bước để bật module benchmark trong Android Studio:

  1. Tải Android Studio phiên bản 3.5 hoặc mới hơn.
  2. Trong Android Studio, nhấp vào Help > Edit Custom Properties.
  3. Thêm mã: npw.benchmark.template.module=true
  4. Lưu và đóng tệp.
  5. Khởi động lại Android Studio.

Bây giờ, chúng ta có thể tạo một module Benchmark khác ngoài app và lib module. Template của module Benchmark sẽ tự động cấu hình các thiết lập cho Benchmarking. Để tạo một module Benchmark, thực hiện các bước sau:

  1. Click chuột phải vào dự án hoặc module của bạn và chọn New > Module.
  2. Chọn Benchmark Module và nhấp Next.
  3. Nhập tên cho module của bạn và ngôn ngữ (Java/Kotlin) sau đó nhấp Finish. Bằng cách thực hiện các bước trên, một module sẽ được tạo đã được cấu hình trước để sử dụng benchmark, với thêm thư mục benchmark và debuggable được đặt thành false. Ở đây, bằng cách đặt debuggable thành false, chúng ta ngăn chặn việc sử dụng bộ gỡ lỗi với các bài kiểm tra của mình.

Để chạy benchmark, trong module, điều hướng đến benchmark/src/androidTest và sau đó nhấn ctrl+shift+F10 (hoặc cmd+shift+R trên Mac). Đây là kết quả của quá trình này:

Cấu hình Benchmark

Vì chúng ta đang tạo một module cho việc đánh giá hiệu suất (Benchmark) và với mọi module, chúng ta đều có một tệp build.gradle. Tương tự, với module đánh giá hiệu suất, tệp build.gradle bao gồm:

Các phần chính bao gồm:

  1. Plugin đánh giá hiệu suất (benchmark): Giúp bạn trích xuất báo cáo đánh giá hiệu suất khi bạn chạy gradlew.
  2. Custom runner (người chạy tùy chỉnh): Người chạy tùy chỉnh, nghĩa là AndroidBenchmarkRunner, giúp ổn định đánh giá hiệu suất của bạn.
  3. Quy tắc Proguard được xây dựng sẵn: Tối ưu hóa mã nguồn của bạn.
  4. Thư viện chính (phiên bản alpha, tính đến thời điểm hiện tại). Code của file build.gradle trông như sau:
apply plugin: 'com.android.library'
apply plugin: 'androidx.benchmark'

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

    buildTypes {
        debug {
            debuggable false
            minifyEnabled true
            proguardFiles getDefaultProguardFile(
                    'proguard-android-optimize.txt'),
                    'benchmark-proguard-rules.pro'
        }
    }
}

dependencies {
    ...
    androidTestImplementation "androidx.benchmark:benchmark-junit4:1.0.0-alpha05"

}

Ngoài ra, trong file AndoridManifest.xml, bạn cần đặt thông tin có thể gỡ lỗi thành sai.

<!-- Important: disable debuggable for accurate performance results -->
<application
    android:debuggable="false"
    tools:replace="android:debuggable"/>

Bạn cần đặt debuggable là false để ngăn chặn bộ gỡ lỗi sử dụng bài kiểm thử của bạn. Dưới đây là một sự khác biệt nhanh giữa việc debuggable được đặt là true và khi nó được đặt là false. Ngoài debuggable, bạn cũng có thể đặt codeCoverageEnabled là false.

Vậy là chúng ta đã hoàn thành quá trình kiểm thử hiệu suất. Nhưng ở đây có một câu hỏi, liệu chúng ta có thể sử dụng Jetpack benchmark Library ở mọi tình huống không, tức là liệu thư viện có tạo ra kết quả giống nhau trong mọi trường hợp không? Câu trả lời là không. Nhưng đừng lo lắng, thư viện sẽ thông báo cho bạn nếu có điều gì đó ảnh hưởng đến bài kiểm thử hiệu suất. Vâng, chúng ta có các cảnh báo trong thư viện kiểm thử hiệu suất. Vì vậy, lần tới khi debuggable của bạn được đặt là true hoặc bạn đang sử dụng máy ảo hoặc thiếu một số runner hoặc nếu bạn đang hết pin thì bộ kiểm thử sẽ cảnh báo cho bạn và bạn có thể kiểm tra và sửa chữa trước khi thực hiện kiểm thử hiệu suất.

Các yếu tố ảnh hưởng đến Benchmarking

Thực hiện một nhiệm vụ kiểm thử hiệu suất không phải là chuyện dễ dàng như nó có vẻ. Có nhiều yếu tố khó khăn và yếu tố lớn nhất là đồng hồ CPU. Đồng hồ CPU đóng vai trò rất lớn đối với sự ổn định. Để chính xác hơn, vấn đề về đồng hồ CPU được chia thành hai vấn đề chính:

  1. Ramping
  2. Throttling

Ramping

Chúng ta đều biết rằng khi thiết bị ở trong tình trạng lý tưởng, tức là không có công việc nào được giao, thì đồng hồ CPU thường ở mức thấp, và khi có công việc được giao, nó sẽ tăng lên chế độ hiệu suất cao. Do đó, nói chung, đồng hồ sẽ tăng lên dưới tác động của công việc.

Vì vậy, nếu chúng ta đo lường trong hai tình huống này, chúng ta có thể nhận được đầu ra sai lầm cho đo lường mã nguồn vì đồng hồ khác nhau trong các tình huống này.

Giải pháp cho vấn đề này rất đơn giản và nó hiện diện trong định nghĩa của việc đo lường hiệu suất. Thư viện Benchmark chạy giai đoạn làm nóng để ổn định điều này, tức là vòng lặp của việc đo lường sẽ chạy ít nhất 250ms, sau đó, đo lường sẽ được thực hiện. Vì vậy, nếu đồng hồ đang ở trạng thái thấp thì trong 250ms đó, đồng hồ sẽ chuyển sang trạng thái ưa thích và đo lường sẽ được thực hiện.

Tuyệt vời, chúng ta đã giải quyết xong vấn đề đầu tiên. Không, nó không phải là tuyệt vời, nó là nóng, và vấn đề thứ hai của chúng ta cũng phát sinh do nhiệt độ của thiết bị.

Diving or Throttling

Khi thiết bị của bạn đang hoạt động nhiều, nó sẽ trở nên nóng và đồng hồ sẽ giảm nhanh chóng. Nói chung, CPU giảm đồng hồ khi thiết bị nóng để tránh hỏng hóc chip. Điều này còn được gọi là giảm hiệu suất nhiệt độ. Do đó, điều này có thể ảnh hưởng đến hiệu suất của bài kiểm tra lớn vì có một khoảnh khắc nhất định đồng hồ rất cao và ở khảnh khắc tiếp theo, đồng hồ lại rất thấp. Dưới đây là một ví dụ về Giảm hiệu suất nhiệt độ (đường màu đỏ): Ở đây, tại một thời điểm, chúng ta có rất nhiều công việc để thực hiện và đồng hồ CPU cao. Nhưng vào cùng một thời điểm khi thiết bị trở nên nóng, CPU giảm đồng hồ và sau một thời gian do công việc đang chờ xử lý, đồng hồ lại tăng lên. Vì vậy, chúng ta không thể tin tưởng vào các đo lường được thực hiện giữa những chu kỳ đồng hồ này.

Có nhiều giải pháp khác nhau cho vấn đề Điều tiết nhiệt này. Chúng là:

  1. Clock Locking
  2. Sustained Prefernce
  3. Thread.sleep()

Một giải pháp có thể là Clock Locking. Vì vậy, bạn có thể Khóa Clock và sau đó đo lường mã. Nhưng điều này không phải là lựa chọn lý tưởng vì nó yêu cầu thiết bị được root. Ngoài ra, chúng ta có một số plugin Gradle để khóa clock, tức là:

./gradlew lockClocks

Nhưng điều này đòi hỏi thiết bị phải được root. Vì vậy, đây không phải là một giải pháp chung.

Một giải pháp khác là Sustained preference. Có một API được gọi là Window.setSustainedPerformanceMode() thường được tạo ra cho VR/game và có thể được sử dụng trong benchmarking vì nó giảm tối đa các chu kỳ đồng hồ, từ đó ngăn chặn quá trình giảm tốc. Nhưng điều này cũng có một số nhược điểm:

  1. Nó yêu cầu Activity đang chạy với một số cờ được thiết lập.
  2. Nó có thể hoạt động ở chế độ đơn hoặc đa luồng.
  3. Cuối cùng, nó chỉ hỗ trợ một số thiết bị hạn chế.

Vì vậy, hãy tìm giải pháp cho ba vấn đề này.

Đối với phần cờ Activity, chúng ta làm là chúng ta inject một Activity mỗi khi chúng ta đang kiểm thử một cái gì đó và sau đó, chúng ta đặt một cờ trên tất cả các activities mà người dùng đã khởi chạy. Bằng cách này, vấn đề cờ Activity của chúng ta có thể được giải quyết.

Đối với vấn đề Đơn và Đa luồng, chúng ta phải buộc thiết bị vào chế độ đa luồng vì bạn có thể sử dụng một lõi ở tốc độ lớn nhất hoặc nhiều lõi ở tốc độ thấp hơn. Nhưng việc chuyển đổi giữa các chế độ có thể dẫn đến sự không nhất quán. Do đó, chúng ta nên buộc thiết bị vào chế độ đa luồng. Để thực hiện điều này, trong AndroidBenchmarkRunner của chúng ta, chế độ hiệu suất ổn định đang được sử dụng, chúng ta tạo một luồng mới sẽ quay. Đây là cách tốt nhất để buộc thiết bị vào chế độ đa luồng.

//AndroidBenchmarkRunner.kt

override fun onCreate(arguments: Bundle) {
    super.onCreate(arguments)

    if(sustainedPerformanceModeInUse) {
        thread(name = "BenchSpinThread") {
            Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST)
            while(true) {}
        }
    }
}

Bây giờ, vấn đề thứ ba và cuối cùng là Window.setSustainedPerformanceMode() chỉ có sẵn cho một số thiết bị hạn chế. Vì vậy, để kiểm tra xem thiết bị của bạn có hỗ trợ điều này hay không, bạn có thể thực hiện như sau:

PowerManager.isSustainedPerformanceModeSupported()

Vì vậy, đến nay chúng ta đã thấy hai phương pháp là Clock Locking và Sustained Preference, nhưng cả hai phương pháp này không thể được sử dụng trong trường hợp thông thường vì không phải tất cả các thiết bị đều được root và cũng không phải tất cả các thiết bị đều hỗ trợ Sustained Performance Mode. Chúng ta cần một giải pháp cụ thể và tổng quát. Vì vậy, đây là giải pháp cuối cùng cho việc kiểm soát tốc độ, đó là thread sleeping và đây là giải pháp đơn giản nhất.

Trong phương pháp Thread.sleep(), chúng ta phát hiện chậm trễ bằng cách chạy một thử nghiệm nhỏ vào giữa mỗi thử nghiệm để xem thiết bị có bắt đầu giảm tốc độ nhiệt độ hay không. Nếu có giảm tốc độ nhiệt độ, chúng ta loại bỏ dữ liệu thử nghiệm hiện tại và chúng ta ngủ để thiết bị nguội xuống.

Dưới đây là so sánh giữa tốc độ đồng hồ trước và sau khi sử dụng Thread.sleep(). (đường màu xanh biển đại diện cho tình trạng sau khi sử dụng Thread.sleep())

Không nên Benchmark mọi thứ

Chúng ta đã thấy rằng với sự giúp đỡ của việc đo lường hiệu suất bằng cách sử dụng benchmarking, chúng ta có thể đánh giá hiệu suất của mã nguồn. Tuy nhiên, nên lưu ý rằng không nên đo lường mọi thứ. Hãy bắt đầu bằng các công cụ theo dõi hoặc làm một số đo lường trên thiết bị thực của bạn để xem phần cụ thể nào của ứng dụng của bạn là chậm. Nếu bạn đang sử dụng benchmark, hãy đo lường các khối đồng bộ và nhỏ.

Ngoài ra, trước khi sử dụng benchmark, bạn nên tìm hiểu về bộ nhớ cache có trong mã nguồn của bạn. Dưới đây là một ví dụ đơn giản về một benchmark mà nó tải một tệp và kiểm tra xem tệp đó có tồn tại hay không.

@get:Rule
val benchmarkRule = BenchmarkRule()

@Test
fun myBenchmark() {
    val file = File(path)
    benchmarkRule.measureRepeated {
        file.exists()
    }
}

Trong đoạn code trên, hệ điều hành sẽ ghi nhớ địa chỉ của tệp và nó sẽ giả định rằng không có gì thay đổi. Do đó, nó sẽ tồn tại trong bộ nhớ cache và mỗi lần bạn sẽ nhận được giá trị cache. Do đó, kết quả của benchmark sẽ khác biệt rất nhiều so với thời gian khởi động. Hãy chú ý đến những điều như vậy.

Kết luận

Chúng ta đã thấy rằng Benchmark là một vấn đề rất phức tạp, tức là rất khó để đo lường hiệu suất code thông thường. Nó phụ thuộc vào Stabilizing Clock (Ổn định đồng hồ). Do đó, để giải quyết những vấn đề này, chúng ta có API Jetpack Benchmark Library có thể đo lường hiệu suất code với bạn. Và hãy nhớ một điều, Đừng So Sánh Các Thiết Bị với Jetpack Benchmark. Ở đây, chúng ta đang so sánh mã được viết cho cùng một thiết bị và cùng một phiên bản hệ điều hành. Để tìm hiểu thêm về benchmark, bạn có thể truy cập trang web Benchmark và để xem các mẫu mã benchmark, bạn có thể ghé thăm trang Github. (https://github.com/googlesamples/android-performance)

Cảm ơn ae đã đọc tới đây!

Nguồn tham khảo: https://blog.mindorks.com/improving-android-app-performance-with-benchmarking/


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.