Hồ sơ cơ sở

Cấu hình cơ sở là danh sách các lớp và phương thức có trong một APK mà Android Runtime (ART) sử dụng trong quá trình cài đặt để tổng hợp trước các đường dẫn quan trọng đến mã máy. Đây là một phương thức tối ưu hoá theo cấu hình (PGO) cho phép ứng dụng tối ưu hoá quá trình khởi động, giảm hiện tượng giật và cải thiện hiệu suất cho người dùng cuối.

Cách hoạt động của Cấu hình cơ sở

Các quy tắc cấu hình được biến dịch thành dạng nhị phân trong APK, trong assets/dexopt/baseline.prof.

Trong quá trình cài đặt, ART thực hiện việc tổng hợp các phương thức trước thời gian (AOT) trong cấu hình, nhờ đó các phương thức đó được thực thi nhanh hơn. Nếu cấu hình chứa các phương thức dùng khi chạy ứng dụng hoặc trong quá trình kết xuất khung hình, thì người dùng sẽ có thời gian khởi chạy nhanh hơn và/hoặc giảm hiện tượng giật.

Trong khi phát triển ứng dụng hoặc thư viện, hãy cân nhắc xác định Cấu hình cơ sở để bao gồm các đường dẫn nóng cụ thể trong các hành trình trọng yếu của người dùng, nơi mà thời gian kết xuất hoặc độ trễ là thành phần quan trọng, chẳng hạn như khởi động, chuyển đổi hoặc cuộn.

Sau đó, Cấu hình cơ sở sẽ được gửi trực tiếp cho người dùng (thông qua Google Play) cùng với APK, như trong hình 1:

Hình 1. Sơ đồ này hiển thị quy trình làm việc của cấu hình cơ sở từ hoạt động tải lên thông qua phương thức phân phối của người dùng cuối và mối quan hệ giữa quy trình làm việc đó với cấu hình trên đám mây.

Lý do sử dụng Cấu hình cơ sở

Thời gian khởi động là một thành phần quan trọng để cải thiện mức độ tương tác của người dùng với ứng dụng của bạn. Việc tăng tốc độ và khả năng phản hồi của ứng dụng sẽ mang lại nhiều người dùng hoạt động hằng ngày hơn và tỷ lệ truy cập trung bình của người dùng cũ cao hơn.

Cấu hình đám mây cũng tối ưu hoá các lượt tương tác này, nhưng chỉ dành cho người dùng một ngày trở lên sau khi bản cập nhật được phát hành và không hỗ trợ Android 7 (API 24) cho đến Android 8 (API 26).

Hành vi tổng hợp trên các phiên bản Android

Các phiên bản Android Platform đã sử dụng các phương pháp tổng hợp ứng dụng khác nhau, mỗi phương pháp đều có sự đánh đổi hiệu suất tương ứng. Cấu hình cơ sở cải thiện theo các phương thức tổng hợp trước đó bằng cách cung cấp một cấu hình cho tất cả các lượt cài đặt.

Phiên bản Android Phương pháp tổng hợp Phương pháp tối ưu hoá
Android 5 (API cấp 21) tới Android 6 (API cấp 23) AOT đầy đủ Toàn bộ ứng dụng được tối ưu hoá trong quá trình cài đặt, dẫn đến thời gian chờ sử dụng ứng dụng lâu, tăng mức sử dụng RAM và dung lượng ổ đĩa cùng với thời gian tải mã lâu hơn từ ổ đĩa, có thể làm tăng thời gian khởi động nguội.
Android 7 (API cấp 24) tới Android 8.1 (API cấp 27) AOT một phần (Cấu hình cơ sở) Cấu hình cơ sở được androidx.profileinstaller cài đặt trong lần chạy đầu tiên, khi mô-đun ứng dụng xác định phần phụ thuộc này. ART có thể cải thiện điều này hơn nữa bằng cách thêm các quy tắc cấu hình khác trong quá trình ứng dụng sử dụng và tổng hợp những quy tắc này khi thiết bị ở trạng thái rảnh. Cách này sẽ tối ưu hoá dung lượng ổ đĩa và thời gian tải mã từ ổ đĩa, từ đó giảm thời gian chờ xử lý ứng dụng.
Android 9 (API cấp 28) trở lên AOT một phần (Cấu hình cơ sở + Cấu hình đám mây) Play sử dụng Cấu hình cơ sở trong quá trình cài đặt ứng dụng để tối ưu hoá APK và cấu hình đám mây (nếu có). Sau khi cài đặt, cấu hình ART được tải lên Play và tổng hợp, sau đó được cung cấp dưới dạng Cấu hình đám mây cho những người dùng khác khi họ cài đặt/cập nhật ứng dụng.

