Nghiên cứu điển hình

Grab giảm 32% lỗi ANR và cải thiện 35% thời gian khởi động nhờ R8 và tính năng Tối ưu hoá theo hồ sơ

Thời gian đọc: 5 phút

Grab là siêu ứng dụng hàng đầu Đông Nam Á, cung cấp dịch vụ giao hàng, di chuyển và tài chính cho hàng triệu người dùng tại 8 quốc gia thông qua một ứng dụng duy nhất. Với toàn bộ mã nguồn 10 năm tuổi chứa hơn 10 triệu dòng mã và hàng trăm thư viện của bên thứ ba được xây dựng bằng cách kết hợp Bazel và Gradle, nhóm Grab phải đối mặt với 3 thách thức ngày càng lớn: tỷ lệ Lỗi ứng dụng không phản hồi (ANR) cao, kích thước ứng dụng không ngừng tăng lên khi siêu ứng dụng này bổ sung các dịch vụ mới và thời gian khởi động tăng dần qua từng năm.  Bằng cách bật tính năng tối ưu hoá R8, tinh chỉnh quy tắc giữ lại, cũng như triển khai Hồ sơ cơ sở và Hồ sơ khởi động, Grab đã giảm 32% tỷ lệ lỗi ANR mà người dùng nhận thấy, giảm khoảng 30% kích thước tải ứng dụng xuống và cải thiện 35% thời gian khởi động.

Loại bỏ tình trạng tăng đột biến lỗi ANR sau khi phát hành để mang lại trải nghiệm mượt mà hơn cho người dùng

Trước khi tối ưu hoá quy trình xây dựng, Grab đã gặp phải tình trạng chậm trễ khi khởi động nguội và số lỗi Ứng dụng không phản hồi (ANR) tăng đột biến ngay sau khi có bản cập nhật mới, chủ yếu là do hao tổn tài nguyên khi đọc nhiều tệp DEX trong quá trình khởi chạy ứng dụng. 

Mặc dù Hồ sơ đám mây của Google Play tối ưu hoá các luồng chuẩn cho toàn bộ cơ sở người dùng, nhưng chúng không tối ưu hoá tất cả các đường dẫn khởi chạy cần thiết cho hành trình trọng yếu của người dùng Grab, chẳng hạn như khởi chạy biểu đồ phần phụ thuộc Dagger trong quá trình khởi chạy ứng dụng. Đối với các đường dẫn mã không có trong bản phân phối ban đầu, thiết bị sẽ dựa vào quá trình biên dịch JIT và hoạt động tối ưu hoá cục bộ trong nền trong những ngày tiếp theo. Việc triển khai Hồ sơ cơ sở tuỳ chỉnh đã giải quyết vấn đề này bằng cách cung cấp trực tiếp các hành trình trọng yếu của người dùng vào Hồ sơ đám mây của Google Play, mở rộng phạm vi tối ưu hoá để các đường dẫn chính này được biên dịch thành mã máy ngay khi cài đặt 

ANDDM_Grab_Inline_Quote_ALT.png

"Chúng tôi liên tục gặp phải tình trạng tỷ lệ ANR tăng đột biến sau mỗi lần phát hành ứng dụng hằng tuần", Nguyễn Văn Minh, Kỹ sư phần mềm chính trong nhóm Passenger App Core của Grab, giải thích. 

Để loại bỏ lỗi ANR và cải thiện thời gian khởi động, Grab đã triển khai các tính năng tối ưu hoá R8, Hồ sơ cơ sở và Hồ sơ khởi động. Tính năng tối ưu hoá theo hồ sơ cùng với trình tối ưu hoá R8 đã giúp cải thiện ngay 12% thời gian khởi động nguội nhờ R8 và thêm 15% nhờ Hồ sơ cơ sở, giúp thời gian khởi động nhanh hơn 27% và loại bỏ hiệu quả tình trạng tăng đột biến lỗi ANR sau khi phát hành.

Grab đã thực hiện việc tối ưu hoá ứng dụng này qua 3 giai đoạn chiến lược: bật tính năng tối ưu hoá R8 ở chế độ tương thích, chuyển sang Chế độ đầy đủ của R8, sau đó tinh chỉnh các quy tắc giữ lại bằng Trình phân tích cấu hình và thêm quy trình quản trị CI/CD.

Giai đoạn 1: Bật tính năng tối ưu hoá R8  (Ra mắt năm 2025)

Trong giai đoạn đầu tiên này, Grab đã chuyển từ tệp Proguard mặc định cũ sang sử dụng proguard-android-optimize.txt, cho phép tối ưu hoá R8.Vì toàn bộ mã nguồn đã hơn một thập kỷ tuổi và chứa hơn 10 triệu dòng mã, nên việc bật trình tối ưu hoá đòi hỏi phải giải quyết các cấu hình cũ và việc sử dụng phản chiếu.

Để tối đa hoá tác động của giai đoạn này, Grab đã phát hành bản cập nhật ứng dụng có bật chế độ R8 Compat cùng với Hồ sơ cơ sở và Hồ sơ khởi động. Nhờ nỗ lực kết hợp này, tỷ lệ lỗi ANR mà người dùng nhận thấy đã giảm ngay 25%, kích thước tải ứng dụng xuống giảm 16% và thời gian bắt đầu nguội tổng thể được cải thiện 27%.

Giai đoạn 2: Chế độ đầy đủ của R8 và di chuyển (Ra mắt năm 2026)

Được triển khai 9 tháng sau lần ra mắt đầu tiên, giai đoạn này đã chuyển ứng dụng sang Chế độ đầy đủ của R8, giúp mở khoá thêm nhiều tính năng tối ưu hoá. Mặc dù các thư viện chuẩn như Retrofit, Firebase và Jetpack Compose hoạt động liền mạch với R8 bằng các quy tắc tích hợp sẵn, nhưng việc di chuyển phần còn lại của toàn bộ mã nguồn khổng lồ được thực hiện bằng cách giải quyết các đường dẫn mã cụ thể có nhiều phản chiếu:

Để chuyển sang chế độ R8 Full, nhóm đã áp dụng quy tắc giữ lại của người dùng từ bản phát hành Gson mới nhất để đảm bảo phân tích cú pháp JSON liền mạch. Họ cũng giải quyết các thách thức về tối ưu hoá bằng proxy động Java, sử dụng phản chiếu để tạo các phương thức triển khai giao diện trong thời gian chạy. Vì R8 không thể phát hiện việc sử dụng thời gian chạy này trong quá trình biên dịch, nên R8 sẽ loại bỏ hoặc đổi tên các lớp cần thiết, khiến ứng dụng gặp sự cố. Thay vì áp dụng các ngoại lệ rộng có thể làm tăng kích thước ứng dụng, nhóm đã tách riêng các lớp cụ thể dựa trên phản chiếu và triển khai các quy tắc giữ lại có mục tiêu cao, giúp duy trì cả độ ổn định của ứng dụng và hiệu quả tối ưu hoá

Cuối cùng, Chế độ đầy đủ của R8 đã mang lại những cải tiến lớn và tổng hợp, bao gồm giảm thêm 14% kích thước tải xuống của ứng dụng, cải thiện thêm 10% thời gian khởi động và giảm thêm 10% tỷ lệ lỗi ANR mà người dùng nhận thấy. Sau khi di chuyển sang Chế độ đầy đủ của R8, toàn bộ mã nguồn của Grab đã đạt được Điểm tối ưu hoá R8 sau đây. 

  • Điểm tối ưu hoá: 72%
  • Điểm rút gọn: 73%
  • Điểm làm rối: 79,6%

Những điểm số này cung cấp ý kiến phản hồi về cấu hình R8 và nhu cầu tinh chỉnh các quy tắc giữ lại để tối đa hoá khả năng tối ưu hoá của R8. 

Giai đoạn 3: Tinh chỉnh quy tắc giữ lại 

Trong giai đoạn cuối của hành trình tối ưu hoá, Grab tập trung vào việc thiết lập tình trạng toàn bộ mã nguồn lâu dài, liên tục theo dõi hiệu suất và quản trị nghiêm ngặt quá trình biên dịch. Để đo lường một cách có hệ thống tình trạng của quy tắc và ngăn chặn tình trạng hồi quy cấu hình trong các chu kỳ phát hành đang diễn ra, nhóm đang tích hợp Trình phân tích cấu hình R8 vào quy trình đánh giá của họ. Công cụ này cung cấp thông tin chi tiết về mức độ hiệu quả của các quy tắc giữ lại của Grab trong việc cho phép R8 tối ưu hoá, rút gọn và làm rối mã nguồn của ứng dụng.

