Công cụ và hiệu suất

Trace me if you can: Automated instrumentation and field profiling

Loa

  • Rahul Ravikumar

    Kỹ sư phần mềm chính

Giới thiệu

Giới thiệu về bài thuyết trình

Để tối ưu hoá ứng dụng Android, bạn cần có thông tin chi tiết về quy trình làm việc đồng thời và trạng thái tạm ngưng coroutine.

Mặc dù việc lập hồ sơ cục bộ cung cấp thông tin tổng quan hữu ích, nhưng việc mở rộng quy mô những thông tin chi tiết này sang môi trường thực tế mà không tốn thêm tài nguyên vẫn là một thách thức.

Phiên này cung cấp một bản thiết kế kiến trúc cho vòng hồi tiếp hiệu suất liên tục.

androidx.tracing 2.0: Hoán đổi phần phụ trợ một cách linh động và truyền ngữ cảnh coroutine (luồng Perfetto) giữa các luồng.

Đo lường thông minh: Sử dụng tính năng theo dõi ART cục bộ để tìm điểm phát sóng, sau đó tự động chèn các điểm theo dõi có mức hao tổn thấp.

Đo điểm chuẩn vĩ mô: Theo dõi lỗi hồi quy và xác minh mức tăng hiệu suất trên thiết bị thực trong CI.

Theo dõi tại chỗ: Vượt ra ngoài phạm vi theo dõi cục bộ và CI bằng ProfilingManager.

Tóm tắt

  • Tính năng Theo dõi 2.0 thay thế các API theo dõi cũ của Android để loại bỏ giới hạn về độ dài nhãn và chạy nhanh hơn gấp 7 lần. Thư viện này cũng cho phép bạn tạo bồn lưu trữ tuỳ chỉnh cho các phần phụ trợ như OpenTelemetry.
  • Tính năng truyền ngữ cảnh liên kết các mối quan hệ nhân quả giữa các luồng, coroutine và quy trình. Bạn có thể theo dõi quá trình thực thi không đồng bộ bằng traceCoroutine hoặc truyền mã thông báo truyền vào mã dựa trên lệnh gọi lại.
  • Jetpack Compose 1.13 bổ sung tính năng Theo dõi quá trình kết hợp lại, sử dụng siêu dữ liệu tuỳ chỉnh để cho biết lý do xảy ra quá trình kết hợp lại. Dấu vết ghi lại các giá trị trạng thái đã sửa đổi và ngăn xếp lệnh gọi chính xác đã kích hoạt các giá trị đó.
  • Các công cụ bản dựng Android sử dụng Tracing 2.0 để phát hiện điểm tắc nghẽn và tăng tốc bản dựng. Tính năng tối ưu hoá dựa trên hoạt động theo dõi giúp tăng tốc độ rút gọn mã R8 thêm 25% và giảm 15% thời gian gửi sau của Metalava.

Gemini Notebook

Chủ đề

  • theo dõi
  • android-performance
  • perfetto
  • truyền bối cảnh
  • jetpack-compose
  • coroutine
  • lập hồ sơ

Bản chép lời

