Nghiên cứu điển hình: cách nhóm Gmail Wear OS cải thiện khả năng khởi động ứng dụng thêm 50%

Quá trình khởi động ứng dụng thể hiện ấn tượng đầu tiên của ứng dụng đối với người dùng. Và người dùng không muốn chờ đợi, vì vậy, bạn cần đảm bảo ứng dụng khởi động nhanh. Để cho bạn thấy cách nhóm phát triển ứng dụng thực tế phát hiện và chẩn đoán sự cố khi khởi động ứng dụng, sau đây là những gì nhóm Gmail Wear OS đã làm.

Nhóm Gmail Wear OS đã nỗ lực tối ưu hoá, đặc biệt chú trọng vào hiệu suất khởi động ứng dụng và kết xuất trong thời gian chạy để đáp ứng các tiêu chí về hiệu suất ứng dụng của nhóm. Tuy nhiên, ngay cả khi bạn không có các ngưỡng cụ thể để nhắm mục tiêu, thì hầu như luôn có khả năng cải thiện quá trình khởi động ứng dụng nếu bạn dành thời gian để tìm hiểu vấn đề.

Ghi lại dấu vết và xem quá trình khởi động ứng dụng

Để bắt đầu phân tích, hãy thu thập dấu vết bao gồm quá trình khởi động ứng dụng để kiểm tra kỹ hơn trong Perfetto hoặc Android Studio. Nghiên cứu điển hình này sử dụng Perfetto vì nó cho bạn biết những gì đang diễn ra trên hệ thống thiết bị, ngoài ứng dụng của bạn. Khi bạn tải dấu vết lên bằng Perfetto, dấu vết sẽ có dạng như sau:

Hình 1. Chế độ xem dấu vết ban đầu trong Perfetto.

Vì trọng tâm là cải thiện quá trình khởi động ứng dụng, nên hãy tìm hàng có chỉ số tuỳ chỉnh Android App Startups (Khởi động ứng dụng Android). Bạn nên ghim hàng đó vào đầu khung hiển thị bằng cách nhấp vào biểu tượng ghim xuất hiện khi bạn di chuột qua hàng đó. Thanh hoặc Lát cắt mà bạn thấy trên hàng Android App Startups (Khởi động ứng dụng Android) cho biết khoảng thời gian mà quá trình khởi động ứng dụng diễn ra cho đến khi khung ứng dụng đầu tiên được vẽ lên màn hình. Vì vậy, bạn nên tìm các vấn đề hoặc nút thắt cổ chai ở đó.

Hàng Android App Startups (Khởi động ứng dụng Android), trong đó tuỳ chọn ghim được làm nổi bật.
Hình 2. Hãy ghim chỉ số tuỳ chỉnh Android App Startups (Khởi động ứng dụng Android) vào đầu trang tổng quan để dễ phân tích hơn.

Lưu ý rằng chỉ số Android App Startups (Khởi động ứng dụng Android) thể hiện thời gian để hiển thị khung hình đầu tiên, ngay cả khi bạn đang sử dụng reportFullyDrawn(). Để xác định thời gian hiển thị đầy đủ, hãy tìm kiếm reportFullyDrawn() trong hộp tìm kiếm Perfetto.

Kiểm tra luồng chính

Trước tiên, hãy kiểm tra xem điều gì đang xảy ra trên luồng chính. Luồng chính rất quan trọng vì đây thường là nơi diễn ra toàn bộ quá trình kết xuất giao diện người dùng; khi bị chặn, hoạt động vẽ không thể xảy ra và ứng dụng của bạn có vẻ như bị treo. Vì vậy, bạn cần đảm bảo rằng không có hoạt động nào diễn ra trong thời gian dài trên luồng chính.

Để tìm luồng chính, hãy tìm hàng có tên gói của ứng dụng rồi mở rộng hàng đó. 2 hàng trùng tên với gói (thường là 2 hàng đầu tiên trong phần) đại diện cho luồng chính. Trong 2 hàng của luồng chính, hàng đầu tiên biểu thị trạng thái CPU và hàng thứ hai biểu thị các điểm theo dõi. Ghim 2 hàng luồng chính bên dưới chỉ số Android App Startups (Khởi động ứng dụng Android).

Đã ghim các hàng trong phần Khởi động ứng dụng Android và chuỗi chính.
Hình 3. Ghim các hàng luồng chính bên dưới chỉ số tuỳ chỉnh Android App Startups (Khởi động ứng dụng Android) để hỗ trợ việc phân tích.

Thời gian ở trạng thái có thể chạy và tranh chấp CPU

Để xem tổng hợp hoạt động của CPU trong quá trình khởi động ứng dụng, hãy kéo con trỏ qua luồng chính để nắm bắt khoảng thời gian khởi động ứng dụng. Bảng điều khiển Thread States (Trạng thái luồng) xuất hiện cho bạn biết tổng thời gian dành cho mỗi trạng thái CPU trong phạm vi thời gian đã chọn.

