Profiling

Sử dụng trình build-in profiler để xem thông tin chi tiết về các hoạt động trong luồng của JavaScript và luồng main thread side-by-side. Để truy cập bạn vào lựa chọn Perf Monitor từ Debug menu.

Trong iOS, Instruments là một công cụ hữu dụng, và trong Android bạn nên học cách sử dụng systrace.

Bạn cũng có thể sử dụng react-addons-perf để xem chi tiết nơi mà React tốn thời gian khi render các components.

Một cách khác để xem profile JavaScript là sử dụng Chrome profiler trong khi debugging. Điều đó không cung cấp một cách chính xác kết quả như là code đang chạy trong Chrome như nó sẽ đưa ra cho bạn những ý tưởng chung để giải quyết một vài bế tắc trong vấn đề Performance.

Nhưng đầu tiên, hãy chắc chắn để Development Mode is OFF!. Bạn nên thấy __DEV__ === false, development-level warning are OFF, performance optimizations are ON trong log của ứng dụng.

Profiling Android UI Performance với systrace

Android hỗ trợ khoảng hơn 10k thiết bị di động và đã được quy chuẩn hóa để hỗ trợ render phần mềm: Kiến trúc của framework và sự chuẩn hóa cần thiết cho các phần cứng mục tiêu, thật không thay điều đó có nghĩa là điều đó hoàn toàn khác với iOS. Nhưng đôi khi, ó những điều bạn có thể cải thiện – và nó không phải lỗi của mã nguồn gốc!

Bước đầu tiên để debugging cho điều này là câu hỏi cơ bản cho câu hỏi ở đâu bạn sẽ bắt đầu chờ đợi mỗi 16ms frame. Và để làm điều đó bạn cần phải sự dụng một công cụ chuẩn của Android profiling được gọi là systrace.

systrace là một công cụ chuẩn của Android marker-based (và nó được cài đặt sẵn khi bạn càn đặt Android platform-tools). Profiled code block là việc tạo ra một vùng bao với start/stop tại các điểm marker và có thể hình thấy được các màu sắc hiển thị theo hình thức biểu đồ. Cả Android SDK và React Native đều cung cấp chuẩn marker để bạn có thể nhìn thấy được.

1. Tổng hợp một trace

Đầu tiên, kết nối thiết bị với máy tính thông qua USB và ứng dụng chạy tới trước điểm mà bạn muốn xem profile. Sau đó bạn chạy systrace với lệnh sau:

$ <path_to_android_sdk>/platform-tools/systrace/systrace.py –time=10 -o trace.html sched gfx view -a <your_package_name>

Ý nghĩa một vài tham số trong câu lệnh

  • time là khoảng thời gian bạn muốn theo dõi và đơn vị của nó là giây
  • sched, gfx, và view là các thẻ của SDK (các collection của marker) mà chúng ta sẽ quan tâm: sched cung cấp cho bạn thông tin cái gì đang được chạy ở mỗi nhận của thiết bị, gfx cung cấp cho bạn giao diện tương tự như đường biên của frame, và view cung cấp cho bạn các thông tin về kích thước, layout, và những gì được vẽ lên.
  • -a <your_package_name> cho phép đánh dấu một ứng dụng cụ thể, cụ thể hơn trong React Native your_package_name có thể được tìm thấy trong file AndroidManifest.xml bên trong ứng dụng của bạn và nó có dạng như com.example.app

Và ở cuối cùng của file trace, systrace sẽ cung cấp cho bạn một đường link và bạn có thể mở nó ở trong trình duyệt.

2. Cách độc trace

Sau khi mở file trace trong trình duyệt của bạn (khuyến nghị sử dụng Chrome), bạn sẽ nhìn thấy một số thứ tương tự như dưới đây

systrace

Bạn có thể thực hiện zoom để xem chi tiết hơn

Nếu như file trace với đuôi .html không được mở đúng, hãy nhiểu tra lại trình duyệt của bạn để thấy được dòng báo lỗi

systrace_error

Từ khi Object.observe bị loại bỏ trong trình duyệt gần đây, bạn có thể mở file bằng Google Chrome Tracing tool. Bạn làm điều đó bằng cách

  • mở một tab mới với địa chỉ chrome://tracing
  • Lựa chọn load
  • Lựa chọn file html được tạo ra bởi dòng lệnh ở trên

Kích hoạt VSync highlighting

Kiểm tra checkbox này ở trên cùng bên phải của màn hình để hiển thị giới hạn 16ms frame

16ms_frame

Bạn nên xem những đường sọc như là ảnh bên trên. Nếu bạn không thấy, hãy thử profiling trên một thiết bị khác: Samsung được biết là đã gặp phải issue khi hiển thị vsyncs trong khi những dòng Nexus thỉ hiển thị rất tôt.

3. Tìm tiến trính của bạn

Scroll cho tới khi bạn thấy được phần có tên là package ứng dụng của bạn. Trong trường hợp này, tôi thấy profiling của com.facebook.adsmanager trong khi đang hiển thị book.adsmanager bởi vì có một sự giới hạn luồng trong kernel.

Tại phần bên trái, bạn sẽ nhìn thấy một danh sách các luồng trong tương tứng với dòng timeline ở bên phải. Có nhiều luồng mà bạn cần phải quan tâm như: luồng UI (Trong đó có package name của bạn hoặc tên của luồng UI), mqt_js, và mqt_native_modules. Nếu như bạn đang chạy Android 5+, chúng ta có thể quan tâm thêm đến Render Thread.

  • UI Thread. Đó là nơi thể hiện các thông tin theo chuẩn của Android measure/layout/draw happens. Tên luồng ở bên phải sẽ là package name của bạn ( trong trường hợp của tôi chính là book.adsmanager) hoặc UI Thread. Sự kiện mà bạn nhìn thấy trên luồng này sẽ giống như cách hiển thị đã từng có trên Choreographer, traversals, và DispatchUI:

ui_thread

  • JS Thread. Đây là nơi mà JavaScript chạy. tên của luồng sẽ có dạng mqt_js hoặc <...> phụ thuộc vào cách mà kernel trên thiết bị của bạn xử lý. Để phát hiện được nó nếu như nó không có một tên cụ thể thì bạn nên tìm giống như JSCall, Bridge.executeJSCall hoặc một vài thứ khác tương tự:

Js_thread

  • Native Modules Thread. Đây là nơi mà native module được gọi (ví dụ như UIManager) bị khởi chạy. tên luồng sẽ có dạng mqt_native_modules hoặc <...>. Để phát hiện được nó trong trường hợp này bạn hãy tìm đến những đoạn giống như NativeCall, callJavaModuleMethod, và onBatchComplete:

native_module_thread

  • Bonus: Render Thread. Nếu như bạn đang sử dụng Android L (5.0) hoặc cao hơn, bạn sẽ có thể thấy thêm được luồng render trong ứng dụng của bạn. Đây là luồng được tạo ra bởi những câu lệnh của OpenGL để vẽ lên UI của bạn. Tên luồng sẽ có dạng RenderThread hoặc <...>, Để phát hiện nó trong trường hợp này, hãy tìm những thứ tương tự như DrawFramequeueBuffer:

render_thread

Phát hiện thủ phạm

Một animation mượt mà nên có biểu đồ tương tự như dưới đây:

smooth_animation

Mỗi thay đổi về màu sắc là một frame – chú ý đó chính là yêu cầu để hiển thị một frame, tất cả UI của chúng ta làm việc sẽ cần hoàn thành với thời gian tiêu chuẩn là 16ms. Một ứng dụng hiện thị tốt nó sẽ cho tốc độ hiện thị là 60FPS

Nếu bạn chú ý chi tiết hơn bạn sẽ thấy nó tương tự như hình dưới đây

chop

Chú ý trong JS thread cơ bản sẽ luôn chạy, và giới hạn những frame chạy qua nó. Ứng dụng này không hiển thị 60FPS, trong trường hợp này, vấn đề đánh lừa lằm trong JS

Bạn sẽ nhìn thấy nó như dưới đây

JS

Trong trường hợp này, UI và Render thread là một và nó cùng hoạt động trong vùng giới hạn. UI chúng ta đã cố render mỗi frame yêu cầu quá nhanh để hiển thị. Trong trường hợp này thì vấn đề đánh lừa nằm ở native view rendered

Ở đây, bạn sẽ có một vài thông tin ữu ích để thực hiện bước tiếp theo.

Giải quyết vấn đề với JavaScript

Nếu bạn phát hiện vấn đề nằm ở JavaScript, hãy xem cụ thể xem Js nào đang được chạy. Trong trường hợp dưới đây, chúng tôi thấy RCTEventEmitter đang được gọi tới nhiều lần mỗi frame. Hở đây là hình ảnh phóng to của JS thread từ trace:

RCTEventEmitter

Dường như nó không đúng. Tại sao nó được gọi thường xuyên như vậy? Có phải đã có những sự kiện khác nữa hay không? Câu trả lời trong những câu hỏi đó liên quan trực tiếp đến việc code ứng dụng của bạn. Và đã nhiều lần, tôi tìm trong shouldComponentUpdate.

Giải quyết vấn đề với UI issue

Nếu như bạn phát hiện vấn đề ở native UI, có hai kịch bản thông thường:

  1. UI bạn đang cố vẽ đối với mỗi một frame đã yêu cầu quá nhiều hoạt động của GPU, hoặc
  2. Bạn đang tạo ra những UI mới trong thời gian đang chạy animation(ví dụ như load nội dung mới trong khi đang scroll).
GPU hoạt động quá nhiều

Trong kịch bản đầu tiên, bạn sẽ thấy trace có UI thread và/hoặc Render thread giống như hình dưới:

GPU

Chú ý đến việc tiêu tốn quá nhiều thời gian trong DrawFrame trong giới hạn frame. Đây là thời gian chờ GPU lưu vùng nhớ đệm từ frame trước.

Để nâng cấp nó, bạn nên:

  • Nghiên cứu cách sử dụng renderToHardwareTextureAndroid để linh động hơn, các nội dung tĩnh phải được animated/transforme ( ví dụ như Navigator slide/alpha animations)

  • Chắc chắn là bạn không sử dụng needsOffscreenAlphaCompositing trong khi nó đã mặc định được tắt, ddieuf đó là tốt hơn cho việc load mỗi một frame với GPU.

Nếu như những điều trên vẫn chưa giúp được bạn, hãy nghiên cứu sâu hơn về các hoạt động của GPU, bạn có thể kiểm tra Tracer for OpenGL ES

Tạo một view mới trên UI thread

Trog kịch bản thứ hai, bạn sẽ thấy nó tương tự như sau

new_view

Chú ý đầu tiên JS thread sử dụng một phần nhỏ, sau đó bạn sẽ thấy công việc hoàn thành trên native module thread, tiêp tục theo đó để mỏ rộng trên UI thread.

Không có một cách thức nào dễ dàng để nâng cấp nó, trừ khi bạn có khả năng tạm dựng việc tạo ra UI mới cho đến khi hành động được thực hiện xong, hoặc bạn có thể tạo ra các UI đơn giản hơn. React Native team đang làm biệc trên những giải pháp nền tảng để có thể cho phép bạn tạo ra các UI mới và cấu hình nó không phải trên main thread, điều đó cho phép bạn tích hợp mà vẫn có được sự mượt mà.

Nguồn Performance