Giới thiệu và quá trình phát triển của tính năng theo dõi Android

  • Rahul Ravikumar

    Chào mọi người!

  • Tôi tên là Rahul.

  • Tôi là kỹ sư thuộc nhóm Trải nghiệm nhà phát triển Android.

  • Tôi tập trung vào hiệu suất của ứng dụng và thư viện, và bài nói chuyện này là về việc theo dõi.

  • Trước khi tìm hiểu sâu về hoạt động theo dõi, tôi muốn thiết lập một số từ vựng chung để bạn có thể theo dõi.

  • Vì vậy, bất cứ khi nào tôi đề cập đến việc theo dõi trong bài nói chuyện này, tôi đang nói về việc theo dõi rõ ràng, đo lường mã theo cách thủ công để bạn biết ứng dụng đang làm gì.

  • Tôi không nói về trình phân tích tự động mà tôi sẽ đề cập sau.

  • Vì vậy, theo dõi thủ công là nghệ thuật để lại dấu vết trong toàn bộ mã nguồn, nhờ đó, bạn có thể suy luận về sau khi gặp vấn đề về hiệu suất hoặc bất kỳ vấn đề nào khác.

  • Thông thường, cách này hiệu quả hơn nhiều so với việc dùng nhật ký.

  • Tôi không biết bạn đã từng thấy kết quả Logcat của một ứng dụng lớn hay chưa.

  • Thông thường, đó là một khối văn bản khổng lồ mà bạn không thể dễ dàng suy luận.

  • Mặt khác, việc theo dõi sẽ dễ dàng hơn nhiều.

  • Dễ hình dung hơn.

  • Bạn có thời lượng.

  • Bạn có một số cấu trúc.

  • Bạn có một số công cụ, vì vậy, bạn thậm chí có thể phân tích và xem chính xác những gì đã xảy ra khi nào.

  • Và như vậy thì tốt hơn nhiều.

  • Và bạn có thể, bạn biết đấy, hướng các tác nhân của mình vào một dấu vết và nói, đặt câu hỏi.

  • Bạn có thể sử dụng các công cụ như Trace Processor, điểm chuẩn và thậm chí chuyển đổi dấu vết thành chỉ số.

  • Vì vậy, đây là tất cả những việc bạn có thể làm với dấu vết mà khó thực hiện được bằng Logcat.

  • Mặt khác, việc lập hồ sơ (ở đây tôi nói đến việc lập hồ sơ tự động) thường là để ứng phó với một vấn đề mà bạn biết.

  • Ví dụ: bạn có thể nhận thấy ứng dụng của mình khởi động chậm hoặc có hiện tượng giật và bạn muốn thu hẹp phạm vi cũng như tìm hiểu xem điều gì đang xảy ra.

  • Và tại thời điểm này, nếu bạn chưa có dấu vết thủ công để trợ giúp, thì đây là lúc bạn cần đến tính năng lập hồ sơ tự động.

  • Bạn có thể sử dụng một công cụ như theo dõi dấu vết phương thức ART, trong đó bạn đo lường mọi điểm vào và ra của phương thức.

  • Hoặc bạn có thể xem xét việc lấy mẫu ngăn xếp bằng các công cụ như Simpleperf.

  • Vì vậy, thay vì sử dụng mọi mục nhập và thoát phương thức và có mức hao tổn cao, bạn hãy xem các mẫu ngăn xếp lệnh gọi và xem những gì có trên đường dẫn nóng của ứng dụng.

  • Đây là năm 2026, RAM là vấn đề ở mọi nơi, vì vậy, bạn có thể đang xem xét bộ nhớ, rò rỉ bộ nhớ và hồ sơ ảnh chụp nhanh của vùng nhớ khối xếp.

  • Vì vậy, đây là một trình phân tích tài nguyên khác mà bạn có thể tận dụng.

  • Bạn có thể xem đường dẫn giữ lại vùng nhớ khối xếp.

  • Bạn có thể xem mã nào đang phân bổ bộ nhớ và đó là tất cả các loại phân tích mà bạn có thể thực hiện bằng công cụ này.

  • Phải.

  • Giờ thì chúng ta đã có từ vựng đó.

  • Bây giờ, hãy cùng tìm hiểu lý do chúng tôi xây dựng một thư viện mới.

  • API theo dõi, API 1.0 ban đầu được giới thiệu trong Android Jelly Bean.

  • Đó là vào năm 2012.

  • Đó là hơn 14 năm trước.

  • Kể từ đó, các API không phát triển nhiều như bạn nghĩ.

  • Chúng tôi đã thêm một vài tiện ích.

  • Các sự kiện và bộ đếm không đồng bộ đã được thêm vào Android 10.

  • Đó là vào năm 2019.

  • Và thế là xong.

  • API này không có thay đổi nào kể từ đó.

  • Tuy nhiên, hệ sinh thái này đã phát triển khá nhiều.

  • Vì vậy, tính năng theo dõi nền tảng đã được cải thiện đáng kể, nhưng không có tính năng nào trong số đó được bổ sung vào API cho bạn.

  • Đó là điều chúng tôi muốn thay đổi.

  • Một vấn đề lớn khác là tính năng truy vết có phần phụ trợ truy vết cố định trong phiên bản 1.0, nghĩa là bất cứ khi nào bạn thêm một phần truy vết trong mã, phần đó luôn kết thúc trong vùng đệm truy vết hệ thống.

  • Atrace là hệ thống con theo dõi của Android sở hữu dữ liệu đó.

  • Vì vậy, nếu muốn lấy dấu vết và chuyển đổi thành dữ liệu đo từ xa tại hiện trường, thì việc này thực sự rất khó, vì giờ đây, bạn phải nhận ra cách atrace lưu trữ dữ liệu đó.

  • Đây là một định dạng có tài liệu chi tiết nhưng có thể khó sử dụng.

  • Vì vậy, chúng tôi muốn khắc phục vấn đề này.

  • Vì vậy, chúng tôi muốn cung cấp cho bạn các công cụ để đưa dữ liệu theo dõi đến bất cứ nơi nào bạn muốn.

  • Chúng tôi cũng có một quy định hạn chế trong tính năng theo dõi 1.0, trong đó nhãn bị giới hạn ở 128 ký tự.

  • Giờ đây, đó có thể là một hạn chế hợp lý vào năm 2012, nhưng vào năm 2026, điều đó có vẻ khá kỳ lạ.

  • Điều tồi tệ hơn là bạn cũng không thể thêm bất kỳ siêu dữ liệu bổ sung nào vào dấu vết của mình, đây là một vấn đề khác.

  • Và sau đó là chi phí chung.

  • Vì vậy, bất cứ khi nào theo dõi ứng dụng, bạn phải cực kỳ lưu ý đến mức hao tổn, vì hành động quan sát hệ thống không được làm thay đổi những gì hệ thống đang thực hiện trong nền.

  • Vì vậy, nếu có một tình huống tương tranh mà bạn muốn tái hiện, thì việc theo dõi sẽ không làm cho tình huống tương tranh đó biến mất.

  • Vì vậy, bạn cần đảm bảo giữ nguyên tất cả ngữ nghĩa mà bạn đang thực hiện.

  • Tracing 1.0 có mức hao tổn tương đối cao và chúng tôi muốn cải thiện điều đó.

  • Được rồi.

  • Tôi đã nói rất nhiều về các vấn đề.

  • Bây giờ, hãy xem xét một số vấn đề một cách trực quan và xem chúng kết hợp với nhau như thế nào trong thực tế.

  • Vì vậy, đây là dấu vết Perfetto.

  • Đó là mã cho ứng dụng com.example.app.

  • Chúng ta có một luồng chính và một lát cắt duy nhất có tên là initializeComponent.

  • Giờ thì chúng ta muốn có thêm thông tin chi tiết.

  • Vì vậy, hãy thêm một chi tiết.

  • Giờ thì chúng ta biết rằng initializeComponent gọi performInitialize.

  • Chúng ta vẫn chưa biết thành phần nào đang được khởi chạy.

  • Giờ đây, công cụ duy nhất trong bộ công cụ có Tracing 1 là tên, vì vậy, chúng ta sẽ thêm công cụ đó vào tên của lát cắt.

  • Bây giờ, hãy nhớ rằng tên của lát cắt theo dõi bị giới hạn ở 128 ký tự, vì vậy, tên này sẽ bị cắt bớt trong dấu vết của bạn.

  • Giờ đây, để thêm phần khó khăn, chi phí cắt bớt cũng là chi phí bạn phải chịu khi theo dõi.

  • Vì vậy, bạn sẽ gặp phải vấn đề này khá nhanh.

  • Vì vậy, đây là vấn đề chúng tôi thực sự muốn khắc phục.

  • Bạn đã thấy dấu vết Perfetto và thấy hình ảnh trực quan của dấu vết đó.

  • Bây giờ, hãy xem xét từ góc độ viết mã để phát ra các phần theo dõi đó.

  • Một lần nữa, chúng ta có API initializeComponent.

  • Hàm này nhận một ngữ cảnh và một tên, gọi performInitialize.

  • Đến giờ thì mọi thứ vẫn ổn!

  • Bây giờ, chúng ta muốn thêm một dấu vết bằng API Tracing 1.0, vì vậy, bạn có thể thực hiện việc này bằng cách nhập androidx.tracing.trace và sử dụng khối dấu vết.

  • Bạn đặt tên cho nhóm.

  • Đây là performInitialize.

  • Nhưng như chúng ta muốn làm trước đây, chúng ta muốn biết tên của thành phần, vì vậy, chúng ta sẽ sử dụng phép nội suy chuỗi của Kotlin và thêm tên.

  • Và đây là vấn đề đầu tiên.

  • Vì vậy, chúng ta có giới hạn 128 ký tự ở đây.

