kako.dev

開発、自作アプリのこと

React Native のパフォーマンス改善入門

React Nativeのパフォーマンス改善を学ぶために意図的にパフォーマンスを悪くしたアプリを題材にパフォーマンスの測定と改善をしてみた。

題材はこちら。PokeAPIを利用したポケモン図鑑アプリ。

github.com

はじめに

わざとリスト画面をScrollViewで実装して、それを元にパフォーマンスを測定する。 FlatListとScrollViewでどれほど差が出るか確認してみる。

まずベースライン計測

まずは Performance Monitor( Perf Monitor ) と React Native DevTools Profiler で、同じ操作を繰り返して計測した。

計測操作

  • 通常スクロール
  • タイプ連続切替

改善前の計測値

まずは改善前のそれぞれの計測値

計測ツール 計測結果
Performance Monitor 通常スクロール
Performance Monitor タイプ連続切替
React Native DevTools Profiler

Perf Monitor(Performance Monitor)の見方

Performance Monitor

何を見るツールか

  • 実機操作中の「今の重さ」をざっくり把握するためのメーター
  • まずは体感と数値を結びつける用途で使う

主要指標

  • UI FPS:
    • 画面描画スレッドのフレームレート
    • 低いとスクロールやアニメーションがガタつく
  • JS FPS:
    • JavaScriptスレッドのフレームレート
    • 低いとタップ反応遅延、状態更新遅れ、切替時の詰まりが出る
  • RAM:
    • 使用メモリの目安
    • 操作を重ねても増え続けるなら要注意
  • Layout (ms):
    • レイアウト計算の時間目安
    • 大きいと描画タイミングが崩れやすい

どう読むか

  • 常時値より「操作直後の落ち込み」を見る
  • UI は高いのに JS が落ちる:
    • 見た目は動くが操作が重い状態
  • JS は高いのに UI が落ちる:
    • 描画側の負荷が高い状態
  • 0fps が出る:
    • 瞬間的な詰まり(フリーズに近い)を疑う

React Native DevTools Profiler の見方

React Native DevTools Profiler

何を見るツールか

  • 「どこが」「どれくらい時間を使ったか」を特定するツール
  • Perf Monitorで異常を見つけた後、原因を掘るために使う

主要項目

  • Commit:
    • 1回の更新単位
    • 棒グラフの高い山が重い更新
  • Render (ms):
    • そのコミットで描画計算に使った時間
    • 16msを大きく超えるとフレーム落ちしやすい
  • Layout effects / Passive effects:
    • Effect処理にかかった時間
    • ここが大きいと描画以外の後処理がボトルネック
  • What caused this update?:
    • 更新起点のコンポーネント/要因

各ビューの使い分け

  • Ranked:
    • 時間の大きいコンポーネントを上位から確認
    • まず犯人候補を絞る
  • Flamegraph:
    • どの親子ツリーで時間を使ったかを可視化
    • 再レンダー範囲の広さを把握する

どう読むか(今回の実例)

  • Render 1190ms、Passive effects 463.9ms
  • 更新原因が /index.tsx に集中

観測(改善前)

通常スクロール

指標
UI FPS 約 119
JS FPS 約 119
RAM 約 345MB

通常スクロール単体では破綻していない。快適に動く。

タイプを連続切替

指標
UI FPS 約 85
JS FPS 0fps が発生(瞬間停止)
RAM 約 502MB

明確に重い。JS が瞬間停止し、UI も落ちる。RAM は通常スクロール比で約 150MB 増と、上昇幅が目立つ。

Profiler

改善前のプロファイルを見ると、タイプ切替時にかなり重いコミットが出ていた。

  • Render: 1190ms
  • Passive effects: 463.9ms
  • 更新原因: /index.tsx (リスト画面)

実装した改善

ここから改善のターン。 やったことは冒頭の通り、一覧を FlatList にする。

  • ScrollView をやめる
  • ページングを onEndReached に戻す
  • ヘッダー/空表示/フッターを FlatList 側へ寄せる
  • 2カラムは columnWrapperStyle ベースに戻す

改善後の計測値

計測ツール 計測結果
Performance Monitor 通常スクロール
Performance Monitor タイプ連続切替
React Native DevTools Profiler

観測(改善後)

通常スクロール

指標
UI FPS 約 119
JS FPS 約 119
RAM 約 295MB

通常スクロールは引き続き安定。大きなカクつきは確認されていない。

タイプを連続切替

指標
UI FPS 119
JS FPS 37
RAM 約 263MB

UI は安定しているが、連続切替時は JS の落ち込みが残る。 ただし改善前の「0fps が発生」からは改善している。

Profiler

  • Render: 221ms
  • Passive effects: 4.8ms
  • 更新原因: VirtualizedList + /index.tsx に収束

改善前後 比較

Performance Monitor比較

観測シーン 指標 before after 評価
通常スクロール UI FPS 119 119 ほぼ変化なし
通常スクロール JS FPS 119 119 ほぼ変化なし
通常スクロール RAM 約 345MB 約 295MB 改善
タイプ連続切替 UI FPS 約 85 約 119 改善
タイプ連続切替 JS FPS 0 約 37 ほぼ変化なし
タイプ連続切替 RAM 約 502MB 約 263MB 大幅改善

React Native DevTools Profiler比較

指標 before after 評価
Render 1190ms 221ms 大幅改善
Passive effects 463.9ms 4.8ms 大幅改善
更新原因 /index.tsx VirtualizedList + /index.tsx 改善方向に収束

まとめ

極端にわざとパフォーマンスを悪くした例なので改善後の数字が明らかによくなりましたが、パフォーマンスを改善したいときはまず測定とどこに着目するといいかまとめました。 Expo上でPerfomance monitorで見れるのはすごいなと思ったものの、ちゃんと見るならDevTools Profilerが適しているかなと思った。

他にはHermes で起動時間も改善したりするらしいので試してみたい。