Tạo cấu hình cơ sở

Tự động tạo quy tắc cấu hình bằng BaselineProfileRule

Là một nhà phát triển ứng dụng, bạn có thể tự động tạo cấu hình cho mọi bản phát hành ứng dụng bằng cách sử dụng thư viện Jetpack Macrobenchmark.

Cách tạo cấu hình cơ sở bằng thư viện Macrobenchmark:

  1. Hãy thêm phần phụ thuộc vào thư viện ProfileInstaller trong build.gradle của ứng dụng để bật tính năng tổng hợp Cấu hình cơ sở cục bộ và trên Cửa hàng Play. Đây là cách duy nhất để tải không qua cửa hàng một Cấu hình cơ sở ở phạm vi cục bộ.

    dependencies {
         implementation("androidx.profileinstaller:profileinstaller:1.2.0-beta01")
    }
    
  2. Thiết lập mô-đun Macrobenchmark trong dự án Gradle của bạn.

  3. Xác định thử nghiệm mới có tên là BaselineProfileGenerator giống như:

    @ExperimentalBaselineProfilesApi
    @RunWith(AndroidJUnit4::class)
    class BaselineProfileGenerator {
        @get:Rule val baselineProfileRule = BaselineProfileRule()
    
        @Test
        fun startup() =
            baselineProfileRule.collectBaselineProfile(packageName = "com.example.app") {
                pressHome()
                // This block defines the app's critical user journey. Here we are interested in
                // optimizing for app startup. But you can also navigate and scroll
                // through your most important UI.
                startActivityAndWait()
            }
    }
    
  4. Kết nối một trình mô phỏng userdebug Dự án nguồn mở Android (AOSP) đã bị can thiệp vào hệ thống chạy Android 9 trở lên.

  5. Chạy lệnh adb root từ thiết bị đầu cuối để đảm bảo dabonemon đang chạy với các quyền gốc.

  6. Chạy thử nghiệm và đợi hoàn thành.

  7. Tìm vị trí hồ sơ đã tạo trong logcat. Tìm thẻ nhật ký Benchmark.

    com.example.app D/Benchmark: Usable output directory: /storage/emulated/0/Android/media/com.example.app
    
    # List the output baseline profile
    ls /storage/emulated/0/Android/media/com.example.app
    SampleStartupBenchmark_startup-baseline-prof.txt
    
  8. Kéo tệp đã tạo từ thiết bị của bạn.

    adb pull storage/emulated/0/Android/media/com.example.app/SampleStartupBenchmark_startup-baseline-prof.txt .
    
  9. Đổi tên tệp đã tạo thành baseline-prof.txt rồi sao chép tệp đó vào thư mục src/main của mô-đun ứng dụng.

Xác định quy tắc cấu hình theo cách thủ công

Bạn có thể xác định quy tắc cấu hình theo cách thủ công trong ứng dụng hoặc mô-đun thư viện bằng cách tạo tệp có tên baseline-prof.txt nằm trong thư mục src/main. Đây chính là thư mục chứa tệp AndroidManifest.xml.

Tệp chỉ định một quy tắc trên mỗi dòng. Mỗi quy tắc thể hiện một mẫu cho các phương thức hoặc lớp trùng khớp trong ứng dụng hoặc thư viện mà bạn cần tối ưu hoá.

Cú pháp cho các quy tắc này là tập mẹ của định dạng cấu hình ART (HRF) mà người dùng có thể đọc được khi sử dụng adb shell profman --dump-classes-and-methods. Cú pháp rất giống với cú pháp cho phần mô tả và chữ ký, nhưng cũng cho phép ký tự đại diện đơn giản hoá quá trình viết quy tắc.

Các ví dụ sau đây hiển thị một số quy tắc của Cấu hình cơ sở có trong thư viện Jetpack Compose:

HSPLandroidx/compose/runtime/ComposerImpl;->updateValue(Ljava/lang/Object;)V
HSPLandroidx/compose/runtime/ComposerImpl;->updatedNodeCount(I)I
HLandroidx/compose/runtime/ComposerImpl;->validateNodeExpected()V
PLandroidx/compose/runtime/CompositionImpl;->applyChanges()V
HLandroidx/compose/runtime/ComposerKt;->findLocation(Ljava/util/List;I)I
Landroidx/compose/runtime/ComposerImpl;

Cú pháp quy tắc

Các quy tắc này dùng một trong 2 biểu mẫu để nhắm mục tiêu hoặc phương thức hoặc lớp:

[FLAGS][CLASS_DESCRIPTOR]->[METHOD_SIGNATURE]

Quy tắc lớp sử dụng mẫu sau:

[CLASS_DESCRIPTOR]
Cú pháp Mô tả
FLAGS Cho biết một hoặc nhiều ký tự H, SP để cho biết phương thức này có bị gắn cờ là Hot, Startup hay Post Startup liên quan đến loại khởi động.

Phương thức có cờ H cho biết đây là phương pháp "nóng", tức là phương thức này được gọi nhiều lần trong suốt vòng đời ứng dụng.

Phương thức có cờ S cho biết đây là phương thức gọi khi khởi động.

Phương thức có cờ P cho biết đây là phương thức nóng không liên quan đến việc khởi động.

Một lớp có trong tệp này cho biết rằng lớp này được dùng trong khi khởi động và cần được phân bổ trước trong vùng nhớ khối xếp để tránh chi phí tải lớp. Trình tổng hợp ART sử dụng nhiều chiến lược tối ưu hoá, chẳng hạn như tổng hợp AOT của các phương pháp này và thực hiện tối ưu hoá bố cục trong tệp AOT đã tạo.
CLASS_DESCRIPTOR Phần mô tả cho lớp của phương pháp nhắm mục tiêu. Ví dụ: androidx.compose.runtime.SlotTable sẽ có phần mô tả là Landroidx/compose/runtime/SlotTable;. Lưu ý: L được thêm vào đây ở định dạng có thể thực thi Dalvik (DEX).
METHOD_SIGNATURE Chữ ký của phương thức, bao gồm tên, loại thông số và các loại dữ liệu trả về của phương thức. Ví dụ: phương thức

// LayoutNode.kt

fun isPlaced():Boolean {
// ...
}

trên LayoutNode có chữ ký isPlaced()Z.

Những mẫu này có thể có ký tự đại diện để có một quy tắc bao gồm nhiều phương thức hoặc lớp. Để được hỗ trợ hướng dẫn khi viết bằng cú pháp quy tắc trong Android Studio, hãy xem trình bổ trợ Cấu hình cơ sở của Android.

Ví dụ về quy tắc ký tự đại diện có thể có dạng như sau:

HSPLandroidx/compose/ui/layout/**->**(**)**

Các loại được hỗ trợ trong quy tắc cấu hình cơ sở

Quy tắc cấu hình cơ sở hỗ trợ các loại sau. Để biết chi tiết về các loại này, hãy xem định dạng có thể thực thi Dalvik (DEX).

Nhân vật Loại Mô tả
B byte Byte đã ký
C ký tự Điểm mã ký tự Unicode được mã hoá bằng UTF-16
D gấp đôi Giá trị dấu phẩy động có độ chính xác gấp đôi
F số thực dấu phẩy động Giá trị dấu phẩy động có độ chính xác đơn
I int Số nguyên
J dài Số nguyên dài
S ngắn Ký tên ngắn
V void Void
Z boolean Đúng hay sai
L (tên lớp) tham chiếu Một bản sao tên lớp

Ngoài ra, các thư viện có thể xác định các quy tắc sẽ được đóng gói trong cấu phần phần mềm AAR. Khi bạn tạo APK để bao gồm các cấu phần phần mềm này, các quy tắc sẽ được hợp nhất với nhau (tương tự như cách thực hiện hợp nhất tệp kê khai) và được tổng hợp thành một cấu hình ART nhị phân nhỏ gọn dành riêng cho APK.

ART tận dụng cấu hình này khi APK được sử dụng trên các thiết bị để AOT tổng hợp một nhóm nhỏ cụ thể của ứng dụng tại thời điểm cài đặt trên Android 9 (API cấp 28) hoặc Android 7 (API cấp 24) khi sử dụng ProfileInstaller.

Ghi chú khác

Khi tạo cấu hình cơ sở, bạn cần lưu ý một số điều sau đây:

  • AOT có sẵn Android 5 tới Android 6 (API cấp 21 và 23) tổng hợp APK tại thời điểm cài đặt.

  • Các ứng dụng có thể gỡ lỗi không bao giờ được tổng hợp bằng AOT để giúp khắc phục sự cố.

  • Các tệp quy tắc phải được đặt tên là baseline-prof.txt và đặt trong thư mục gốc của nhóm tài nguyên chính (phải là tệp đồng cấp với tệp AndroidManifest.xml)

  • Bạn chỉ sử dụng được các tệp này nếu bạn đang dùng trình bổ trợ Android cho Gradle 7.1.0-alpha05 trở lên (Android Studio Bumblebee Canary 5).

  • Bazel hiện không hỗ trợ việc đọc và hợp nhất các Cấu hình cơ sở thành một APK.

  • Cấu hình cơ sở đã nén không được lớn hơn 1,5 MB. Do đó, các thư viện và ứng dụng nên cố gắng xác định một bộ quy tắc nhỏ để tăng tối đa mức độ ảnh hưởng.

  • Các quy tắc rộng tổng hợp quá nhiều ứng dụng có thể làm chậm quá trình khởi động do quyền truy cập ổ đĩa tăng lên. Bạn nên kiểm tra hiệu suất của cấu hình cơ sở.

Đo lường các điểm cải thiện

Tự động đo lường với thư viện Macrobenchmark

Macrobenchmark cho phép bạn kiểm soát việc tổng hợp đo lường trước thông qua CompilationMode API, bao gồm cả mức sử dụng BaselineProfile.

Nếu đã thiết lập bài kiểm thử BaselineProfileRule trong mô-đun Macrobenchmark, thì bạn có thể xác định bài kiểm thử mới trong mô-đun đó để đánh giá hiệu suất của bài kiểm thử:

@RunWith(AndroidJUnit4::class)
class BaselineProfileBenchmark {
  @get:Rule
  val benchmarkRule = MacrobenchmarkRule()

  @Test
  fun startupNoCompilation() {
    startup(CompilationMode.None())
  }

  @Test
  fun startupBaselineProfile() {
    startup(CompilationMode.Partial(
      baselineProfileMode = BaselineProfileMode.Require
    ))
  }

  private fun startup(compilationMode: CompilationMode) {
    benchmarkRule.measureRepeated(
      packageName = "com.example.app",
      metrics = listOf(StartupTimingMetric()),
      iterations = 10,
      startupMode = StartupMode.COLD,
      compilationMode = compilationMode
    ) { // this = MacrobenchmarkScope
        pressHome()
        startActivityAndWait()
    }
  }
}

Hình 2 minh hoạ ví dụ về kết quả kiểm thử đạt:

Hình 2. Đây là các kết quả của một bài kiểm thử nhỏ. Những ứng dụng lớn hơn sẽ nhận được nhiều lợi ích hơn từ Cấu hình cơ sở.

Lưu ý rằng mặc dù trong ví dụ ở trên xem xét StartupTimingMetric, có những chỉ số quan trọng khác mà bạn nên cân nhắc (như Độ giật (chỉ số về khung)) có thể được đo bằng Jetpack Macrobenchmark.

Đo lường các điểm cải thiện với ứng dụng theo cách thủ công

Trước tiên, hãy đo lường quá trình khởi động ứng dụng chưa được tối ưu hoá để tham khảo.

PACKAGE_NAME=com.example.app
# Force Stop App
adb shell am force-stop $PACKAGE_NAME
# Reset compiled state
adb shell cmd package compile --reset $PACKAGE_NAME
# Measure App startup
# This corresponds to `Time to initial display` metric
# For additional info https://developer.android.com/topic/performance/vitals/launch-time#time-initial
adb shell am start-activity -W -n $PACKAGE_NAME/.ExampleActivity \
 | grep "TotalTime"

Tiếp theo, hãy tải không qua cửa hàng Cấu hình cơ sở.

# Unzip the Release APK first
unzip release.apk
# Create a ZIP archive
# Note: The name should match the name of the APK
# Note: Copy baseline.prof{m} and rename it to primary.prof{m}
cp assets/dexopt/baseline.prof primary.prof
cp assets/dexopt/baseline.profm primary.profm
# Create an archive
zip -r release.dm primary.prof primary.profm
# Confirm that release.dm only contains the two profile files:
unzip -l release.dm
# Archive:  release.dm
#   Length      Date    Time    Name
# ---------  ---------- -----   ----
#      3885  1980-12-31 17:01   primary.prof
#      1024  1980-12-31 17:01   primary.profm
# ---------                     -------
#                               2 files
# Install APK + Profile together
adb install-multiple release.apk release.dm

Để xác minh rằng gói đã được tối ưu hoá khi cài đặt, hãy chạy lệnh sau:

# Check dexopt state
adb shell dumpsys package dexopt | grep -A 1 $PACKAGE_NAME

Kết quả sẽ nêu rõ gói đó đã được tổng hợp.

[com.example.app]
  path: /data/app/~~YvNxUxuP2e5xA6EGtM5i9A==/com.example.app-zQ0tkJN8tDrEZXTlrDUSBg==/base.apk
  arm64: [status=speed-profile] [reason=install-dm]

Bây giờ, chúng ta có thể đo lường hiệu suất khi khởi động ứng dụng như trước đây, nhưng không đặt lại trạng thái tổng hợp.

# Force Stop App
adb shell am force-stop $PACKAGE_NAME
# Measure App startup
adb shell am start-activity -W -n $PACKAGE_NAME/.ExampleActivity \
 | grep "TotalTime"

Vấn đề đã biết

Việc sử dụng Hồ sơ cơ sở hiện tồn tại một số vấn đề đã biết:

  • Các hồ sơ cơ sở không được đóng gói chính xác khi tạo APK từ một gói ứng dụng. Để giải quyết vấn đề này, hãy áp dụng từ com.android.tools.build:gradle:7.3.0-beta02 trở lên (vấn đề).

  • Các hồ sơ cơ sở chỉ được đóng gói chính xác cho tệp classes.dex chính. Điều này ảnh hưởng đến các ứng dụng có nhiều tệp .dex. Để giải quyết vấn đề này, hãy áp dụng từ com.android.tools.build:gradle:7.3.0-beta02 trở lên (vấn đề).

  • Macrobenchmark không tương thích với Hồ sơ cơ sở trên thiết bị Android 12L (API cấp 32) (vấn đề ) và Android 13 (API cấp 33) (vấn đề ).

  • Không cho phép đặt lại bộ nhớ đệm của hồ sơ ART trên các bản dựng user (không bị can thiệp). Để giải quyết vấn đề này, androidx.benchmark:benchmark-macro-junit4:1.1.0-rc02 bao gồm bản sửa lỗi sẽ cài đặt lại ứng dụng trong quá trình đo điểm chuẩn (vấn đề).

  • Trình phân tích tài nguyên Android Studio không cài đặt Cấu hình cơ sở khi phân tích tài nguyên ứng dụng (vấn đề).

  • Các hệ thống xây dựng không phải Gradle (chẳng hạn như Bazel, Buck, v.v.) không hỗ trợ biên dịch Cấu hình cơ sở thành APK đầu ra.

  • Cửa hàng Play hiện sẽ cần 10 – 14 giờ sau khi tải AAB lên để cung cấp Cấu hình cơ sở tại thời điểm cài đặt. Người dùng tải ứng dụng xuống trong khoảng thời gian này sẽ không thấy lợi ích cho đến khi công cụ dexopt ở chế độ nền chạy (có thể qua đêm). Tính năng này đang được tích cực cải thiện.

  • Các kênh phân phối ứng dụng không phải Cửa hàng Play có thể sẽ không hỗ trợ việc sử dụng Cấu hình cơ sở tại thời điểm cài đặt. Người dùng ứng dụng thông qua những kênh này sẽ không thấy lợi ích cho đến khi công cụ dexopt ở chế độ nền chạy (có thể qua đêm).