Tổng quan về tính năng theo dõi 2.0 và hiệu suất

  • Rahul Ravikumar

    Chúng ta có thể khắc phục vấn đề này bằng Tracing 2.

  • Thay vì sử dụng androidx.tracing.trace, bạn sử dụng Tracer.

  • API này trông rất giống.

  • Bạn có Tracer.global.trace thay vì trace.

  • Bạn có một danh mục.

  • Chúng ta sẽ nói chi tiết hơn về danh mục sau, nhưng về cơ bản, bạn có một tên.

  • Sau đó, bạn vẫn có khối mã mà bạn đang cố gắng theo dõi.

  • Và giờ đây, bạn không còn bị giới hạn 128 ký tự nữa.

  • Tôi đã nói về mức hao tổn khi theo dõi.

  • Tôi muốn đi sâu hơn một chút và xem cách chúng tôi cải thiện điều này.

  • Trên Pixel 6a chạy Android 16, việc chèn một phần bắt đầu và một phần kết thúc mất khoảng 1.200 nano giây.

  • Bây giờ, con số đó có vẻ không nhiều, nhưng nếu bạn có mã thực sự nóng đang thêm nhiều phần theo dõi, thì đó là thời gian mà ứng dụng của bạn có thể dành cho việc kết xuất khung hình.

  • Vì vậy, chúng tôi không muốn làm mất thời gian của bạn.

  • Chúng tôi thực sự đã cải thiện khá nhiều nhờ SDK AndroidX Perfetto.

  • Đây là một SDK thử nghiệm mà chúng tôi đã giới thiệu cho tính năng Theo dõi thành phần và thực sự đã cải thiện được rất nhiều tình hình.

  • Chúng tôi thực sự không muốn bạn cung cấp SDK này vì đây là SDK Perfetto C có liên kết JNI và SDK C có dung lượng khoảng 4 MB trong APK của bạn.

  • Đây không phải là điều chúng tôi mong muốn bạn gặp phải.

  • Giờ đây, với Tracing 2, chúng ta có một thư viện được xây dựng có mục đích.

  • Chúng tôi đã cải thiện đáng kể mức hao tổn tài nguyên và giờ đây, bạn sẽ rất vui khi biết rằng Tracing 2 nhanh hơn gấp 7 lần, tức là bạn có nhiều thời gian hơn để làm những việc mình muốn và cơ sở hạ tầng sẽ tốn ít thời gian hơn.

  • Một công cụ như thế này có thể giúp bạn làm được những gì mà trước đây bạn không thể làm?

  • Vì vậy, một trong những ví dụ là tính năng theo dõi thời gian chạy của Metro.

  • Metro, dành cho những ai chưa biết, là một thư viện chèn phần phụ thuộc.

  • Thư viện này hỗ trợ Kotlin Multiplatform.

  • Thư viện này sử dụng một trình bổ trợ trình biên dịch Kotlin.

  • Nó tạo ra tất cả mã cho tính năng chèn phần phụ thuộc và tích hợp với Tracing 2.0 để giúp bạn dễ dàng hình dung chi phí của từng @Inject trong mã của mình.

  • Sau đây là một ví dụ.

  • Giả sử chúng ta có một biểu đồ ứng dụng.

  • Trong Metro, mọi thứ đều thuộc phạm vi của một biểu đồ, vì vậy, chúng ta có một trường duy nhất là AnalyticsTracker.

  • Chúng ta cũng có một liên kết cung cấp.

  • Chúng ta có một ứng dụng mạng với OkHttpClient là impl, sau đó việc triển khai AnalyticsTracker đến từ AnalyticsTrackerImpl và chúng ta cần một thực thể của ứng dụng mạng mà biểu đồ biết cách cung cấp.

  • Vì vậy, về cơ bản, trình bổ trợ trình biên dịch sẽ tạo mã như sau.

  • Vì vậy, nếu bạn có AnalyticsTracker, phương thức getter sẽ tạo một thực thể của ứng dụng mạng và truyền thực thể đó đến hàm khởi tạo của AnalyticsTrackerImpl. Điều này là tốt.

  • Nhưng giờ đây, khi bật tính năng theo dõi, bạn có thể thấy chi phí tạo không chỉ của trình triển khai trình theo dõi mà còn của mọi phần phụ thuộc mà trình theo dõi cần.

  • Giờ đây, chúng tôi cho phép bạn xem không chỉ chi phí tạo thực thể cho loại mà còn cả tất cả các phần phụ thuộc của loại đó trong một dấu vết, và điều này thực sự rất hữu ích.

  • Vì vậy, khi sử dụng thông tin này, Wise (một ứng dụng tài chính) đã cải thiện thời gian khởi động ứng dụng thêm 45%.

  • Họ có thể trì hoãn những việc không cần thiết phải khởi chạy trong quá trình khởi động ứng dụng, đồng thời xác định một số mục có phạm vi không chính xác, trong đó những mục lẽ ra phải là singleton nhưng thực tế lại không phải là singleton, và đây là những điều bạn có thể thấy và dễ dàng khắc phục trong mã của mình.

  • Nếu bạn nhìn vào các chỉ số khởi động của họ, bạn sẽ thấy rất ấn tượng.

  • Họ không chỉ cải thiện thời gian hiển thị khung hình ban đầu mà còn cải thiện thời gian hiển thị đầy đủ.

  • Một đối tác khác là BandLab cũng sử dụng thông tin tương tự về CI.

  • Vì vậy, bạn không chỉ biết chi phí của từng liên kết @Inject mà còn biết luồng nào đã phát sinh chi phí đó.

  • Vì vậy, khi biết điều đó, bạn có thể tìm thấy tất cả các liên kết @Inject tốn kém trên luồng chính và trong CI, bạn có thể chạy một truy vấn để nói, này, hãy cho tôi danh sách tất cả các liên kết tốn kém và sau đó quy nó trở lại thay đổi đã giới thiệu chúng và sau đó bạn có thể đổ lỗi cho thay đổi thực tế và cố gắng khắc phục nó.

