アプリのパフォーマンスを継続的に計測する

 アプリのパフォーマンスを継続的に計測する

Aaf94d61dec57d1c3cf2de7c09e472d4?s=128

Daiki Katayama

September 21, 2020
Tweet

Transcript

  1. 継続的にアプリの パフォーマンスを計測する iOSDC Japan 2020 kariad

  2. 自己紹介 • kariad / 片山大樹 / @kariad_uu • DeNA >

    SWET > Automation test iOS team ◦ Software Engineer in Test • ソフトウェアテスト、それを支えるプロセス改善
  3. はじめに 今回の発表では、SWETで取り組んでいるアプリのパフォーマンス計測について話しま す パフォーマンス計測に関する知見は多くはなく、試行錯誤しながら作っていったため、失 敗もたくさんあります そうした失敗や今はこうしているといった知見を紹介できたらと

  4. アジェンダ 1. なぜパフォーマンス計測が必要なのか? 2. iOSアプリにおけるパフォーマンス計測方法 3. 継続して計測する仕組みの作成 4. 運用してみて 5.

    これからについて
  5. なぜパフォーマンス計測が必要なのか?

  6. アプリのパフォーマンスが悪いと ひとくちにパフォーマンスと言っても様々 • なかなか読み込みが終わらない • 短時間の利用で端末が熱くなる • 動作が重い、カクカク、もっさり 場合によってはパフォーマンスが悪いことでクラッシュも起こりうる

  7. アプリのパフォーマンスが悪いと ユーザーがアプリを快適に利用できない →ユーザーの離脱の可能性 防ぐためにはリリース前に機能が正しく実装されているかだけではなくパフォー マンス観点でもテストを行い、問題ないかを確認できると良い

  8. iOSアプリにおけるパフォーマンス計測

  9. iOSアプリにおけるパフォーマンス計測 代表的な例 • Instruments • MetricKit • 外部(Apple以外が提供している)サービス どのような情報が取れるか、どのように情報が取れるか、計測タイミングなどは異なる

  10. Instruments • Xcodeの一部として提供されているパフォーマンス計測ツール • どこのパフォーマンスが悪いのか分析、特定する機能をサポート • TimeProfiler, Leaks, Network等多くのテンプレートを持ち様々な角度からの計測 が可能

    • 原因の特定はdSYMがあれば実際のコードのどこの部分かというところまで特定で きる
  11. Instruments Templateが充実していて選ぶだけで簡単に計測ができる

  12. None
  13. Instruments GUIだけではなくCUIからでも計測が可能 instruments -t ${template} -w ${UDID} -l ${timeLimit} -D

    ${fileName}
  14. Instruments GUIだけではなくCUIからでも計測が可能 instruments -t ${template} -w ${UDID} -l ${timeLimit} -D

    ${fileName} ※保存時に少し時間がかかるので注意 template名(e.g. Time Profiler) 計測に利用する端末 UDID 計測時間の上限(ms) 保存時のファイル名 (xxx.trace)
  15. MetricKit • iOS13から利用可能 • 実際にコードに埋め込んでデバイス上の情報を取得できる • 起動時間、CPU使用時間、バッテリー消費量、クラッシュなど • ユーザの環境で計測し、24時間に一回サーバにデータが送られる WWDCのセッションによるとMetricKitを用いてTestFlightのベータテスターや実際の

    ユーザーの手元で何が起こっているかを計測するべきだと述べられている 詳しく知りたい方はWWDC 2020の「What’s new in MetricKit」がおすすめ https://developer.apple.com/videos/play/wwdc2020/10081/
  16. 外部サービス SDKとして組み込んで計測するタイプ • Firebase Performance Monitoring デバイスファームとセットになったサービス • HeadSpin →

    計測できる指標はサービスによりけり
  17. 継続して計測する仕組み

  18. 継続的な計測 • 常に安定したアプリをリリースするには悪くなってからでは遅い • そのために継続的にパフォーマンス計測が行える仕組みが必要 ◦ 継続して計測することで改善も実施しやすくなる • 毎回手動で実施するのは大変なので自動で実施したい

  19. 今回の計測対象 Pococha(ポコチャ)は、DeNAが運営するライブコミュニケーションアプリです。 誰でもいつでも簡単にライブ配信と視聴することができ、ライバー(配信者)とリスナー(視聴者)による双方向コミュ ニケーションで、一緒にライブ配信を盛り上げます。 熱く応援してくれるファンに出会い、ファンとつながるコミュニティ・プラットフォームとして、ライバー・リスナーの両者 にとって居心地のよい場を提供しています。

  20. 今回計測したかった内容 Pocochaで視聴者が多い配信での端末の温度上昇とそれに伴うパフォーマンス低下が 問題になっていた そのため一定時間負荷をかけ続けたアプリの状態を配信/視聴画面まで移動してから計 測対象とした • 計測時間: 30分, 2時間 •

    計測内容: CPU使用率と端末の温度(Thermal state)の変化 • 計測タイミング: 毎晩
  21. 継続的な計測で必要と考えたもの • 計測ツール • 最新のアプリ • 実機 ◦ 計測する内容にもよるが、端末の温度などは実機でしか計測ができない •

    定期的に実行させるための環境(CIサービス) • アプリを操作するUIテスト • アプリに負荷をかけるツール
  22. 計測ツール Instrumentsを採用 • すでにパフォーマンスが問題になり始めているということもありリリース前、開発中 に問題ないかを確認したかった • 問題がある場合は早く原因を特定したい ◦ InstrumentsならdSYMがあれば原因まで調査できる

  23. 最新のアプリ 毎日の変更に追従した最新のアプリで計測(場合によっては任意のブランチなど) デバッグ用にdSYMも必要なためCIでArtifactsとして残す必要 さらにInstrumentsで正しく計測するためには以下の条件を満たす必要がある • コンパイラの最適化を実際のreleaseビルドと合わせる • developmentの証明書でsignしている

  24. 最新のアプリ コンパイラの最適化の問題 • Xcodeのデフォルトの設定ではdebugビルドとreleaseビルドで最適化の設定は異 なっている • 最適化の差異がパフォーマンス計測に影響がでる可能性がある • 実際にユーザが利用するreleaseビルドと同等の最適化がされたアプリで計測する 必要がある

    • WWDC 2019のGetting Started with Instrumentsセッションが参考になる ◦ https://developer.apple.com/videos/play/wwdc2019/411/
  25. 最新のアプリ signする証明書の問題 • Instrumentsで計測するためにはdevelopmentのcertificateでsignしたものでなくて はいけない(配布用では計測できない) releaseの最適化設定を利用しながらdevelopmentでsignが必要

  26. 最新のアプリ ipaのresign • ipaに対して証明書を付け替えることができる • releaseでビルドしたipaに対してdevelopmentの証明書でresignできる • fastlaneのresignを利用 ◦ https://docs.fastlane.tools/actions/resign/

  27. 最新のアプリ fastlane resign resign( ipa: ipa_path, signing_identity: "iPhone Developer: Xxxxxx

    Xxxxxxx(FFFFFFFFF)", provisioning_profile: { "com.swet.app" => provisioning_profile_path, "com.swet.app.notificationservice" => provisioning_profile_notification_path }, display_name: "SWET Debug" )
  28. 最新のアプリ fastlane resign resign( ipa: ipa_path, signing_identity: "iPhone Developer: Xxxxxx

    Xxxxxxx(FFFFFFFFF)", provisioning_profile: { "com.swet.app" => provisioning_profile_path, "com.swet.app.notificationservice" => provisioning_profile_notification_path }, display_name: "SWET Debug" ) resign後のidentity(今回の例ではrelease→development なのでdevelopmentのを記載) 左側に今signしているBundle identifier(今回はrelease) 右側にresignするProvisioning profileのpath 後からわかるようにdisplay nameも変えられる
  29. 実機 Thermal stateを計測するために実機での計測が必要 • 自前で管理はコストが高い ◦ テストが走っているタイミングで他のテストが走らないようにする排他制御が必要 ◦ OSのバージョン管理 ◦

    場所も取る ◦ 問題が発生したときに出社が必要
  30. 実機 クラウド型のデバイスファーム Web等から実際の実機と同じようにアクセス、操作できるサービス • AWS Device Farm • Remote Testkit

    • HeadSpin
  31. HeadSpin 様々なパフォーマンス計測もできるデバイスファーム 端末単位の契約で端末を他社と共有することはない • Xcodeから直接Runができる • Wi-Fiだけでなく特定のSIMで4G回線での利用もできる • 実機でできる大体のことはできる •

    AppiumサーバーがHeadSpin側で用意されている 今回計測はInstrumentsを使うことにしたのでHeadSpinの機能は使っていない(将来的 に計測項目が増えた際に使いたいとは思っている)
  32. HeadSpin Appiumについての補足 Appiumクライアント Appiumサーバー Web driver agent テストスクリプト テスト対象アプリ 端末

    Appium動かすにはここが必要
  33. HeadSpin

  34. HeadSpin APIも充実していて例えば • ipaのインストール、アンインストール • スクリーンショットの撮影 • デバイスのロック、アンロック • OSアップデートのポップアップを消す

    等々、他にも色々ある
  35. HeadSpin Appiumから利用する場合 • Capabilityのserver_urlにHeadSpin側に表示されるURLをセットするだけで Appiumサーバーを立てずに利用できる(UDID等は必要) • Appium DesktopからもHeadSpin Web Driver

    URLをセットするだけで利用できる • AppiumのバージョンもCapabilityから指定できる(インストールされていない場合は お願いしたらすぐ対応してくれる)
  36. HeadSpin Appium以外から利用する場合 • HeadSpin CLIを使ってbridgeする • XcodeからRunできる、Instrumentsで計測もできる

  37. CI 今回の計測では2種類の用途がある • 計測対象のアプリをビルド、resignする • 計測を行う(負荷ツールを動かす) 元々Bitriseを利用していたのでビルドはBitriseで問題ない 計測は最大2時間を想定していたためBitriseでは不可能(現時点の最大は90分) → 色々やりたいと考えるとひとまず選択肢としてはJenkins

  38. CI Jenkins • Jenkins自体の管理が必要 → SWETのCI/CDチームが管理してくれているJenkinsに相乗り Xcodeの新しいバージョンも簡単にインストールできる仕組みが整っている (Jenkinsに詳しいメンバーが周りにいるので困ったら気軽に質問できる) ただし最終的には脱Jenkinsを考えてはいる

  39. アプリ操作用のUITestフレームワーク 目的の画面までアプリの操作が必要 • Appium x Ruby(RSpec) • XCUITest HeadSpinを利用する予定だったのでAppiumサーバーを用意する必要がなく、Appium が相性が良かった

    AppiumからInstrumentsを利用することもできたのも◯
  40. Appium Insturmentsでの計測がAppiumのコード経由で実行することができる # Rubyでの例 # 計測開始 @driver.start_performance_record( timeout: ENV['TIMEOUT'], profile_name:

    'Time Profiler' ) # 計測完了 @driver.get_performance_record( save_file_path: "fine_name", profile_name: 'Time Profiler' )
  41. Appium Capabilityでの注意点 • 実機の場合はbundleIdの指定が必須 • 待ちが発生するためnewCommandTimeoutの設定をしてTimeoutを防ぐ • HeadSpinのAppiumサーバーを利用する場合はserverUrlに設定する

  42. アプリに負荷をかけるツール 必ずしも必要なものとは限らないが、今回の要件では必要だった • SWETの他チーム(Goチーム)のメンバーと協力して作成 • 変更が入った場合でも常に最新のものを利用するためGitHub ReleaseのAssets からDLできるように

  43. この構成で起きた問題 必要なものはすべて揃った、後は組み合わせるだけ... そう上手いこといくわけはなかった 起きた問題の一例としては • Appium serverの問題 • Instrumentsのデータ取得の問題

  44. Appium serverの問題 最初Appium serverはHeadSpinのものを使う想定だった いくつかの理由からAppium serverをJenkins上で持つ形に • Xcodeのバージョン • traceファイルの保存先

    Appium serverをHeadSpin側のものを利用するということは、Instrumentsのバージョン (Xcodeのバージョン)も向こうに依存してしまう 新しくしたりビルドと合わせたりと柔軟性がほしかった
  45. Appium serverの問題 traceファイルの保存先 Instrumentsでの計測自体はHeadSpinのサーバーで実行されている → traceファイルを転送する必要がある • 転送先を考える必要性 ◦ 転送の仕組み自体は

    Appiumで実装されている Xcodeのバージョンの問題とを考慮してJenkins上でAppium serverを建てる方法を採 用 → HeadSpinは端末のみの利用に
  46. Instrumentsの問題 Instrumentsの問題点として計測結果を定量的な数値で取得する方法がない (実際にはある程度とれるもの*はあったがこの時点では存在は知らなかった) そのため、結果の確認はInstruments appを開いてGUIで結果を確認する必要がある • 30分の計測ともなるとファイルサイズ(150〜200MB程度)も大きく、開くのに時間が かかる ◦ 1分くらいかかることも

    ... • 毎回手動で結果を確認するのはかなり面倒 *TraceUtility(https://github.com/Qusic/TraceUtility )
  47. Nominal → Fair → Serious → Critical

  48. Nominal → Fair → Serious → Critical 各stateの開始時間と終了時間が表示される

  49. CPU使用率の推移はグラフでのみ表示される 詳細に表示されるのはどの処理が何%CPUを利用しているか グラフの最大値がその回の一番使用率が高い値になってしまう (全く同じグラフだったとしても同じ使用率とは限らない)

  50. Instrumentsの問題 XCUITestを用いた強引な解決策 • XCUITestはInstrumentsを操作することもできる • XCUITestでtraceファイルを開いてThermal stateとCPU使用率の部分でスクリー ンショットを撮影、Slackに投稿させた • Thermal

    stateについては文字列での情報も取れるためSeriousまで行ったら失敗 させるといったこともできる • CPU使用率については厳しかった
  51. Instrumentsの問題 Slackへの投稿

  52. 検討した上での今回の構成 • 計測ツール: Instruments(Appiumから利用) • 最新のアプリ: Bitriseでビルド • 実機: HeadSpin

    • CI(計測): Jenkins • アプリ操作用のUITestフレームワーク: Appium • 負荷をかけるツール: お手製 複数のツールに跨っていて終わったら次、といった感じにピタゴラスイッチみたいにに なってしまった
  53. Bitrise 1. releaseビルドでipaを生成 2. development証明書でresign

  54. Bitrise 5. Jenkins jobトリガーAPIを実行 Jenkins 3. dSYMをArtifacts化 HeadSpin 4. HeadSpin

    APIを使ってipaを配布
  55. Bitrise 7. 負荷ツールのDL、Appiumサー バーの起動等の準備 Jenkins HeadSpin 6. HeadSpinの端末に接続 Appium サーバー

  56. Bitrise 8. 計測開始 9. 計測完了 Jenkins HeadSpin Appium サーバー

  57. Bitrise 10. traceファイルをXCUITestで操作 して結果を取得 Jenkins HeadSpin Appium サーバー trace ファイル

  58. Bitrise 11. traceファイルのArtifacts化 12. 結果のSlackへの投稿 Jenkins HeadSpin Appium サーバー

  59. 処理の流れ(概要) 1. Bitriseでreleaseビルドでipaを作成 2. ipaに対してdevelopment証明書でresign 3. 最終的なipaをHeadSpinにAPI経由で配布、dSYMをArtifacts化 4. BitriseからJenkinsのトリガー実行 5.

    Jenkinsで負荷ツールのDL、起動、HeadSpinへの接続等の準備 6. 計測開始 7. 計測完了 8. 結果のtraceファイルをXCUITestを用いて結果のSSを撮影 9. 結果をSlackに通知、traceファイル等のArtifacts化
  60. そしてJenkinsfileが長くなる 処理が多くJenkinsfileがどうしても長くなってしまうので可読性等 が低い 前述の流れの中でさらにいろいろやっている デバッグもしづらくメンテナンスがしづらい問題はある

  61. 実際に運用してみて

  62. 実際に運用してみて 実際に運用をしていく中で想定していた問題や、新たな問題が発生 • Appiumの不安定さ • 実行パターンの増加 • 季節 • インカメラへの利用

    • 結果確認の方法
  63. Appiumの不安定さ 負荷をかけ続けた状態で一定時間経つと、connection errorが発生する 最後の最後でInstrumentsが完了できないということも • 本来の使い方ではないので仕方が無いといえば仕方がないのかもしれない • Appiumはserverとクライアントで通信をしながら操作しているため起きた問題かも • 詳細な原因はわからずじまい

  64. Appiumの不安定さ 採用理由がHeadSpinでAppiumサーバーが用意されていて便利だったから HeadSpinのAppium serverを利用しなくなった時点でAppiumである必要性がほぼなく なっていた → であればXCUITestでもいいのではないか • 現在はXCUITestに移行済み •

    InstrumentsもCUIで実行している
  65. 実行パターンの増加 最初は1パターンx2(30分, 2時間)での計測 新機能追加に伴う色々な状態での計測が求められるように • 端末温度が上昇するためそのまま次を計測してしまうと、前回の結果に引きづられ る正しい計測ができない • 環境にもよるが、最低でも30分は間隔を空けないと実機で前回の影響を受けずに 正しい計測はできない

  66. 実行パターン 計測 クールタイム 計測 計測 クールタイム 計測 端末A 端末B 端末数を増やして対応(どの端末が何分待ったかという調整は必要)

  67. 季節 外部気温の変化によるThermal stateへの影響 • 実装時は冬〜春 • 運用中に夏に • 同じ条件でも外部気温の影響でよりはやく端末温度の上昇が起きた ただこれはユーザーにも同じことが言えるため、夏でも問題ないようにパフォーマンスを

    良くするべき
  68. インカメラの利用 計測パターンでインカメラに人の顔を写した状態で計測する必要が出てきた HeadSpinの場合は同一の端末を契約単位で利用している インカメラに特定の画像を写し続けるといった特別な対応も行ってくれる → HeadSpinのおかげで解決

  69. 結果確認フローの問題 Slackに投稿しているとはいえCPU使用率に関しては実際に開いてみないとわかりづら い問題がやはりある • 結果として毎回100MB超えのファイルをDLして、起動に時間がかかり...を手動で チェックする必要があった • CPU使用率に関してはGUIですら明確な数値が出せない • グラフも最大値に合わせて上下してしまう

    ◦ 定量的に判断が難しい • まだ未解決
  70. 運用した結果の変更 • 計測ツール: Instruments CLI • 最新のアプリ: Bitriseでビルド • 実機:

    HeadSpin(端末数が増えた) • CI(計測): Jenkins • アプリ操作用のUITestフレームワーク: XCUITest • 負荷をかけるツール: お手製 その他細かいところは調整している
  71. これからの展望

  72. xctrace Xcode12からInstrumentsコマンドがdeprecatedとなりxctraceが追加された • 基本的にInstrumentsコマンドでできていたことはできる • traceファイルからXML形式でデータが取得可能 • Thermal stateが取得できることは確認済み •

    CPU使用率については現時点では難しいかもしれない
  73. Bitrise(MacStadium)への移行 当初2時間での計測も考えていたが、今は30分の計測のみに変わった • 結果として90分制限に縛られなくなったためBitriseへの移行も考えている • Instrumentsでの計測にそれなりのスペックがないと不安定なのでそこだけ心配 一方でJenkinsもオンプレからMacStadiumへ移行している • Mac miniを借りれるクラウド

    • MacStadiumだと再起動が出社なしで可能
  74. 他項目の計測 今計測している項目以外でも計測したいものが出てきている そのため現在、FPSなどの新たな項目での計測の準備をしている そちらはBig Queryにデータを入れてData studio等で可視化を検討している このパフォーマンス計測もそこに合わせてデータを可視化したい

  75. さいごに 今回紹介した計測方法はあくまでも一例 アプリの特性や何を計測したいかでも変わってくる アプリの特性にもよるが、パフォーマンスがまだ問題になっていないのであれば MetricKitから導入してみるのがいいかもしれない そこから改善のサイクルを回していきましょう

  76. DeNA Tech の Twitter アカウントでは、 DeNA のエンジニアリングに関する 登壇資料やブログを紹介しています! 後日ブログで紹介しきれなかったところを書くの でその告知もあります

  77. ありがとうございました!