プロファイリング
プロファイリングとは、アプリのパフォーマンス、リソース使用量、および動作を分析して、潜在的なボトルネックや非効率性を特定するプロセスです。アプリがさまざまなデバイスや環境でスムーズに動作するように、プロファイリングツールを利用することをおすすめします。
iOSの場合は、Instrumentsが非常に役立つツールです。Androidの場合は、systrace
を使用する方法を学ぶ必要があります。
ただし、まず、開発モードがオフになっていることを確認してください!アプリケーションログに__DEV__ === false, development-level warning are OFF, performance optimizations are ON
と表示されるはずです。
systrace
を使用したAndroid UIパフォーマンスのプロファイリング
Androidは1万以上の異なる電話をサポートしており、ソフトウェアレンダリングをサポートするように一般化されています。残念ながら、フレームワークアーキテクチャと多くのハードウェアターゲット間で一般化する必要があるため、iOSに比べて無料で得られるものが少なくなっています。しかし、改善できるものがある場合もあります。多くの場合、ネイティブコードのせいではありません!
このジャンプをデバッグする最初のステップは、各16msフレーム中に時間がどこで費やされているかという根本的な質問に答えることです。そのため、systrace
と呼ばれる標準のAndroidプロファイリングツールを使用します。
systrace
は、標準のAndroidマーカーベースのプロファイリングツールです(Android platform-toolsパッケージをインストールするとインストールされます)。プロファイルされたコードブロックは、開始/終了マーカーで囲まれ、その後カラフルなグラフ形式で視覚化されます。Android SDKとReact Nativeフレームワークの両方が、視覚化できる標準マーカーを提供します。
1. トレースの収集
まず、調査したいスタッタリングが発生するデバイスをUSB経由でコンピューターに接続し、プロファイルするナビゲーション/アニメーションの直前のポイントにします。次のようにsystrace
を実行します。
$ <path_to_android_sdk>/platform-tools/systrace/systrace.py --time=10 -o trace.html sched gfx view -a <your_package_name>
このコマンドの簡単な内訳
time
は、トレースが収集される時間(秒単位)です。sched
、gfx
、view
は、対象となるAndroid SDKタグ(マーカーのコレクション)です。sched
は、携帯電話の各コアで実行されているものに関する情報を提供し、gfx
はフレーム境界などのグラフィックス情報を提供し、view
は測定、レイアウト、および描画パスに関する情報を提供します。-a <your_package_name>
は、アプリ固有のマーカー(特にReact Nativeフレームワークに組み込まれているもの)を有効にします。your_package_name
は、アプリのAndroidManifest.xml
にあり、com.example.app
のようになります。
トレースの収集が開始されたら、対象のアニメーションまたはインタラクションを実行します。トレースの最後に、systraceはブラウザで開くことができるトレースへのリンクを提供します。
2. トレースの読み取り
ブラウザ(できればChrome)でトレースを開くと、次のようなものが表示されるはずです。
WASDキーを使用して、横移動とズームを行います。
トレースの.htmlファイルが正しく開かない場合は、ブラウザのコンソールで次を確認してください。
Object.observe
が最近のブラウザで非推奨になったため、Google Chrome Tracingツールからファイルを開く必要がある場合があります。これを行うには、次の手順に従います。
- chrome://tracingでChromeのタブを開く
- ロードを選択
- 前のコマンドで生成されたhtmlファイルを選択します。
画面の右上にあるこのチェックボックスをオンにして、16msフレーム境界を強調表示します。
上記のスクリーンショットのように、縞模様が表示されるはずです。表示されない場合は、別のデバイスでプロファイリングを試してください。Samsungはvsyncの表示に問題があることが知られていますが、Nexusシリーズは一般的に非常に信頼性があります。
3. プロセスの検索
パッケージ名の一部が表示されるまでスクロールします。この場合、com.facebook.adsmanager
をプロファイリングしていたため、カーネルのスレッド名制限により、book.adsmanager
として表示されています。
左側には、右側のタイムライン行に対応する一連のスレッドが表示されます。今回の目的では、UIスレッド(パッケージ名またはUIスレッドという名前)、mqt_js
、mqt_native_modules
のいくつかのスレッドが重要です。Android 5以降で実行している場合は、Render Threadも重要です。
-
UIスレッド。これは、標準のAndroidの測定/レイアウト/描画が行われる場所です。右側のスレッド名は、パッケージ名(この場合はbook.adsmanager)またはUIスレッドになります。このスレッドに表示されるイベントは、
Choreographer
、traversals
、およびDispatchUI
に関連する次のようになります。 -
JSスレッド。これは、JavaScriptが実行される場所です。スレッド名は、デバイスのカーネルの協力度合いに応じて、
mqt_js
または<...>
になります。名前がない場合は、JSCall
、Bridge.executeJSCall
などを探して識別します。 -
ネイティブモジュールスレッド。これは、ネイティブモジュールの呼び出し(
UIManager
など)が実行される場所です。スレッド名は、mqt_native_modules
または<...>
になります。後者の場合、NativeCall
、callJavaModuleMethod
、およびonBatchComplete
などを探して識別します。 -
ボーナス:レンダー スレッド。Android L(5.0)以上を使用している場合は、アプリケーションにもレンダー スレッドがあります。このスレッドは、UIの描画に使用される実際のOpenGLコマンドを生成します。スレッド名は、
RenderThread
または<...>
になります。後者の場合、DrawFrame
やqueueBuffer
などを探して識別します。
原因の特定
スムーズなアニメーションは、次のようになります。
色の変化はそれぞれフレームです。フレームを表示するには、16msの期間が終わるまでにすべてのUI作業を完了する必要があることを覚えておいてください。どのスレッドもフレーム境界近くで動作していないことに注目してください。このようにレンダリングするアプリケーションは、60 FPSでレンダリングしています。
ただし、途切れに気付いた場合は、次のようなものが表示される場合があります。
JSスレッドがほぼ常に実行されており、フレーム境界を越えていることに注目してください。このアプリは60 FPSでレンダリングされていません。この場合、問題はJSにあります。
次のようなものが表示される場合もあります。
この場合、フレーム境界を越えて動作しているのはUIスレッドとレンダースレッドです。各フレームでレンダリングしようとしているUIには、完了するために多くの作業が必要です。この場合、問題はレンダリングされているネイティブビューにあります。
この時点で、次のステップを知らせる非常に役立つ情報が得られます。
JavaScriptの問題の解決
JSの問題を特定した場合は、実行している特定のJSで手がかりを探してください。上記のシナリオでは、RCTEventEmitter
がフレームごとに複数回呼び出されていることがわかります。上記のトレースからのJSスレッドの拡大図を次に示します。
これは正しくないようです。なぜこんなに頻繁に呼ばれているのでしょうか。それらは実際に異なるイベントなのでしょうか。これらの質問に対する答えは、おそらく製品コードに依存します。多くの場合、shouldComponentUpdateを調べる必要があります。
ネイティブUIの問題の解決
ネイティブUIの問題を特定した場合、通常、2つのシナリオがあります。
- 各フレームで描画しようとしているUIは、GPUで多くの作業を必要とするか、
- アニメーション/インタラクション中に新しいUIを構築している場合(スクロール中に新しいコンテンツをロードするなど)。
GPUの作業が多すぎる
最初のシナリオでは、UIスレッドやレンダー スレッドが次のように見えるトレースが表示されます。
DrawFrame
に費やされた時間がフレームの境界をまたいで長く続いていることに注目してください。これは、GPU が前のフレームからのコマンドバッファを排出するのを待っている時間です。
これを軽減するには、次のようにする必要があります。
- アニメーション/変換される複雑な静的コンテンツ (例:
Navigator
のスライド/アルファアニメーション) には、renderToHardwareTextureAndroid
の使用を検討してください。 - ほとんどの場合、フレームごとの GPU 負荷を大幅に増加させる
needsOffscreenAlphaCompositing
を使用していないことを確認してください。これはデフォルトで無効になっています。
UIスレッドでの新しいViewの作成
2番目のシナリオでは、次のようになります。
まずJSスレッドがしばらく考え込み、次にネイティブモジュールスレッドでいくらかの作業が行われ、その後、UIスレッドでコストのかかる走査が行われていることに注目してください。
インタラクション後まで新しいUIの作成を延期できるか、作成するUIを簡略化できない限り、これを軽減するための簡単な方法はありません。react nativeチームは、メインスレッド外で新しいUIを作成および構成できるようにするインフラストラクチャレベルのソリューションに取り組んでおり、インタラクションをスムーズに続行できるようにします。