Phân tích tài nguyên cho bản dựng

Các dự án lớn hoặc các dự án triển khai nhiều bản dựng logic tuỳ chỉnh, có thể đòi hỏi bạn tìm hiểu sâu hơn về quy trình xây dựng để tìm nút thắt cổ chai. Bạn có thể làm được điều này bằng cách phân tích thời gian Gradle thực thi từng giai đoạn trong vòng đời bản dựng và từng tác vụ của bản dựng. Ví dụ: nếu hồ sơ bản dựng của bạn cho biết Gradle đang dành quá nhiều thời gian để định cấu hình dự án thì bạn cần chuyển logic bản dựng tuỳ chỉnh ra khỏi giai đoạn cấu hình. Thêm vào đó, nếu tác vụ mergeDevDebugResources tiêu tốn một lượng thời gian quá lớn bạn có thể phải chuyển đổi hình ảnh của mình thành WebP hoặc tắt tính năng phân tích cú pháp PNG.

Nếu bạn đang sử dụng Android Studio 4.0 trở lên, cách tốt nhất để kiểm tra các vấn đề về hiệu suất của bản dựng là sử dụng Trình phân tích bản dựng.

Thêm vào đó, có hai tuỳ chọn để lập hồ sơ bản dựng của bạn bên ngoài Android Studio:

  1. Công cụ gradle-profiler độc lập – một công cụ độc đáo để phân tích chuyên sâu bản dựng.

  2. Dùng tuỳ chọn --profile của Gradle một công cụ thuận tiện có sẵn tại dòng lệnh Gradle.

Sử dụng công cụ gradle-profiler độc lập

Để tìm cách thiết lập dự án cung cấp cho bạn tốc độ bản dựng tốt nhất, bạn nên sử dụng trình lập hồ sơ Gradle, một công cụ thu thập thông tin về cấu hình và điểm chuẩn của các bản dựng Gradle. Trình phân tích tài nguyên trong Gradle cho phép bạn tạo các kịch bản xây dựng và chạy các bản dựng đó nhiều lần, ngăn chặn sự chênh lệch đáng kể giữa các kết quả và đảm bảo khả năng lặp lại của kết quả.

Bạn nên sử dụng Chế độ đo điểm chuẩn để thu thập thông tin về các bản dựng sạch và tăng dần (clean and incremental builds), trong lúc chế độ lập hồ sơ được dùng để thu thập thông tin chi tiết hơn về quá trình chạy, bao gồm cả ảnh chụp nhanh của CPU.

Sau đây là một số cấu hình thiết lập dự án giúp bạn đo điểm chuẩn:

  • Phiên bản trình bổ trợ
  • Phiên bản Gradle
  • Các chế độ cài đặt của máy ảo Java (kích thước vùng nhớ xếp, kích thước ngăn xếp, bộ thu gom rác, v.v.)
  • Số lượng lớp worker trong Gradle (org.gradle.workers.max)
  • Các tuỳ chọn trên mỗi trình bổ trợ để tối ưu hoá hiệu suất hơn nữa

Bắt đầu

  • Cài đặt gradle-profiler bằng cách làm theo các hướng dẫn sau
  • Chạy gradle-profiler --benchmark --project-dir <root-project> :app:assembleDebug

Phương thức này sẽ đo điểm chuẩn cho một bản dựng mới cập nhật vì --benchmark sẽ chạy tác vụ này nhiều lần mà không ảnh hưởng tới dự án trong quá trình chạy. Sau đó, phương thức này sẽ tạo báo cáo dạng HTML trong thư mục profile-out/ cho biết thời gian xây dựng.

Ngoài ra, còn có các kịch bản khác hữu ích hơn trong việc so sánh điểm chuẩn như:

  • Thay đổi mã bên trong một phương thức bất kỳ trên một lớp mà bạn thực hiện hầu hết các tác vụ của mình.
  • Thay đổi API trong một mô-đun dùng trong toàn bộ dự án. Mặc dù không phổ biến bằng thay đổi mã, việc này lại gây ra ảnh hưởng lớn hơn và khá hữu ích khi bạn đo lường kịch bản này.
  • Chỉnh sửa bố cục để mô phỏng vòng lặp trong các tác vụ của giao diện người dùng.
  • Chuỗi chỉnh sửa để mô phỏng quá trình xử lý biên dịch.
  • Dọn sạch các bản dựng để mô phỏng các thay đổi trên chính bản dựng đó (ví dụ: Bản cập nhật của trình bổ trợ Android cho Gradle, bản cập nhật của Gradle hoặc các bản chỉnh sửa đối với mã bản dựng trong buildSrc).

