Phân tích bằng tính năng phân tích trực quan kết xuất GPU

Công cụ phân tích trực quan kết xuất GPU cho biết thời gian tương đối mà mỗi giai đoạn của quy trình kết xuất cần để hiển thị khung trước. Kiến thức này có thể giúp bạn xác định nút thắt cổ chai trong quy trình để bạn có thể biết những gì cần tối ưu hóa nhằm cải thiện hiệu suất hiển thị của ứng dụng.

Trang này giải thích ngắn gọn những gì xảy ra trong từng giai đoạn của quy trình, đồng thời trình bày các vấn đề có thể gây nút thắt cổ chai trong từng giai đoạn. Trước khi đọc trang này, bạn nên làm quen với thông tin trình bày trong phần Phân tích trực quan kết xuất GPU. Ngoài ra, để hiểu cách tất cả các giai đoạn kết hợp với nhau, bạn nên xem lại cách hoạt động của quy trình kết xuất.

Minh họa bằng hình ảnh

Công cụ phân tích trực quan kết xuất GPU hiển thị các giai đoạn và thời gian tương đối ở dạng biểu đồ: biểu đồ được mã hóa màu. Hình 1 cho thấy một ví dụ có phần hiển thị như vậy.

Hình 1. Biểu đồ phân tích trực quan kết xuất GPU

Mỗi phân đoạn của mỗi thanh dọc hiển thị trong biểu đồ phân tích trực quan kết xuất GPU đại diện cho một giai đoạn trong quy trình, và được đánh dấu bằng một màu cụ thể trong biểu đồ thanh. Hình 2 cho thấy ý nghĩa của từng màu hiển thị.

Hình 2. Chú giải biểu đồ phân tích trực quan kết xuất GPU

Sau khi hiểu rõ từng đặc điểm màu sắc, bạn có thể nhắm mục tiêu các khía cạnh cụ thể của ứng dụng nhằm cố gắng tối ưu hóa hiệu suất hiển thị của ứng dụng.

Các giai đoạn và ý nghĩa của các giai đoạn

Phần này giải thích những gì xảy ra trong mỗi giai đoạn tương ứng với màu sắc ở Hình 2, cũng như các nguyên nhân gây nút thắt cổ chai cần chú ý.

Xử lý đầu vào

Giai đoạn xử lý đầu vào của quy trình đo lường thời gian ứng dụng xử lý sự kiện đầu vào. Chỉ số này cho biết lượng thời gian ứng dụng thực thi mã được gọi do các lệnh gọi lại sự kiện đầu vào.

Khi phân đoạn này lớn

Các giá trị cao trong khu vực này thường là do có quá nhiều công việc hoặc công việc phức tạp xảy ra bên trong lệnh gọi lại sự kiện xử lý đầu vào. Vì các lệnh gọi lại này luôn xảy ra trên chuỗi chính, nên các giải pháp cho vấn đề này sẽ tập trung vào việc tối ưu hóa tác vụ trực tiếp hoặc chuyển tác vụ sang một luồng khác.

Cũng cần lưu ý cuộn RecyclerView có thể xuất hiện trong giai đoạn này. RecyclerView di chuyển ngay lập tức ngay khi có sự kiện chạm. Do đó, tính năng này có thể làm tăng hoặc điền chế độ xem mục mới. Vì lý do này, bạn cần phải thực hiện thao tác này càng nhanh càng tốt. Các công cụ định cấu hình như Traceview hoặc Systrace có thể giúp bạn điều tra thêm.

Ảnh động

Giai đoạn Ảnh động cho bạn biết thời gian cần để đánh giá tất cả ảnh động đã chạy trong khung đó. Các toán tử hoạt động phổ biến nhất là ObjectAnimator, ViewPropertyAnimatorChuyển tiếp.

Khi phân đoạn này lớn

Các giá trị cao trong khu vực này thường là kết quả của việc thực thi do có một số thay đổi về thuộc tính của ảnh động. Ví dụ: một ảnh động từ cử chỉ hất, cuộn ListView hoặc RecyclerView của bạn sẽ dẫn đến một lượng lớn lượt xem và tăng giả tạo.

Đo lường/bố cục

Để có thể vẽ các mục chế độ xem trên màn hình của bạn, Android sẽ thực thi hai thao tác cụ thể trên bố cục và chế độ xem trong hệ phân cấp chế độ xem.

Đầu tiên, hệ thống đo lường các mục chế độ xem. Mỗi chế độ xem và bố cục đều có dữ liệu cụ thể mô tả kích thước của đối tượng trên màn hình. Một số chế độ xem có thể có kích thước cụ thể; một số khác có kích thước thích ứng với kích thước của vùng chứa bố cục mẹ

Tiếp đến, hệ thống bố trí các mục trong chế độ xem. Sau khi tính toán kích thước của các chế độ xem con, hệ thống có thể tiến hành bố trí, định cỡ và định vị các chế độ xem trên màn hình.