Truyền ngữ cảnh, coroutine và Jetpack Compose

  • Rahul Ravikumar

    Được rồi.

  • Cho đến nay, tôi đã nói về tất cả những điều chúng tôi đã cải thiện so với Tracing 1, nhưng bây giờ tôi sẽ nói về các tính năng mới trong Tracing 2 mà trước đây chưa từng có.

  • Vì vậy, một trong những tính năng cốt lõi được gọi là Truyền ngữ cảnh và vì các ứng dụng hiện đại ngày càng phức tạp, nên khối lượng công việc chạy trên nhiều luồng, nhiều coroutine và nhiều quy trình.

  • Bạn muốn có thể hình dung cách một yêu cầu truyền giữa từng luồng và quy trình.

  • Và đó là điều mà tính năng Truyền ngữ cảnh có thể làm được.

  • Vì vậy, nếu tôi chỉ cung cấp cho bạn danh sách các lát cắt mà không cho bạn biết nguyên nhân gây ra vấn đề, thì các lát cắt đó tuy hữu ích nhưng không dễ để suy luận.

  • Nhưng nếu tôi nói với bạn rằng đây là chuỗi sự kiện nhân quả đã xảy ra, thì bạn sẽ dễ dàng tìm ra điều gì đang xảy ra hơn, sau đó liên kết chi phí của từng lát cắt.

  • Đây là những gì mà tính năng Truyền ngữ cảnh cung cấp.

  • Bạn sẽ rất vui khi biết rằng tính năng Truyền ngữ cảnh được hỗ trợ đầy đủ và sau đây là một ví dụ thực tế.

  • Nhìn lại dấu vết Perfetto com.example.app, giả sử performInitialize mất quá nhiều thời gian và bạn muốn trì hoãn chi phí hoặc chuyển chi phí đó sang một luồng khác.

  • Bạn hoàn toàn có thể làm được.

  • Giả sử tôi đã chuyển performInitialize sang một chuỗi tin nhắn khác.

  • Giờ đây, bạn đã mất đi tính chất phân cấp của lát cắt.

  • Vì vậy, giờ đây, bạn không thể biết rằng initializeComponent là nguyên nhân khiến performInitialize xảy ra, trừ phi bạn xem mã.

  • Trong một thế giới lý tưởng, bạn sẽ muốn có một mũi tên cho bạn biết rằng A gây ra B và sau đó bạn có thể suy luận về chuỗi sự kiện nhân quả.

  • Bạn thậm chí có thể đã thấy một dấu vết hệ thống mà chúng tôi thực sự làm điều này cho các giao dịch liên kết trên nền tảng.

  • Vì vậy, tại đây, bạn có thể thấy điểm chuẩn Compose thực hiện giao dịch liên kết với system_server để chuẩn bị khung.

  • Vì vậy, đây là những việc luôn có thể thực hiện được từ góc độ theo dõi nền tảng, nhưng chúng tôi chưa từng cung cấp công cụ cho nhà phát triển ứng dụng.

  • Vì vậy, giờ đây, tính năng Truyền ngữ cảnh coroutine Kotlin được hỗ trợ đầy đủ trong Tracing 2.0.

  • Bạn không phải thực hiện bất kỳ điều gì.

  • Bạn chỉ cần lấy thiết bị ra khỏi hộp.

  • Vì vậy, ở đây chúng ta có một lệnh gọi phương thức tạm ngưng duy nhất tên là fetchProfile.

  • Hàm này gọi client.get (một phương thức tạm ngưng khác) rồi chuyển sang Dispatchers.Default để giải tuần tự hoá phản hồi.

  • Mã coroutine rất rập khuôn, có thể bạn đã xử lý hằng ngày.

  • Bây giờ, giả sử chúng ta muốn theo dõi điều này.

  • Chúng ta thêm một phần theo dõi.

  • Vì vậy, ở đây, thay vì theo dõi, chúng ta sẽ sử dụng API traceCoroutine vì đây là một khối mã tạm ngưng, sau đó, đối với lệnh gọi bên trong trước khi chuyển ngữ cảnh sang Dispatchers.Default, chúng ta sẽ thêm một phần theo dõi bên trong khác bằng cách sử dụng cùng một API traceCoroutine.

  • Bây giờ, trong dấu vết, bạn sẽ thấy nội dung như sau.

  • Bạn sẽ thấy công việc của chúng ta bắt đầu trong worker-1, sau đó, công việc này thực sự chuyển đổi ngữ cảnh và chuyển sang worker-3. Bạn sẽ thấy các mũi tên hữu ích trong dấu vết chỉ ra điều đó.

  • Không chỉ vậy, nếu phóng to hình vuông ở giữa, bạn có thể thấy quá trình giải tuần tự thực sự bắt đầu trong worker-1 và lệnh gọi đến withContext là nguyên nhân khiến quá trình chuyển đổi ngữ cảnh xảy ra, sau đó, quá trình này tiếp tục trên worker-3 và sau đó, fetchProfile tiếp tục trên worker-3 vì cơ chế coroutine muốn giảm thiểu số lần chuyển đổi ngữ cảnh xảy ra.

  • Bạn có thể thấy chính xác thời điểm một coroutine bị tạm ngưng, thời điểm coroutine đó tiếp tục và bạn có thể thấy toàn bộ chuỗi sự kiện nhân quả.

  • Một điều khác mà Tracing 2 có thể làm là khả năng thêm siêu dữ liệu tuỳ chỉnh vào dấu vết.

  • Vì vậy, trước đây, công cụ duy nhất bạn có là tên của phần theo dõi, nhưng giờ đây, bạn có thể thêm một khối siêu dữ liệu cho phép bạn thêm bao nhiêu mục siêu dữ liệu tuỳ thích.

  • Bạn có thể thêm thông tin đó vào dấu vết.

  • Khi xem dấu vết thực tế, bạn có thể thấy tất cả siêu dữ liệu mà bạn đã thêm đều có trong dấu vết để tất cả công cụ sử dụng.

  • Giờ đây, bạn có thể nhúng tất cả những gì bạn biết, tất cả thông tin bổ sung mà bạn luôn muốn.

  • Tôi đã giới thiệu cho bạn một loạt tính năng trừu tượng mà tính năng theo dõi có thể thực hiện, nhưng điểm nổi bật nhất là một tính năng mới trong Jetpack Compose có tên là Theo dõi quá trình kết hợp lại.

  • Tính năng này được cung cấp trong Compose 1.13 nên bạn có thể sử dụng ngay hôm nay.

  • Sau đây là một ví dụ.

  • Đây là tính năng Theo dõi quá trình hợp thành mà có lẽ các bạn đều đã quen thuộc.

  • Tính năng này đã có từ những ngày đầu của Compose.

  • Nó cho bạn biết chi phí tương đối của một thành phần kết hợp và tất cả các thành phần phụ của nó, cũng như chi phí của từng thành phần trên luồng mà chúng đang xảy ra.

  • Vì vậy, điều này khá đơn giản.

  • Tuy nhiên, chỉ số này không cho bạn biết bối cảnh.

  • Báo cáo này không cho bạn biết lý do xảy ra vấn đề.

  • Công cụ này không cho bạn biết lý do các thành phần kết hợp lại.

  • Vì vậy, nếu tôi thu nhỏ, thì đó là khu vực quan tâm mà chúng ta vừa xem trong trang trình bày trước.

  • Và ở bên trái, bạn có thể thấy một sự kiện nhập dữ liệu gửi đi đã xảy ra.

  • Có thể đó là điều đã thay đổi một số trạng thái và có thể đó là điều đã gây ra quá trình tái cấu trúc.

  • Nhưng điều này chưa rõ ràng.

  • Bạn không biết chắc.

  • Tuy nhiên, với tính năng theo dõi quá trình kết hợp lại, giờ đây, chúng tôi thêm siêu dữ liệu tuỳ chỉnh vào dấu vết để cho bạn biết chính xác lý do xảy ra quá trình kết hợp lại.

  • Không chỉ vậy, giờ đây bạn có thể dễ dàng nhận biết rằng các sự kiện này có liên quan đến nhau.

  • Sau đó, nếu bạn nhấp vào các mục này, thì bạn sẽ biết chính xác trạng thái nào đã thay đổi.

  • Công cụ này sẽ cho bạn biết rằng đó là một trạng thái, một giá trị trạng thái có thể thay đổi được chuyển sang false, trong khi trước đó là true.

  • Không chỉ vậy, chúng tôi thậm chí còn cho bạn biết chính xác ngăn xếp lệnh gọi đã gây ra lỗi đó.

  • Vì vậy, tại đây, bạn có thể thấy rằng AbstractClickableNode.performClick đã xảy ra, khiến trạng thái có thể thay đổi thay đổi, dẫn đến việc kết hợp lại.

  • Vì vậy, đây là một tính năng thực sự rất thú vị mà trước đây chúng tôi chưa thể thực hiện được.

  • Những công cụ này cũng có sẵn, xin lỗi, tất cả chức năng này cũng có trong công cụ Android Performance Analyzer mới, nếu bạn muốn dùng thử một số công cụ dựa trên tác nhân.

  • Và đúng vậy.

  • Như vậy, tôi đã cho bạn biết rằng việc truyền ngữ cảnh được hỗ trợ đầy đủ cho coroutine Kotlin.

  • Nhưng còn mã sử dụng kết hợp coroutine và mã không phải coroutine thì sao? Hoặc có thể bạn đang tự chạy các luồng, hoặc có thể bạn có một số mã cũ sử dụng lệnh gọi lại vì lý do nào đó?

  • Được rồi.

  • Vì vậy, tin vui là bạn vẫn có thể nhận được tất cả, bạn biết đấy, sự tốt đẹp của việc truyền tải ngữ cảnh ở đó.

  • Giả sử chúng ta có một API brownfield sử dụng lệnh gọi lại và trình thực thi.

  • Quá trình triển khai sử dụng executor.execute, gọi runCatching (là thông tin triển khai), nhận phản hồi, sau đó thông báo cho lệnh gọi lại về phản hồi.

  • Được rồi.

  • Bây giờ, nếu muốn theo dõi, tôi sẽ thực hiện Tracer.global.trace, gắn nhãn rồi bao quanh khối.

  • Nhưng vì tôi cũng muốn duy trì chuỗi sự kiện nhân quả, nên tôi sẽ chấp nhận một loại mới, không bắt buộc, được gọi là mã thông báo truyền.

  • Và tôi sẽ chỉ truyền mã thông báo này đến API theo dõi bên trong.

  • Vì vậy, bất kỳ ai gọi phương thức này đều có thể cung cấp cho tôi mã thông báo truyền, nhờ đó tôi có thể duy trì chuỗi sự kiện nhân quả xảy ra.

  • Được rồi.

  • Giờ đây, phương thức gọi API đó có thể là một API tạm ngưng.

  • Vì vậy, ở đây tôi có một hàm tạm ngưng profileRequest, hàm này sẽ gọi API sử dụng lệnh gọi lại và trình thực thi.

  • Ở đây, vì tôi đang kết nối hai thế giới, nơi tôi kết nối thế giới tạm ngưng với thế giới gọi lại, nên tôi sẽ sử dụng trình tạo suspendCoroutine.

  • Tôi sẽ nhận được một lệnh tiếp tục, sau đó tôi sẽ gọi API, rồi tiếp tục lệnh tiếp tục trong lệnh gọi lại sau khi nhận được phản hồi.

  • Sau đó, trình thực thi mà tôi đang xác định được gọi là profileRequestContext.

  • Đây là trình thực thi đơn luồng.

  • Và vì traceCoroutine nhúng mã thông báo truyền vào trong ngữ cảnh coroutine, nên tôi có thể lấy mã thông báo truyền từ bên trong ngữ cảnh coroutine, sau đó truyền mã thông báo đó làm dữ liệu đầu vào cho API brownfield, nhờ đó tôi vẫn có thể duy trì chuỗi sự kiện nhân quả.

  • Và trong dấu vết, điều này rất rõ ràng.

  • Vì vậy, ở đây bạn có một thứ bắt đầu trong worker-3 đã chuyển sang trình thực thi đơn luồng, sau khi chúng ta thực hiện chuyển đổi ngữ cảnh, rồi tiếp tục trở lại trên worker.

  • Vì vậy, nó khá gọn gàng.

Theo dõi toàn cầu và khởi chạy tuỳ chỉnh

  • Rahul Ravikumar

    Được rồi.

  • Một việc khác mà bạn có thể làm là tuỳ chỉnh Tracer.global.

  • Cho đến nay, API Tracing 2.0 cung cấp cho bạn các giá trị mặc định thực sự phù hợp cho Tracer.global, nhưng nếu muốn tuỳ chỉnh, bạn vẫn có thể thực hiện.

  • Ngoài ra, Tracing 2.0 hỗ trợ JVM và Android vì chúng tôi muốn tính năng theo dõi được hỗ trợ đầy đủ cho cả thư viện Java và Android.

  • Vì vậy, ba bước để khởi tạo là: bạn tạo một bồn theo dõi.

  • Bộ lưu vết là nơi lưu trữ các gói dấu vết một cách hiệu quả.

  • Định dạng này xác định định dạng dây và vị trí lưu trữ của dấu vết đầu ra.

  • Chúng tôi cung cấp cho bạn một phương thức triển khai có sẵn cho một bồn lưu vết hỗ trợ định dạng Perfetto, nhưng bạn cũng có thể tự viết bồn lưu vết của riêng mình.

  • Điều này quay lại việc cố gắng giải quyết vấn đề rằng atrace là phần phụ trợ duy nhất có sẵn trong Tracing 1.

  • Chúng tôi muốn khắc phục vấn đề đó.

  • Vì vậy, giờ đây, nếu muốn viết một bồn lưu trữ OpenTelemetry hoặc muốn mang bồn lưu trữ của riêng mình, bạn hoàn toàn có thể làm được.

  • Bạn có thể chọn ghi đè chế độ đó.

  • Sau khi có bồn lưu trữ, bạn có thể tạo trình điều khiển dấu vết và cung cấp bồn lưu trữ cho trình điều khiển đó.

  • Sau đó, bạn cũng có thể chọn danh mục mà bạn muốn bật.

  • Vì vậy, đây là phần mà các danh mục thực sự toả sáng.

  • Vì vậy, bạn có thể quyết định rằng trong bản phát hành, một số danh mục quan trọng hơn các danh mục khác khi so sánh với bản gỡ lỗi và bạn có thể nói rằng đó là những danh mục mà bạn muốn bật.

  • Hoặc bạn cũng có thể nói: "Tôi muốn bật tất cả danh mục bất kể chúng đến từ đâu".

  • Điều đó hoàn toàn không có vấn đề gì.

  • Bạn cũng có thể thêm các thuộc tính tùy chỉnh mang tính toàn cục vào dấu vết của mình.

  • Vì vậy, bạn có thể nhúng những thông tin như phiên bản bản dựng, phiên bản ứng dụng hoặc bất kỳ siêu dữ liệu nào khác mà bạn muốn đính kèm vào dấu vết bằng API addAttribute.

  • Cuối cùng, bạn có thể đăng ký một trình theo dõi bằng cách gọi setGlobalTracer.

  • Đây là yếu tố liên kết Tracer.global với phiên bản trình theo dõi này và sau đó, đây là yếu tố có sẵn cho quy trình.

  • Trên Android, vì các quy trình có vòng đời được xác định rõ ràng hơn nhiều, nên bạn chỉ cần triển khai AbstractTraceDriver.Factory, ghi đè phương thức create và thực hiện chính xác những gì bạn đã làm trước đó.

  • Vì vậy, bạn tạo bồn rửa, sau đó bạn tạo trình điều khiển.

  • Bạn không cần phải đăng ký tại đây.

  • Chúng tôi sẽ đăng ký cho bạn vì chúng tôi biết rằng đây là việc xảy ra trước tiên.

  • Được rồi.

  • Được rồi, tôi muốn nhắc lại rằng tính năng theo dõi đã sẵn sàng cho nhiều nền tảng.

  • Đây là một lựa chọn có chủ đích vì chúng tôi biết Compose đang chạy trên nhiều nền tảng và chúng tôi muốn giúp bạn có thể theo dõi ở nhiều nơi nhất có thể.

  • Hiện tại, chúng tôi hỗ trợ Android và JVM, nhưng cách triển khai của chúng tôi hoàn toàn cho phép chúng tôi hỗ trợ các nền tảng trong tương lai nếu muốn.

Tối ưu hoá công cụ xây dựng (R8 và metalava)

  • Rahul Ravikumar

    Bạn sẽ rất vui khi biết rằng R8, công cụ dùng để rút gọn và tối ưu hoá mã nguồn trên Android, sử dụng Tracing 2.0.

  • Bằng cách tận dụng các dấu vết, chúng tôi đã tối ưu hoá R8 để thực sự thu nhỏ và tối ưu hoá mã của bạn nhanh hơn 25%.

  • Điều này có nghĩa là thời gian xây dựng ngắn hơn và bạn biết đấy, thời gian chờ đợi để tối ưu hoá cũng ngắn hơn.

  • Tôi sẽ không trình bày mọi hoạt động tối ưu hoá mà chúng tôi đã thực hiện trong R8, nhưng tôi chỉ muốn trình bày một hoạt động để bạn hiểu rõ hơn về những gì mà tính năng theo dõi có thể làm được.

  • Vì vậy, về cơ bản, R8 là một trình chuyển đổi từ nguồn sang nguồn.

  • Vì vậy, bạn sẽ nhận được một loạt tệp JAR, sau đó thu nhỏ và tối ưu hoá chúng, rồi tạo ra một tập hợp tệp JAR khác mà bạn có thể dex.

  • Vì vậy, tại đây, AGP bắt đầu bằng cách đọc các tệp JAR, không chỉ là ứng dụng của bạn mà còn tất cả các phần phụ thuộc của ứng dụng.

  • Vì vậy, công cụ này sử dụng trình thực thi Gradle để bắt đầu đọc tất cả các tệp JAR đầu vào.

  • Bạn có rất nhiều tệp JAR mà bạn muốn đọc.

  • Sau đó, khi đọc xong các tệp JAR, R8 sẽ bắt đầu phân tích cú pháp mã và xây dựng một mô hình đại diện cho mã của bạn cũng như tất cả các điểm truy cập của mã, nhờ đó, R8 có thể bắt đầu tối ưu hoá và rút gọn mã.

  • Bây giờ, đó là lát cắt, chờ đợi, đại diện cho thời điểm R8 thực sự sẵn sàng để bắt đầu quá trình tối ưu hoá, phải không?

  • Bạn có thể thấy một số khoảng trống ở giữa và có thể thắc mắc tại sao chúng ta lại có thời gian của CPU không được sử dụng như vậy.

  • Lý do là vì cách hoạt động của API cũ là chúng ta đợi tất cả trình chạy đọc xong tất cả tệp trước khi phân tích cú pháp bất cứ thứ gì.

  • Rõ ràng là chúng tôi có thể khắc phục vấn đề này và chúng tôi đã làm đúng như vậy.

  • Vì vậy, trong AGP 9, bạn có thể thấy rằng ngay khi đọc xong các tệp JAR đầu vào, chúng tôi sẽ bắt đầu xử lý ngay lập tức.

  • Và chúng tôi không phải đợi từng trình thực thi đọc xong dữ liệu đầu vào.

  • Điều này thực sự đã giúp giảm đáng kể thời gian chờ để đọc tất cả dữ liệu đầu vào.

  • Vì vậy, đây là một hoạt động tối ưu hoá rõ ràng mà bạn có thể thấy rất rõ khi xem dấu vết, nhưng không rõ ràng khi bạn chỉ xem mã nguồn để biết những gì đáng tối ưu hoá.

  • Một công cụ khác mà chúng tôi sử dụng hằng ngày với tư cách là kỹ sư AndroidX là Metalava.

  • Metalava là một công cụ mà chúng tôi sử dụng để đảm bảo rằng mọi API mà chúng tôi cung cấp đều có khả năng tương thích API như bạn mong đợi và người dùng bên ngoài có thể sử dụng đầy đủ mà không gặp phải bất kỳ vấn đề nào mà chúng tôi muốn tránh.

  • Đây là một công cụ chạy trên mọi API công khai và API bị hạn chế, vì vậy, bất cứ khi nào chúng tôi lưu ở đây thực sự là thời gian mà chúng tôi nhận lại với tư cách là kỹ sư AndroidX.

  • Metalava cũng sử dụng tính năng theo dõi để cải thiện thời gian sau khi gửi lên đến 15%.

  • Và đi sâu vào dấu vết để giải thích những gì đã xảy ra, vì vậy Metalava chạy hai loại kiểm tra: loại thứ nhất là kiểm tra các API bị hạn chế và loại thứ hai là kiểm tra các API công khai.

  • Nhưng có thể bạn đã nhận ra một quy luật.

  • Chúng ta đang thực hiện một loạt các bước thiết lập để chạy các bước kiểm tra đó hai lần.

  • Vì vậy, điều xảy ra là tất cả dữ liệu đầu vào của chương trình đều được đọc, chúng tôi xây dựng PSI, tức là giao diện cấu trúc chương trình, hoặc bạn biết đấy, chính xác hình dạng mã của bạn có thể được suy luận.

  • Sau đó, chúng ta xây dựng AST trước khi bắt đầu áp dụng tất cả các quy trình kiểm tra tìm lỗi mã nguồn mà chúng ta muốn.

  • Và chúng ta đang thực hiện công việc tốn kém này hai lần.

  • Chúng tôi đã khắc phục vấn đề này như thế nào?

  • Chúng tôi chỉ làm một lần.

  • Vì vậy, thay vì thực hiện tất cả công việc thiết lập đó hai lần, chúng tôi chỉ thực hiện một lần và sử dụng lại các dữ liệu đầu vào đó cho cả quy trình kiểm tra hạn chế cũng như quy trình kiểm tra công khai, nhờ đó tiết kiệm được tất cả những chi phí đó.

  • Vâng.

  • Được rồi.

Các phương pháp hay nhất về theo dõi và đo điểm chuẩn

  • Rahul Ravikumar

    Giờ đây, khi tôi đã giới thiệu sơ qua về những khả năng của Tracing 2.0, bạn cần biết rằng mặc dù việc theo dõi là quan trọng, nhưng việc biết nên theo dõi điều gì cũng quan trọng không kém.

  • Bạn không nên rải dấu vết ở khắp mọi nơi.

  • Bạn nên cân nhắc kỹ lưỡng khi chọn nội dung và tôi sẽ cố gắng đưa ra một số nguyên tắc về những nội dung đáng để truy vết.

  • Vì vậy, việc đầu tiên bạn nên làm là tìm những khoảng trống không giải thích được trong dấu vết.

  • Vì vậy, bất cứ khi nào bạn đang thực hiện khi các luồng của bạn ở trạng thái đang chạy hoặc có thể chạy và bạn thấy các khoảng trống rõ ràng trong dấu vết hệ thống của mình và bạn không thể lý giải về chúng, đồng thời bạn không biết ứng dụng của mình đang làm gì, thì điều đó có thể đáng để điều tra.

  • Tại thời điểm đó, tôi có thể lấy dấu vết phương thức được phủ lên dấu vết hệ thống và xem những gì đáng để theo dõi, đồng thời tôi có thể thêm các phần dấu vết có liên quan đó.

  • Bạn cũng nên ưu tiên theo hành trình của người dùng.

  • Vì vậy, bạn nên xem xét quá trình khởi động ứng dụng.

  • Bạn muốn xem hành trình cốt lõi của người dùng ứng dụng và muốn tìm hiểu xem liệu bạn có thể giải thích hành trình đó bằng cách xem dấu vết hay không.

  • Vì vậy, bất cứ khi nào bạn không thể suy luận về điều gì đó, thì điều đó đáng để theo dõi.

  • Bạn cũng nên xem xét các ngăn xếp lệnh gọi sâu hoặc mức sử dụng CPU không rõ nguyên nhân từ một dấu vết và cố gắng tìm hiểu chính xác điều gì đang xảy ra, sau đó, bạn nên chuyển đổi các phần đó của toàn bộ mã nguồn thành dấu vết.

  • Vì vậy, bạn có thể theo dõi chúng.

  • Sau đó, bất cứ khi nào luồng của bạn ở trạng thái ngủ, bạn cần đảm bảo rằng bạn có thể hiểu được lý do tại sao luồng đó ở trạng thái ngủ.

  • Các luồng này sẽ chờ I/O, chờ giao dịch liên kết hoặc chờ một số sự kiện đầu vào bên ngoài mà bạn dự kiến sẽ xảy ra.

  • Nhưng nếu bạn có, bạn biết đấy, giấc ngủ không rõ nguyên nhân, thì những giấc ngủ đó cực kỳ đáng để điều tra vì rõ ràng có thể có điều gì đó đang ẩn nấp ở đó.

  • Sau đó, khi đã thêm tất cả các phần theo dõi vào mã đó, bạn nên chuyển đổi chúng thành điểm chuẩn để có thể theo dõi các thay đổi trong đó.

  • Vì vậy, khi toàn bộ mã nguồn phát triển, bạn có thể xem liệu các chỉ số có thay đổi hay không, liệu trang tổng quan có thực sự phản ánh những thay đổi lớn hay không, vì khi đó bạn biết rằng có điều gì đó quan trọng đã thay đổi.

  • Hoặc bạn có thể bỏ qua tất cả các bước trước đó và chỉ cần yêu cầu tác nhân thực hiện.

  • Hãy lấy những nguyên tắc này, biến chúng thành một kỹ năng, sau đó chỉ định tác nhân bạn yêu thích thực hiện kỹ năng đó và để tác nhân đó thử sức.

  • Vâng.