Để đo điểm chuẩn các trường hợp sử dụng này, bạn có thể tạo một kịch bản dùng để thúc đẩy quá trình thực thi gradle-profiler và áp dụng các sửa đổi phù hợp cho nguồn. Bạn có thể kiểm tra một số kịch bản phổ biến dưới đây.

Lập hồ sơ thiết lập cho từng bộ nhớ/CPU

Để đo điểm chuẩn theo từng chế độ cài đặt bộ nhớ và CPU, bạn có thể tạo nhiều kịch bản sử dụng các giá trị riêng cho org.gradle.jvmargs. Ví dụ: bạn có thể tạo các kịch bản như sau:

# <root-project>/scenarios.txt
clean_build_2gb_4workers {
    tasks = [":app:assembleDebug"]
    gradle-args = ["--max-workers=4"]
    jvm-args = ["-Xmx2048m"]
    cleanup-tasks = ["clean"]
}
clean_build_parallelGC {
    tasks = [":app:assembleDebug"]
    jvm-args = ["-XX:+UseParallelGC"]
    cleanup-tasks = ["clean"]
}

clean_build_G1GC_4gb {
    tasks = [":app:assembleDebug"]
    jvm-args = ["-Xmx4096m", "-XX:+UseG1GC"]
    cleanup-tasks = ["clean"]
}

Việc chạy gradle-profiler --benchmark --project-dir <root-project> --scenario-file scenarios.txt sẽ kèm theo với 3 kịch bản mà bạn sẽ có thể so sánh thời gian :app:assembleDebug cần cho mỗi thông tin thiết lập.

Lập hồ sơ các phiên bản trình bổ trợ Gradle khác nhau

Để tìm hiểu các sửa đổi trên phiên bản của trình bổ trợ Gradle sẽ ảnh hưởng như thế nào đến thời gian tạo bản dựng, hãy tạo một kịch bản để đo điểm chuẩn trong trường hợp này. Điều này đòi hỏi một số bước sơ bộ để phiên bản trình bổ trợ có thể chèn thêm mã vào từ kịch bản. Thay đổi gốc build.gradle của bạn:

# <root-project>/build.gradle
buildscript {
    def agpVersion = providers.systemProperty("agpVersion").forUseAtConfigurationTime().orNull ?: '4.1.0'

    ext.kotlin = providers.systemProperty('kotlinVersion').forUseAtConfigurationTime().orNull ?: '1.4.0'

    dependencies {
        classpath "com.android.tools.build:gradle:$agpVersion"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin"
    }
}

Bây giờ, bạn có thể xác định phiên bản của trình bổ trợ Android cho Gradle và phiên bản của trình bổ trợ Kotlin trong Gradle từ các tệp kịch bản rồi thêm một phương thức mới vào các tệp nguồn:

# <root-project>/scenarios.txt
non_abi_change_agp4.1.0_kotlin1.4.10 {
    tasks = [":app:assembleDebug"]
    apply-abi-change-to ["app/src/main/java/com/example/your_app/your_code_file.java,
                              "app/src/main/java/com/example/your_app/your_code_file.kt"]
    System-properties {
      "agpVersion" = "4.1.0"
      "kotlinVersion" = "1.4.10"
}

non_abi_change_agp4.2.0_kotlin1.4.20 {
    tasks = [":app:assembleDebug"]
    apply-abi-change-to ["app/src/main/java/com/example/your_app/your_code_file.java,
                              "app/src/main/java/com/example/your_app/your_code_file.kt"]
    System-properties {
      "agpVersion" = "4.2.0-alpha16"
      "kotlinVersion" = "1.4.20"
}

Lập hồ sơ bản dựng tăng dần

Phần lớn các bản dựng đều có khả năng gia tăng, đây là một trong những kịch bản quan trọng nhất đối trong quá trình lập hồ sơ. Trình phân tích tài nguyên của Gradle sẽ hỗ trợ tiện ích trong quá trình xây dựng các bản dựng tăng dần. Bạn có thể tự động áp dụng các thay đổi cho tệp nguồn bằng cách thay đổi mã bên trong phương thức, thêm phương thức mới hoặc thay đổi bố cục hoặc tài nguyên chuỗi. Ví dụ: bạn có thể tạo các kịch bản gia tăng như sau:

# <root-project>/scenarios.txt
non_abi_change {
    tasks = [":app:assembleDebug"]
    apply-non-abi-change-to = ["app/src/main/java/com/example/your_app/your_code_file.java,
                              "app/src/main/java/com/example/your_app/your_code_file.kt"]
}

abi_change {
    tasks = [":app:assembleDebug"]
    apply-abi-change-to = ["app/src/main/java/com/example/your_app/your_code_file.java,
                              "app/src/main/java/com/example/your_app/your_code_file.kt"]
}

layout_change {
    tasks = [":app:assembleDebug"]
    apply-android-layout-change-to = "app/src/main/res/your_layout_file.xml"
}
string_resource_change {
    tasks = [":app:assembleDebug"]
    apply-android-resource-value-change-to = "app/src/main/res/values/strings.xml"
}

Việc chạy gradle-profiler --benchmark --project-dir &lt;root-project> --scenario-file scenarios.txt sẽ tạo báo cáo dạng HTML chứa dữ liệu đo điểm chuẩn.

Bạn có thể kết hợp các kịch bản gia tăng với các tuỳ chọn cài đặt khác, như kích thước vùng nhớ khối xếp, số lớp worker hoặc phiên bản Gradle:

# <root-project>/scenarios.txt
non_abi_change_4g {
    tasks = [":app:assembleDebug"]
    apply-non-abi-change-to ["app/src/main/java/com/example/your_app/your_code_file.java,
                              "app/src/main/java/com/example/your_app/your_code_file.kt"]
    jvm-args = ["-Xmx4096m"]
}

non_abi_change_4g_8workers {
    tasks = [":app:assembleDebug"]
    apply-non-abi-change-to ["app/src/main/java/com/example/your_app/your_code_file.java,
                              "app/src/main/java/com/example/your_app/your_code_file.kt"]
    jvm-args = ["-Xmx4096m"]
    gradle-args = ["--max-workers=8"]
}

non_abi_change_3g_gradle67 {
    tasks = [":app:assembleDebug"]
    apply-non-abi-change-to ["app/src/main/java/com/example/your_app/your_code_file.java,
                              "app/src/main/java/com/example/your_app/your_code_file.kt"]
    jvm-args = ["-Xmx3072m"]
    version = ["6.7"]
}

Lập hồ sơ bản dựng sạch

Để đo điểm chuẩn cho bản dựng sạch, bạn có thể tạo một kịch bản dùng để thúc đẩy quá trình thực thi gradle-profiler:

# <root-project>/scenarios.txt
clean_build {
    tasks = [":app:assembleDebug"]
    cleanup-tasks = ["clean"]
}

Để chạy kịch bản này, hãy sử dụng lệnh sau:

gradle-profiler --benchmark --project-dir <root-project> --scenario-file scenarios.txt

Sử dụng tuỳ chọn --profile của Gradle

Để tạo và xem hồ sơ bản dựng thông qua dòng lệnh Gradle, hãy thực hiện các bước sau:

  1. Mở một thiết bị đầu cuối dòng lệnh ở gốc dự án của bạn.
  2. Thực hiện bản dựng sạch bằng cách nhập lệnh dưới đây. Khi lập hồ sơ cấu hình bản dựng, bạn nên tạo một bản dựng sạch giữa mỗi bản dựng mà bạn lập hồ sơ bởi Gradle sẽ bỏ qua các tác vụ khi dữ liệu đầu của tác vụ đấy (chẳng hạn như mã nguồn) được giữ nguyên. Do đó, bản dựng thứ hai khi giữ nguyên dữ liệu đầu vào luôn chạy nhanh hơn vì các tác vụ không được chạy lại. Vì vậy, việc chạy tác vụ clean giữa các bản dựng sẽ đảm bảo quy trình lập hồ sơ cho quy trình xây dựng được toàn vẹn.
    // On Mac or Linux, run the Gradle wrapper using "./gradlew".
    gradlew clean
    
  3. Thực hiện bản dựng gỡ lỗi của một trong các phiên bản sản phẩm của bạn, chẳng hạn như phiên bản dành cho "dev" với các cờ sau:
    gradlew --profile --offline --rerun-tasks assembleFlavorDebug
    
    • --profile: Bật tính năng lập hồ sơ.
    • --offline: Tắt Gradle trong quá trình tìm nạp các phần phụ thuộc trực tuyến. Điều này giúp đảm bảo rằng mọi sự chậm trễ khi Gradle cố gắng cập nhật phần phụ thuộc của bạn sẽ không ảnh hưởng đến dữ liệu hồ sơ của bạn. Bạn nên tạo sẵn dự án của mình một lần để đảm bảo Gradle đã tải xuống và lưu các phần phụ thuộc của bạn vào bộ nhớ đệm.
    • --rerun-tasks: Buộc Gradle chạy lại mọi tác vụ và bỏ qua mọi quy trình tối ưu hoá tác vụ.
  4. Hình 1. Chế độ xem dự án cho biết vị trí của báo cáo hồ sơ.

    Sau khi quá trình xây dựng hoàn tất, hãy sử dụng cửa sổ Dự án để điều hướng đến thư mục project-root/build/reports/profile/ (như trong hình 1).

  5. Nhấp chuột phải vào tệp profile-timestamp.html rồi chọn Mở trong trình duyệt > Mặc định. Báo cáo sẽ trông giống như trong hình 2. Bạn có thể kiểm tra từng thẻ trong báo cáo để tìm hiểu về bản dựng của mình, chẳng hạn như thẻ Thực thi tác vụ, cho biết thời gian Gradle cần để thực thi từng tác vụ bản dựng.

    Hình 2. Xem báo cáo trong trình duyệt.

  6. Không bắt buộc: Trước khi thực hiện bất kỳ thay đổi nào trong dự án hoặc cấu hình bản dựng, hãy lặp lại lệnh trong bước 3 nhưng bỏ qua cờ --rerun-tasks. Vì Gradle cố tiết kiệm thời gian bằng cách không thực thi lại các thao tác có dữ liệu đầu vào không thay đổi (được biểu thị dưới dạng UP-TO-DATE trong thẻ Task Execution (Thực thi tác vụ) của báo cáo, như trong hình 3), bạn có thể xác định những tác vụ đang thực hiện mà tác vụ đó đáng lẽ không nên thực hiện. Ví dụ: nếu :app:processDevUniversalDebugManifest không được đánh dấu là UP-TO-DATE, thì cấu hình bản dựng của bạn có thể tự động cập nhật tệp kê khai cho mọi bản dựng. Tuy nhiên, một số tác vụ cần phải chạy trong quá trình dựng, chẳng hạn như :app:checkDevDebugManifest.

    Hình 3. Chế độ xem kết quả thực thi tác vụ.

Giờ đây, khi đã có báo cáo hồ sơ bản dựng, bạn có thể bắt đầu tìm kiếm cơ hội tối ưu hoá bằng cách kiểm tra thông tin trong từng thẻ của báo cáo. Một số chế độ cài đặt bản dựng yêu cầu thử nghiệm vì lợi ích có thể khác nhau giữa các dự án và máy trạm. Ví dụ: các dự án có cơ sở mã lớn có thể hưởng lợi từ việc thu gọn mã để xoá mã không sử dụng và thu nhỏ kích thước ứng dụng. Tuy nhiên, các dự án nhỏ hơn có thể hưởng lợi nhiều hơn từ việc tắt hoàn toàn tính năng thu nhỏ mã. Ngoài ra, việc tăng kích thước vùng nhớ xếp đống của Gradle (bằng org.gradle.jvmargs) có thể tác động tiêu cực đến hiệu suất trên các máy có bộ nhớ thấp.

Sau khi thực hiện thay đổi cấu hình bản dựng, hãy quan sát kết quả của các bản sửa đổi bằng cách lặp lại các bước ở trên và lập một hồ sơ bản dựng mới. Ví dụ: hình 4 cho thấy một báo cáo cho cùng một ứng dụng mẫu sau khi áp dụng một số phương pháp tối ưu hoá cơ bản được mô tả trên trang này.

Hình 4. Xem một báo cáo mới sau khi tối ưu hoá tốc độ bản dựng.