Hệ thống thực hiện đo lường và bố cục không chỉ cho các chế độ xem được vẽ, mà còn cho hệ phân cấp chính của các chế độ xem đó, đến hết chế độ xem gốc.

Khi phân đoạn này lớn

Nếu ứng dụng của bạn dành quá nhiều thời gian cho mỗi khung hình ở khu vực này, thì thường là do số lượt xem lớn cần được bố trí, hoặc các vấn đề như thuế kép ở sai vị trí trong hệ phân cấp. Trong cả hai trường hợp này, bạn cần giải quyết vấn đề hiệu suất bao gồm cải thiện hiệu suất của hệ phân cấp chế độ xem.

Mã mà bạn đã thêm vào onLayout(boolean, int, int, int, int) hoặc onMeasure(int, int) cũng có thể gây ra các vấn đề về hiệu suất. TraceviewSystrace có thể giúp bạn kiểm tra ngăn xếp cuộc gọi để xác định các vấn đề mà mã của bạn có thể gặp phải.

Ngang nhau

Giai đoạn vẽ sẽ dịch các hoạt động kết xuất của chế độ xem, chẳng hạn như vẽ nền hoặc vẽ văn bản, thành một chuỗi các lệnh vẽ gốc. Hệ thống sẽ ghi lại các lệnh này vào danh sách hiển thị.

Thanh Vẽ ghi lại khoảng thời gian cần thiết để hoàn thành việc ghi các lệnh vào danh sách hiển thị, cho tất cả các chế độ xem cần được cập nhật trên màn hình khung này. Thời gian đo được áp dụng cho mọi mã mà bạn đã thêm vào đối tượng giao diện người dùng trong ứng dụng của mình. Ví dụ về mã đó có thể là onDraw(), dispatchDraw(), và draw ()methods khác nhau thuộc các lớp con của lớp Drawable.

Khi phân đoạn này lớn

Nói một cách ngắn gọn, bạn có thể hiểu chỉ số này là chỉ số thời gian để chạy tất cả các lệnh gọi đến onDraw() cho mỗi chế độ xem không hợp lệ. Quá trình đo lường này bao gồm bất kỳ thời gian gửi lệnh vẽ nào cho thành phần con và những đối tượng vẽ được có thể hiện diện. Do đó, khi bạn thấy thanh này tăng đột biến, nguyên nhân có thể là do một loạt lượt xem đột nhiên bị vô hiệu hóa. Việc vô hiệu hóa khiến bạn cần phải tạo lại danh sách hiển thị của các chế độ xem. Ngoài ra, thời gian dài có thể là do một vài chế độ xem tùy chỉnh có số logic cực kỳ phức tạp trong phương thức onDraw() của chúng.

Đồng bộ hoá/tải lên

Chỉ số Đồng bộ hóa và tải lên thể hiện thời gian cần thiết để chuyển các đối tượng bitmap từ bộ nhớ CPU sang bộ nhớ GPU trong khung hiện tại.

Là các bộ xử lý khác nhau, CPU và GPU có các vùng RAM khác nhau dành riêng cho quá trình xử lý. Khi bạn vẽ một bitmap trên Android, hệ thống sẽ chuyển bitmap vào bộ nhớ GPU trước khi GPU có thể kết xuất bitmap vào màn hình. Sau đó, GPU lưu vào bộ nhớ đệm bitmap để hệ thống không cần phải chuyển lại dữ liệu trừ khi kết cấu bị loại khỏi bộ nhớ đệm cấu trúc GPU.

Lưu ý: Trên các thiết bị Lollipop, giai đoạn này có màu tím.

Khi phân đoạn này lớn

Tất cả tài nguyên cho một khung cần phải nằm trong bộ nhớ GPU trước khi có thể dùng để vẽ khung. Điều đó có nghĩa là giá trị cao của chỉ số này đại diện cho một lượng lớn lượt tải tài nguyên nhỏ hoặc một lượng nhỏ lượt tải các tài nguyên rất lớn. Trường hợp phổ biến là khi ứng dụng hiển thị một bitmap gần với kích thước của màn hình. Một trường hợp khác là khi ứng dụng hiển thị một lượng lớn các hình thu nhỏ.

Để thu nhỏ thanh này, bạn có thể sử dụng các kỹ thuật như bên dưới:

  • Đảm bảo độ phân giải bitmap của bạn không lớn hơn kích thước mà chúng sẽ hiển thị. Ví dụ: ứng dụng của bạn nên tránh hiển thị hình ảnh có kích thước 1024x1024 dưới dạng hình ảnh có kích thước 48x48.
  • Tận dụng lợi thế của hàm prepareToDraw() để tải bitmap lên một cách không đồng bộ trước giai đoạn đồng bộ hoá tiếp theo.

Lệnh thực thi

Phân đoạn Lệnh thực thi cho biết thời gian đưa ra tất cả các lệnh cần thiết để vẽ danh sách hiển thị lên màn hình.