Bằng cách xây dựng trên nền tảng của Chế độ đầy đủ của R8 và cắt giảm một cách có phương pháp các quy tắc giữ lại cũ, Grab đang đạt được những cải tiến đáng kể trên cả ba khía cạnh cốt lõi được trình phân tích đánh giá:

  • Điểm tối ưu hoá: tăng từ 72% lên 78,2%
  • Điểm rút gọn: tăng từ 73% lên 79,1%
  • Điểm làm rối: tăng từ 79,6% lên 81,7%

Ngoài việc tăng điểm chẩn đoán, giai đoạn tinh chỉnh quy tắc giữ lại chuyên biệt này còn giúp giảm thêm khoảng 3,6% kích thước tải ứng dụng xuống. Quan trọng hơn, việc theo dõi các chỉ số này cung cấp cho nhóm kỹ thuật dữ liệu khách quan cần thiết để triển khai các biện pháp bảo vệ CI tự động trước khi hợp nhất, đảm bảo rằng mã mới được giới thiệu tuân thủ các tiêu chuẩn nghiêm ngặt về quy tắc giữ lại và hiệu suất đạt được sau nhiều nỗ lực vẫn được bảo vệ theo thời gian.

Kết quả

Những điểm cải thiện chính về hiệu suất sau khi triển khai trình tối ưu hoá R8:

Giai đoạn tối ưu hoáANRKích thướcKhởi động nguội
Giai đoạn 1: Trình tối ưu hoá R8 (Ra mắt năm 2025)Giảm ~25%Giảm 16%Cải thiện ~27%
Giai đoạn 2: Chế độ đầy đủ của R8 (Ra mắt năm 2026)Giảm 10%Giảm 14%Cải thiện 10%
Tối ưu hoá quy tắc giữ lại-Giảm 3,6%-
Tổng thể (Tích luỹ)Giảm ~32,5%Giảm ~30,4%~35% cải thiện

Thách thức về kỹ thuật 

Việc bật trình tối ưu hoá R8 trên toàn bộ mã nguồn đã có từ một thập kỷ trước của Grab đã gây ra những rào cản kỹ thuật quan trọng trên quy mô lớn:

  • Thử nghiệm: Một thập kỷ tích luỹ suy ngẫm đã tạo ra các trường hợp hiếm gặp trong quá trình tối ưu hoá hoàn toàn. Vì không thể kiểm thử toàn diện trên hàng triệu dòng mã, nên nhóm đã áp dụng phương pháp gia tăng, ưu tiên cao, cụ thể là tập trung kiểm thử vào mức độ sử dụng mã được các quy tắc giữ lại đang được thu hẹp bao gồm, đồng thời tinh chỉnh lặp lại các quy tắc đó để chỉ nhắm đến các lớp, phương thức hoặc trường cụ thể cần thiết cho việc phản chiếu nhằm tối đa hoá khả năng Tối ưu hoá của R8
  • Độ trễ của bản dựng và cấu trúc kết hợp: Hệ thống bản dựng kết hợp của Grab (Bazel để gỡ lỗi, Gradle cho bản phát hành R8) có nghĩa là các thay đổi thử nghiệm đối với R8 cần đến 2 giờ trong CI từ xa. Để thu hẹp khoảng cách về ý kiến phản hồi, Grab đã điều chỉnh các bản dựng QA hằng ngày cho phù hợp với hành vi của R8 trong môi trường thực tế, đồng thời tự động hoá việc thu thập các cấu phần phần mềm chính của bản dựng CI như mapping.txt, configuration.txt để đơn giản hoá quy trình điều tra.
  • Chu kỳ gỡ lỗi chậm: Việc tìm ra nguyên nhân gốc rễ trên hàng trăm lớp được tối ưu hoá đã đặt gánh nặng lên một nhóm tinh gọn. Grab đã giải quyết vấn đề này bằng cách tự động hoá việc tải tệp ánh xạ lên Firebase Crashlytics thông qua Gradle và gói các tệp ánh xạ gỡ rối mã nguồn cho Play Console, cho phép phân giải biểu tượng tức thì và chính xác cho các vấn đề về thời gian chạy