Thiết lập, tóm tắt và kết luận

  • Rahul Ravikumar

    Nếu bạn đã tin rằng việc theo dõi là một nỗ lực đáng giá và bạn nên thực hiện, thì đây là cách thiết lập dự án.

  • Nó khá đơn giản.

  • Truy vết chỉ là một thư viện Java truyền thống đơn giản, vì vậy, bạn xác định phần phụ thuộc trên androidx.tracing:tracing-wire, đây là trình điều khiển truy vết tương thích với Perfetto cộng với tổ hợp truy vết và phiên bản ổn định hiện tại là 2.0.5.

  • Nếu bạn là một thư viện Java, thì điều này cũng hoàn toàn giống nhau; xin nhắc lại, đây là một API hoàn toàn đa nền tảng.

  • Chúng tôi muốn mở rộng tính năng này sang các nền tảng khác khi phù hợp.

  • Và tôi chỉ muốn tóm tắt nhanh.

  • Tôi đã nói về tất cả các tính năng mới thú vị mà chúng tôi đã thêm vào.

  • Vì vậy, tính năng Truyền ngữ cảnh thực sự rất mạnh mẽ.

  • Chúng ta có thể nhúng thông tin tuỳ chỉnh, siêu dữ liệu, thông tin ngăn xếp lệnh gọi và tất cả các loại thông tin khác.

  • Tôi chưa nói về rất nhiều điều.

  • Bạn có thể thay đổi định dạng theo dõi, thay đổi bồn lưu trữ dữ liệu theo dõi, bạn có thể, bạn biết đấy, đưa dữ liệu theo dõi của mình vào OpenTelemetry.

  • Đó là tất cả những việc bạn có thể làm.

  • Tính năng theo dõi hoạt động có thể dùng trên nhiều nền tảng.

  • Đây cũng là một quyết định được cân nhắc rất kỹ lưỡng vì chúng tôi muốn càng nhiều người có thể tận dụng tính năng này càng tốt.

  • Và tất cả công cụ mà chúng tôi cung cấp, cho dù là Perfetto, AndroidX Benchmarks, trình phân tích hiệu suất mới hay Trình phân tích tài nguyên Android cũ, đều hỗ trợ Tracing 2.0.

  • Cảm ơn bạn rất nhiều vì đã lắng nghe.

  • Chúc bạn có một hội nghị thượng đỉnh dành cho nhà phát triển thật tuyệt vời. Xin cảm ơn bạn.