Để vẽ các danh sách hiển thị lên màn hình, hệ thống sẽ gửi các lệnh cần thiết đến GPU. Thông thường, công cụ sẽ thực hiện hành động này thông qua API OpenGL ES.

Quá trình này sẽ mất một chút thời gian, vì hệ thống thực hiện chuyển đổi lần cuối và cắt nội dung cho mỗi lệnh trước khi gửi lệnh đến GPU. Chi phí bổ sung lúc này phát sinh ở phía GPU, đây là nơi tính toán các lệnh cuối cùng. Các lệnh này bao gồm các phép biến đổi cuối cùng và các thao tác cắt bổ sung.

Khi phân đoạn này lớn

Thời gian dành cho giai đoạn này là thước đo trực tiếp về độ phức tạp và số lượng của danh sách hiển thị mà hệ thống kết xuất trong một khung hình nhất định. Chẳng hạn như việc có nhiều thao tác vẽ, đặc biệt là trong trường hợp có một khoản chi phí cố định nhỏ cho mỗi lần vẽ nguyên bản, có thể làm tăng thời gian này. Ví dụ:

Kotlin

for (i in 0 until 1000) {
    canvas.drawPoint()
}

Java

for (int i = 0; i < 1000; i++) {
    canvas.drawPoint()
}

chi phí phát hành đắt hơn rất nhiều so với:

Kotlin

canvas.drawPoints(thousandPointArray)

Java

canvas.drawPoints(thousandPointArray);

Không phải lúc nào cũng có mối tương quan 1: 1 giữa việc ra lệnh và thực sự vẽ danh sách hiển thị. Khác với Lệnh thực thi, chỉ số Vẽ không ghi lại thời gian cần thiết để gửi lệnh vẽ tới GPU mà sẽ đại diện cho thời gian cần thiết để ghi lại các lệnh đã đưa ra vào danh sách hiển thị.

Sự khác biệt này phát sinh vì danh sách hiển thị sẽ được hệ thống lưu vào bộ nhớ đệm bất cứ khi nào có thể. Kết quả là trong một số trường hợp khi cuộn, chuyển đổi hoặc thực hiện các thao tác ảnh động, hệ thống sẽ được yêu cầu phải gửi lại danh sách hiển thị, nhưng không thực sự phải tạo lại danh sách đó - lấy lại lệnh vẽ từ đầu. Kết quả là bạn có thể thấy thanh “Phát hành lệnh” cao mà không thấy thanh Lệnh vẽ cao.

Vùng đệm quy trình/hoán đổi

Sau khi Android hoàn tất việc gửi danh sách hiển thị tới GPU, hệ thống sẽ đưa ra một lệnh cuối cùng để báo cho trình điều khiển đồ họa biết hệ thống đã thực hiện lệnh đó với khung hình hiện tại. Tại thời điểm này, trình điều khiển cuối cùng có thể hiển thị hình ảnh cập nhật cho màn hình.

Khi phân đoạn này lớn

Điều quan trọng là bạn cần phải biết GPU thực thi hoạt động song song với CPU. Hệ thống Android sẽ đưa ra các lệnh vẽ cho GPU, sau đó chuyển sang thao tác tiếp theo. GPU sẽ đọc các lệnh vẽ đó từ hàng đợi và xử lý các lệnh đó.

Trong những tình huống CPU đưa ra các lệnh nhanh hơn mức GPU tiêu thụ chúng, hàng đợi giao tiếp giữa các bộ xử lý có thể trở nên đầy. Khi điều này xảy ra, CPU sẽ chặn và đợi cho đến khi có khoảng trống trong hàng đợi để đặt lệnh tiếp theo. Trạng thái nghẽn hàng đợi này thường phát sinh trong giai đoạn Trao đổi vùng đệm, vì vào thời điểm đó, hệ thống đã gửi một lệnh là toàn bộ khung hình.

Mấu chốt để giảm thiểu vấn đề này là giảm bớt sự phức tạp của công việc hoạt động trên GPU, tương tự như cách bạn sẽ làm đối với giai đoạn "Lệnh thực thi".

Khác

Ngoài thời gian hệ thống kết xuất thực hiện công việc, còn có một nhóm công việc khác diễn ra trên chuỗi chính và không liên quan đến quá trình kết xuất. Thời gian mà công việc này dùng được báo cáo là thời gian khác. Thời gian khác thường thể hiện công việc có thể diễn ra trên luồng giao diện người dùng giữa hai khung hình kết xuất liên tiếp.

Khi phân đoạn này lớn

Nếu giá trị này cao, thì có thể ứng dụng của bạn có các lệnh gọi lại, ý định hoặc hoạt động khác sẽ diễn ra trên một chuỗi khác. Các công cụ như Dấu vết phương thức hoặc Systrace có thể cho phép hiển thị các tác vụ đang chạy trên luồng chính. Thông tin này có thể giúp bạn nhắm mục tiêu các cải thiện về hiệu suất.