Tác động lâu dài của mã khi dùng R8

Ngoài việc cải thiện hiệu suất ngay lập tức, việc bật trình tối ưu hoá R8 còn mang lại lợi thế cấu trúc lâu dài cho toàn bộ mã nguồn khổng lồ của Grab. Tại Grab, nhóm đã sử dụng công cụ nguồn mở app-sizer để đo lường tác động của R8 và nhận thấy rằng R8 đã giảm vĩnh viễn dung lượng tải xuống của từng dòng mã trong ứng dụng.

ANDDM_Grab_Inline_Metric.png

Trong kỷ nguyên phát triển sử dụng AI, khi khối lượng mã tăng nhanh, thì việc tối ưu hoá này thậm chí còn quan trọng hơn. Nếu không có R8, việc mở rộng mã sẽ làm tăng kích thước tải xuống theo tỷ lệ. R8 đảm bảo rằng cùng một tỷ lệ mở rộng mã sẽ duy trì mức sử dụng bộ nhớ giảm đáng kể.

Lưu ý: Phương pháp: Đóng góp của Kotlin/Java trong app-sizer vào kích thước tải ứng dụng xuống (thiết bị đại diện), chia cho LOC Kotlin chính thức (cloc; chỉ các nhóm tài nguyên chính; không bao gồm các mô-đun kiểm thử, mô phỏng và mẫu).

Quản lý quy tắc và biện pháp bảo vệ của Keep

Để ngăn chặn tình trạng suy giảm quy tắc và duy trì khả năng tối ưu hoá của R8 theo thời gian, Grab dự định tích hợp các biện pháp bảo vệ tự động trước khi hợp nhất trực tiếp vào quy trình CI của họ. Theo chính sách này, các quy tắc lưu giữ mới sẽ bị hạn chế nghiêm ngặt, mọi quy tắc bổ sung đều phải có lý do rõ ràng và phải được xem xét. Nhóm tại Grab theo dõi điểm tối ưu hoá R8 trong CI. Dựa trên kết quả này, nhóm dự định sử dụng các chỉ số này làm vòng hồi tiếp liên tục, nhờ đó, các lỗi hồi quy sẽ được phát hiện trước khi hợp nhất và các lợi ích tối ưu hoá được duy trì trong mọi chu kỳ phát hành. 

R8-rules-score (1).png

Hình 1: Trang tổng quan CI theo dõi điểm số về việc rút gọn, tối ưu hoá và làm rối mã nguồn của R8 theo thời gian trong các bản dựng của Grab để chủ động phát hiện và ngăn chặn tình trạng hồi quy trên các bản phát hành.

Kết luận 

Đối mặt với thách thức từ một toàn bộ mã nguồn lớn và ngày càng hoàn thiện, nhóm Pax App Core của Grab đã chủ động giải quyết vấn đề thời gian khởi động tăng và hiệu suất tăng đột biến sau khi phát hành bằng cách triển khai Hồ sơ cơ sở và liên tục cải thiện Cấu hình R8. Nhóm đã giảm khoảng 32,5% số lỗi ANR mà người dùng nhận thấy, giảm khoảng 30,3% kích thước tải ứng dụng xuống và cải thiện khoảng 35% thời gian bắt đầu nguội, đồng thời cải thiện đáng kể hiệu quả mở rộng toàn bộ mã nguồn.

Những điểm tối ưu hoá này mang lại trải nghiệm siêu ứng dụng nhanh hơn, ổn định hơn cho hàng triệu người dùng trên khắp Đông Nam Á, đồng thời cung cấp cho các kỹ sư của Grab công cụ để xây dựng tính năng nhanh hơn mà không phải lo về việc kích thước tải xuống tăng nhanh.

Để bắt đầu và tìm hiểu thêm về R8, hãy truy cập vào - d.android.com/r8.

Tác giả:
Tiếp tục đọc