$30 off During Our Annual Pro Sale. View Details »

Jetpack Benchmarkでの ViewのInflateパフォーマンスの可視化と改善 / The Jetpack Benchmark. Visualizing and improving View Inflate performance

Jetpack Benchmarkでの ViewのInflateパフォーマンスの可視化と改善 / The Jetpack Benchmark. Visualizing and improving View Inflate performance

■ZOZOGLASSについて
https://zozo.jp/zozoglass/

■Android Profiler でアプリのパフォーマンスを測定する
https://developer.android.com/studio/profile/android-profiler?hl=ja

■Fighting regressions with Benchmarks in CI
https://medium.com/androiddevelopers/fighting-regressions-with-benchmarks-in-ci-6ea9a14b5c71

■Jetpack Benchmark アプリコードのベンチマークを行う
https://developer.android.com/studio/profile/benchmark?hl=ja

■レイアウトパフォーマンスを改善する
https://developer.android.com/training/improving-layouts

■ZOZOテクノロジーズ エンジニア【Android】 の求人一覧
https://hrmos.co/pages/zozotech/jobs?category=1479046501883478016

Ryosuke Horie

April 20, 2021
Tweet

More Decks by Ryosuke Horie

Other Decks in Programming

Transcript

  1. Jetpack Benchmarkでの

    ViewのInflateパフォーマンスの可視化と改善

    2021/4/20(Tue) 

    Android Test Online #1

    株式会社ZOZOテクノロジーズ

    ZOZOTOWN本部 ZOZOアプリ部 Androidチーム Tech Lead

    堀江 亮介
    Copyright © ZOZO Technologies, Inc.

    View Slide

  2. © ZOZO Technologies, Inc.
    株式会社ZOZOテクノロジーズ

    ZOZOTOWN本部 ZOZOアプリ部 Androidチーム

    Tech Lead
    堀江 亮介

    ● 自動化とビールが好き
    ● @Horie1024

    2

    View Slide

  3. © ZOZO Technologies, Inc.
    https://zozo.jp/

    ● 日本最大級のファッション通販サイト

    ● 1,400以上のショップ、8,100以上のブランドの取り扱い(ともに2020年12
    月末時点)

    ● 常時83万点以上の商品アイテム数と毎日平均3,000点以上の新着 商
    品を掲載

    ● 即日配送サービス

    ● ギフトラッピングサービス

    ● ツケ払い など

    3

    View Slide

  4. © ZOZO Technologies, Inc.
    https://zozo.jp/

    ● 日本最大級のファッション通販サイト

    ● 1,400以上のショップ、8,100以上のブランドの取り扱い(ともに2020年12
    月末時点)

    ● 常時83万点以上の商品アイテム数と毎日平均3,000点以上の新着 商
    品を掲載

    ● 即日配送サービス

    ● ギフトラッピングサービス

    ● ツケ払い など

    4

    View Slide

  5. © ZOZO Technologies, Inc.
    5
    ● リニューアルで発生したスクロールパフォーマンスの問題

    ● ボトルネックの確認と改善に使用した方法

    ○ Android Profilerでのボトルネック確認

    ○ Jetpack BenchmarkでのInflateのパフォーマンス計測・改善

    今日話すこと


    View Slide

  6. © ZOZO Technologies, Inc.
    6
    ● ZOZOGLASSについて

    ● もし話を聞きたい方いましたら堀江にDMください🙏

    今日話さないこと


    View Slide

  7. © ZOZO Technologies, Inc.
    スクロールパフォーマンス問題

    7

    View Slide

  8. © ZOZO Technologies, Inc.
    8
    ● ホーム画面のリニューアル

    ● 縦×横でNestedなRecyclerView構成

    ● 縦スクロールのカクツキ問題が発生

    ● 改善するにはどうすれば良いか?


    View Slide

  9. © ZOZO Technologies, Inc.
    9
    “推測するな計測せよ”


    View Slide

  10. © ZOZO Technologies, Inc.
    10
    Android Profiler



    ● Android Studio 3.0以上で提供されるAndroid Monitorの後継

    ● 各種プロファイリングが可能

    ○ https://developer.android.com/studio/profile/android-profiler?hl=ja

    ● Android Profilerでパフォーマンス上のボトルネックを探る


    View Slide

  11. © ZOZO Technologies, Inc.
    11
    ● Trace System Callsでトレースを記録


    CPU Profilerでの検査


    View Slide

  12. © ZOZO Technologies, Inc.
    12
    ● トレース記録の検証


    CPU Profilerでの検査


    View Slide

  13. © ZOZO Technologies, Inc.
    13
    トレース記録からのボトルネックの発見


    View Slide

  14. © ZOZO Technologies, Inc.
    14
    トレース記録からのボトルネックの発見


    View Slide

  15. © ZOZO Technologies, Inc.
    15
    トレース記録からのボトルネックの発見

    Framesの赤 16ms以内でのレンダリングが完了しない

    フレームのドロップまたは遅延(ジャンク)が発生

    View Slide

  16. © ZOZO Technologies, Inc.
    16
    トレース記録からのボトルネックの発見


    View Slide

  17. © ZOZO Technologies, Inc.
    17
    トレース記録からのボトルネックの発見


    View Slide

  18. © ZOZO Technologies, Inc.
    18
    トレース記録からのボトルネックの発見

    ● ネストされたRecyclerViewでの商品表示

    ● スクロール時にジャンクが発生

    ● 商品CellのInflateコストをボトルネックと判断


    View Slide

  19. © ZOZO Technologies, Inc.
    19
    ● Inflateコスト高がスクロールのカクツキ(ジャンク)の一要因

    ● 商品CellについてInflateコストを下げる改善を実施

    ● パフォーマンスを数値として客観的に確認でき、

    簡単に繰り返し実行できる手段があると便利

    ボトルネックの解消


    View Slide

  20. © ZOZO Technologies, Inc.
    20
    ● Inflateコスト高がスクロールのカクツキ(ジャンク)の一要因

    ● 商品CellについてInflateコストを下げる改善を実施

    ● パフォーマンスを数値として客観的に確認でき、

    簡単に繰り返し実行できる手段があると便利

    → Jetpack Benchmarkが利用できる

    ボトルネックの解消


    View Slide

  21. © ZOZO Technologies, Inc.
    21
    ● Instrumented testsと同様にBenchmarkのコードを書ける

    ● Android Studioからいつでも実行可能

    ● CIのフローに組み込みことも可能

    ○ 参考: Fighting regressions with Benchmarks in CI

    Jetpack Benchmark


    View Slide

  22. © ZOZO Technologies, Inc.
    22
    ● Benchmark Moduleを作成することでベンチマークを実行可能

    Jetpack Benchmarkでのベンチマーク実行


    View Slide

  23. © ZOZO Technologies, Inc.
    23
    @RunWith(AndroidJUnit4::class)
    class ViewInflateBenchmark {
    @get:Rule
    val benchmarkRule = BenchmarkRule()
    @UiThreadTest
    @Test
    fun inflate() {
    val context = ContextThemeWrapper(ApplicationProvider.getApplicationContext(), R.style.AppTheme)
    val inflater = LayoutInflater.from(context)
    val root = FrameLayout(context)
    benchmarkRule.measureRepeated {
    inflater.inflate(R.layout.view_item, root, false)
    }
    }
    }

    View Slide

  24. © ZOZO Technologies, Inc.
    24
    @RunWith(AndroidJUnit4::class)
    class ViewInflateBenchmark {
    @get:Rule
    val benchmarkRule = BenchmarkRule()
    @UiThreadTest
    @Test
    fun inflate() {
    val context = ContextThemeWrapper(ApplicationProvider.getApplicationContext(), R.style.AppTheme)
    val inflater = LayoutInflater.from(context)
    val root = FrameLayout(context)
    benchmarkRule.measureRepeated {
    inflater.inflate(R.layout.view_item, root, false)
    }
    }
    }

    View Slide

  25. © ZOZO Technologies, Inc.
    25
    @RunWith(AndroidJUnit4::class)
    class ViewInflateBenchmark {
    @get:Rule
    val benchmarkRule = BenchmarkRule()
    @UiThreadTest
    @Test
    fun inflate() {
    val context = ContextThemeWrapper(ApplicationProvider.getApplicationContext(), R.style.AppTheme)
    val inflater = LayoutInflater.from(context)
    val root = FrameLayout(context)
    benchmarkRule.measureRepeated {
    inflater.inflate(R.layout.view_item, root, false)
    }
    }
    }

    View Slide

  26. © ZOZO Technologies, Inc.
    26
    @RunWith(AndroidJUnit4::class)
    class ViewInflateBenchmark {
    @get:Rule
    val benchmarkRule = BenchmarkRule()
    @UiThreadTest
    @Test
    fun inflate() {
    val context = ContextThemeWrapper(ApplicationProvider.getApplicationContext(), R.style.AppTheme)
    val inflater = LayoutInflater.from(context)
    val root = FrameLayout(context)
    benchmarkRule.measureRepeated {
    inflater.inflate(R.layout.view_item, root, false)
    }
    }
    }

    View Slide

  27. © ZOZO Technologies, Inc.
    27
    @RunWith(AndroidJUnit4::class)
    class ViewInflateBenchmark {
    @get:Rule
    val benchmarkRule = BenchmarkRule()
    @UiThreadTest
    @Test
    fun inflate() {
    val context = ContextThemeWrapper(ApplicationProvider.getApplicationContext(), R.style.AppTheme)
    val inflater = LayoutInflater.from(context)
    val root = FrameLayout(context)
    benchmarkRule.measureRepeated {
    inflater.inflate(R.layout.view_item, root, false)
    }
    }
    }

    View Slide

  28. © ZOZO Technologies, Inc.
    28
    @RunWith(AndroidJUnit4::class)
    class ViewInflateBenchmark {
    @get:Rule
    val benchmarkRule = BenchmarkRule()
    @UiThreadTest
    @Test
    fun inflate() {
    val context = ContextThemeWrapper(ApplicationProvider.getApplicationContext(), R.style.AppTheme)
    val inflater = LayoutInflater.from(context)
    val root = FrameLayout(context)
    benchmarkRule.measureRepeated {
    inflater.inflate(R.layout.view_item, root, false)
    }
    }
    }

    View Slide

  29. © ZOZO Technologies, Inc.
    29
    @RunWith(AndroidJUnit4::class)
    class ViewInflateBenchmark {
    @get:Rule
    val benchmarkRule = BenchmarkRule()
    @UiThreadTest
    @Test
    fun inflate() {
    val context = ContextThemeWrapper(ApplicationProvider.getApplicationContext(), R.style.AppTheme)
    val inflater = LayoutInflater.from(context)
    val root = FrameLayout(context)
    benchmarkRule.measureRepeated {
    inflater.inflate(R.layout.view_item, root, false)
    }
    }
    }

    View Slide

  30. © ZOZO Technologies, Inc.
    30
    商品CellのInflateパフォーマンスの改善


    ● 商品CellのInflateコストを削減

    ● スクロール時にジャンクを低減させる

    ● ベンチマーク結果を確認しながら改善


    View Slide

  31. © ZOZO Technologies, Inc.
    31
    ● 方針

    ○ レイアウト構造のflatten化

    ○ レイアウトの遅延読み込み

    ● Android Developersの「レイアウトパフォーマンスを改善する」

    の内容が基本

    ○ https://developer.android.com/training/improving-layouts


    商品CellのInflateパフォーマンスの改善


    View Slide

  32. © ZOZO Technologies, Inc.
    32
    ● 2種類のレイアウトの遅延読み込み方法

    ○ ViewStub

    ○ AsyncLayoutInflater


    レイアウトの遅延読み込み


    View Slide

  33. © ZOZO Technologies, Inc.
    33
    ● 軽量なView

    ● ViewStub自身は何も描画しない

    ● layout属性で指定したレイアウトに任意のタイミングで置き換え


    ViewStub

    android:id="@+id/stub_import"
    android:inflatedId="@+id/panel_import"
    android:layout="@layout/progress_overlay"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom" />

    View Slide

  34. © ZOZO Technologies, Inc.
    34
    ● 軽量なView

    ● ViewStub自身は何も描画しない

    ● layout属性で指定したレイアウトに任意のタイミングで置き換え


    ViewStub

    val view: View = findViewById(R.id.stub_import).inflate()

    View Slide

  35. © ZOZO Technologies, Inc.
    35
    ● レイアウトを非同期でInflate

    ● ViewStub同様任意のタイミングでInflateを行える

    ● Inflateした結果のViewをcallbackで受け取る


    AsyncLayoutInflater

    class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val content = findViewById(android.R.id.content)
    AsyncLayoutInflater(this)
    .inflate(R.layout.activity_main, content) { view, _, _ ->
    setContentView(view)
    }
    }
    }

    View Slide

  36. © ZOZO Technologies, Inc.
    36
    ViewStub vs. AsyncLayoutInflater

    ● ViewStubは同期的、AsyncLayoutInflaterは非同期的にInflate

    ● 頻繁に使われるViewの置き換えにViewStubを使用

    ○ 同期的にInflateが行われるためジャンクが解消されない

    ○ 使用されないViewであればViewStubを使って問題ない

    ○ 例: onBindViewHolderで同期的にInflateが起きている




    View Slide

  37. © ZOZO Technologies, Inc.
    37
    ● 商品Cellから次の特徴を持つViewを遅延読み込み

    ○ ネストが深い構造を持ったView

    ○ ホーム画面で使用されないView

    ■ 商品Cellは複数のリストで共有されている

    ○ Inflateのコストが高いView

    ■ Profilerの結果から確認


    AsyncLayoutInflaterでのViewの遅延読み込み


    View Slide

  38. © ZOZO Technologies, Inc.
    38
    ● 分析ペインのcallstack表示からInflateコストが高いViewを確認

    AsyncLayoutInflaterでのViewの遅延読み込み


    View Slide

  39. © ZOZO Technologies, Inc.
    Inflateコストの改善結果

    39

    View Slide

  40. © ZOZO Technologies, Inc.
    40
    ● 12.31msから1.32msに短縮


    改善前後のBenchmarkでの計測結果


    View Slide

  41. © ZOZO Technologies, Inc.
    41
    ● Profilerの結果では改善はされているが劇的に良くなった訳ではない

    ● RecyclerView自体のパフォーマンスチューニングも実施

    ○ チーム内での検証では体感でも改善された

    実際のスクロールパフォーマンスは改善されたのか?


    View Slide

  42. © ZOZO Technologies, Inc.
    42
    ● Profilerの結果では改善はされているが劇的に良くなった訳ではない

    ● RecyclerView自体のパフォーマンスチューニングも実施

    ○ チーム内での検証では体感でも改善された

    ● 今後

    ○ 改善された状態で近日中にリリース(4/20時点で未リリース🙇)

    ○ 仕様検討まで含めて商品Cellの最適化を進める

    ○ Benchmarkの対象をInflateのではなくRecyclerViewとする

    実際のスクロールパフォーマンスは改善されたのか?


    View Slide

  43. © ZOZO Technologies, Inc.
    まとめ

    43

    View Slide

  44. © ZOZO Technologies, Inc.
    44
    ● Benchmarkベースで商品CellのInflateパフォーマンス改善をした

    ● だがスコアほど実際のボトルネックは解消されていない

    ● RecyclerViewのスクロールそのもののBenchmarkを取る等を行いよりパ
    フォーマンスを改善できる方法を探る

    まとめ


    View Slide

  45. © ZOZO Technologies, Inc.
    45
    ● ZOZOテクノロジーズではAndroidエンジニアを募集しています!

    ● 少しでもご興味のある方気軽にお声かけください。カジュアル面談をぜひ
    お願いします!

    最後に


    View Slide

  46. View Slide