Xem thời gian ở trạng thái Runnable. Khi một luồng ở trạng thái Runnable, luồng này có thể thực hiện công việc nhưng không có công việc nào được lên lịch. Điều đó có thể cho biết thiết bị đang quá tải và không thể lên lịch các tác vụ có mức độ ưu tiên cao. Ứng dụng hàng đầu mà người dùng nhìn thấy có mức độ ưu tiên cao nhất về việc lập lịch. Vì vậy, một luồng chính ở trạng thái rảnh thường cho biết rằng các quy trình chuyên sâu trong ứng dụng, chẳng hạn như kết xuất ảnh động, đang cạnh tranh với luồng chính về thời gian của CPU.

Luồng chính được làm nổi bật với tổng thời gian ở nhiều trạng thái trong bảng điều khiển Trạng thái chuỗi.
Hình 4. Đánh giá thời gian tương đối ở các trạng thái từ Runnable đến Running để nắm được ban đầu mức độ tranh chấp về CPU.

Tỷ lệ thời gian ở trạng thái Runnable so với thời gian ở trạng thái Running càng cao, thì càng có nhiều khả năng xảy ra tranh chấp CPU. Khi kiểm tra các vấn đề về hiệu suất theo cách này, trước tiên, hãy tập trung vào khung chạy dài nhất và tìm các khung nhỏ hơn.

Khi phân tích thời gian ở trạng thái Runnable, hãy xem xét phần cứng của thiết bị. Vì ứng dụng được mô tả đang chạy trên một thiết bị đeo có 2 CPU, nên kỳ vọng sẽ dành nhiều thời gian hơn ở trạng thái Runnable và nhiều xung đột CPU hơn với các quy trình khác so với khi chúng ta xem một thiết bị có nhiều CPU hơn. Mặc dù thời gian ở trạng thái Runnable nhiều hơn dự kiến đối với một ứng dụng điện thoại thông thường, nhưng điều này có thể dễ hiểu khi áp dụng cho thiết bị đeo.

Thời gian ở OpenDexFilesFromOat*

Bây giờ, hãy kiểm tra thời gian dành cho OpenDexFilesFromOat*; trong dấu vết, điều này xảy ra cùng lúc với lát cắt bindApplication. Lát cắt này thể hiện thời gian cần thiết để đọc tệp DEX của ứng dụng.

Giao dịch liên kết bị chặn

Tiếp theo, hãy kiểm tra các giao dịch liên kết. Giao dịch liên kết biểu thị các lệnh gọi giữa ứng dụng và máy chủ: trong trường hợp này, ứng dụng (ứng dụng) gọi hệ thống Android (máy chủ) bằng binder transaction và máy chủ phản hồi bằng binder reply. Đảm bảo ứng dụng không thực hiện các giao dịch liên kết không cần thiết trong quá trình khởi động, vì các giao dịch này làm tăng nguy cơ tranh chấp CPU. Nếu có thể, hãy trì hoãn công việc liên quan đến các lệnh gọi liên kết sau khoảng thời gian khởi động ứng dụng. Nếu bạn phải thực hiện các giao dịch liên kết, hãy đảm bảo các giao dịch đó không mất nhiều thời gian hơn tốc độ làm mới Vsync của thiết bị.

Giao dịch liên kết đầu tiên, thường xảy ra cùng lúc với lát ActivityThreadMain, có vẻ như khá dài trong trường hợp này. Để tìm hiểu thêm về những điều có thể đang xảy ra, hãy làm theo các bước sau:

  1. Để xem phản hồi liên kết được liên kết và tìm hiểu thêm về cách giao dịch liên kết đang được ưu tiên, hãy nhấp vào phần mối quan tâm của giao dịch liên kết.
  2. Để xem câu trả lời liên kết, hãy chuyển đến bảng điều khiển Lựa chọn hiện tại rồi nhấp vào câu trả lời liên kết trong phần Chuỗi đang theo dõi. Trường Thread (Luồng) cũng cho bạn biết luồng phản hồi liên kết xảy ra nếu bạn muốn điều hướng đến đó theo cách thủ công; thao tác này sẽ diễn ra trong một quy trình khác. Một dòng sẽ xuất hiện kết nối giao dịch liên kết và phản hồi.

    Một đường thẳng kết nối lệnh gọi liên kết và trả lời.
    Hình 5. Xác định các giao dịch liên kết diễn ra trong quá trình khởi động ứng dụng và đánh giá xem bạn có thể trì hoãn các giao dịch đó hay không.
  3. Để xem cách máy chủ hệ thống đang xử lý giao dịch liên kết này, hãy ghim các luồng Cpu 0Cpu 1 lên đầu màn hình.

  4. Tìm các quy trình hệ thống xử lý phản hồi liên kết bằng cách tìm các lát cắt có tên luồng phản hồi liên kết, trong trường hợp này là "Binder:687_11 [2542]". Nhấp vào các quy trình hệ thống có liên quan để biết thêm thông tin về giao dịch liên kết.

Hãy xem quy trình hệ thống này liên kết với giao dịch liên kết quan tâm xảy ra trên CPU 0:

Quy trình hệ thống có Trạng thái kết thúc là "Có thể chạy (Được giành quyền trước).
Hình 6. Quy trình hệ thống này đang ở trạng thái Runnable (Preempted), cho biết rằng quy trình này đang bị trễ.

Trạng thái kết thúc cho biết Runnable (Preempted), nghĩa là quá trình này đang bị trì hoãn vì CPU đang làm việc khác. Để tìm hiểu xem sự kiện nào đang bị giành quyền, hãy mở rộng các hàng Sự kiện Ftrace. Trong thẻ Sự kiện Ftrace đã có sẵn, hãy di chuyển qua và tìm các sự kiện liên quan đến luồng liên kết quan tâm "Binder:687_11 [2542]". Trong khoảng thời gian quy trình hệ thống bị giành quyền, có 2 sự kiện máy chủ hệ thống bao gồm đối số "decon" đã xảy ra, có nghĩa là các sự kiện đó có liên quan đến bộ điều khiển hiển thị. Điều này nghe có vẻ hợp lý vì bộ điều khiển màn hình đặt các khung hình lên màn hình — một nhiệm vụ quan trọng! Sau đó, hãy giữ nguyên các sự kiện.

Các sự kiện FTrace liên kết với giao dịch liên kết quan tâm được làm nổi bật.
Hình 7. Các sự kiện FTrace cho biết rằng giao dịch liên kết đang bị các sự kiện của bộ điều khiển hiển thị trì hoãn.

Hoạt động JIT

Để điều tra hoạt động biên dịch đúng thời điểm (JIT), hãy mở rộng các quy trình thuộc về ứng dụng của bạn, tìm 2 hàng "nhóm luồng Jiit" rồi ghim lên đầu khung hiển thị. Vì ứng dụng này hưởng lợi từ Hồ sơ cơ sở trong quá trình khởi động ứng dụng, nên rất ít hoạt động JIT sẽ xảy ra cho đến khi khung đầu tiên được vẽ, được biểu thị bằng sự kết thúc của lệnh gọi Choreographer.doFrame đầu tiên. Tuy nhiên, hãy lưu ý lý do JIT compiling void khởi động chậm. Điều này cho thấy hoạt động hệ thống diễn ra trong điểm theo dõi có nhãn Application creation đang gây ra nhiều hoạt động JIT trong nền. Để giải quyết vấn đề này, hãy thêm các sự kiện xảy ra ngay sau khi khung đầu tiên được vẽ vào Hồ sơ cơ sở bằng cách mở rộng bộ sưu tập hồ sơ đến điểm mà ứng dụng đã sẵn sàng để sử dụng. Trong nhiều trường hợp, bạn có thể làm việc này bằng cách thêm một dòng vào cuối bài kiểm thử Macrobenchmark thu thập Hồ sơ cơ sở. Bài kiểm thử này sẽ chờ một tiện ích giao diện người dùng cụ thể xuất hiện trên màn hình, cho biết màn hình đã được điền đầy đủ dữ liệu.

Nhóm luồng Jit, trong đó lát cắt "Jit compile void" được làm nổi bật.
Hình 8. Nếu bạn thấy nhiều hoạt động JIT, hãy mở rộng Hồ sơ cơ sở đến thời điểm ứng dụng đã sẵn sàng để sử dụng.

Kết quả

Theo kết quả phân tích này, nhóm Gmail Wear OS đã thực hiện những cải tiến sau:

  • Vì nhận thấy tranh chấp trong quá trình khởi động ứng dụng khi xem hoạt động của CPU, họ đã thay thế ảnh động vòng quay dùng để cho biết ứng dụng đang tải bằng một hình ảnh tĩnh duy nhất. Họ cũng kéo dài màn hình chờ để trì hoãn trạng thái ánh sáng lung linh (trạng thái thứ hai của màn hình dùng để cho biết ứng dụng đang tải) nhằm giải phóng tài nguyên CPU. Cách này giúp độ trễ khi khởi động ứng dụng tăng 50%.
  • Từ việc xem xét thời gian dành cho OpenDexFilesFromOat*hoạt động JIT, họ đã cho phép R8 viết lại Hồ sơ cơ sở. Điều này giúp độ trễ khởi động ứng dụng giảm 20%.

Sau đây là một số mẹo của đội ngũ về cách phân tích hiệu quả hiệu suất của ứng dụng:

  • Thiết lập một quy trình liên tục có khả năng tự động thu thập dấu vết và kết quả. Hãy cân nhắc thiết lập tính năng theo dõi tự động cho ứng dụng của bạn bằng cách sử dụng đo điểm chuẩn.
  • Hãy sử dụng tính năng thử nghiệm A/B đối với những thay đổi bạn cho rằng sẽ cải thiện mọi thứ. Sau đó, từ chối những thay đổi đó nếu chúng không cải thiện. Bạn có thể đo lường hiệu suất trong nhiều trường hợp bằng cách sử dụng thư viện Macrobenchmark.

Để tìm hiểu thêm, hãy xem các tài nguyên sau: