[React Native] Guide - Performance - Phần 2
Bài đăng này đã không được cập nhật trong 7 năm
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âysched
,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 Nativeyour_package_name
có thể được tìm thấy trong fileAndroidManifest.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
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
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
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
:
- 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ự:
- 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ạngmqt_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
:
- 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ưDrawFrame
vàqueueBuffer
:
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:
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
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
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:
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:
- 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
- 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:
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
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
All rights reserved