こんにちは、私の名前はVladimir Pchelyakov、私はinDriveのiOSエンジニアです この記事では、アプリの起動時間が会社の収益にどのように影響するか、どのように私たちは私たちのアプリを加速させたかについて話し、ツール、アプローチ、実際の状況ともちろん結果をカバーします。 この記事は、アプリの起動時間とユーザーの重要な経路を加速させたいチームのプランとして役立つことができます、なぜなら、我々が解決した問題は、ほとんどの非最適化プロジェクトに存在する可能性が高いからです。 文脈のために、inDriveアプリでは、乗客としてタクシーを注文したり、ドライバーとしてオーダーを取ったり、ドライブをすることができます。 この記事では、私はカバーします: スタートタイムが会社の収益にどのように影響するか App Launchの種類 App Launch Time、TTI、Hang Time 平均値とパーセント スタートタイムに影響するもの App Acceleration サイクル 加速するための最も効果的な方法 ツール、プロセス、状況 結果 スタートアップ時間は会社の収益にどのように影響するか ほとんどの人は、開発者が「実質的な価値をもたらさない」最適化にあまりにも執着していると考えていますが、データでは、何百ミリ秒で物事を加速させることは、新しい機能を立ち上げるか新しい市場に参入するのと同じくらいビジネスに影響を与える可能性があります。 inDrive 研究 会社内では、旅客の重要な経路を750msで加速するとオーダー数が1.5%増加することを示した大規模な研究を実施しました。 デロイト研究 デロイトはいくつかの非常に印象的な数字を発表しました: 100 msでモバイルウェブサイトを加速すると、電子商取引での変換率は +8.5%、旅行での変換率は +10%です。 さらに、一部の企業は、アプリを特定のユーザーグループのために意図的に減速させ、仮説を証明する:アプリがより速く起動するほど、ユーザーがより多くの注文を作成します。 App Launchの種類 アプリの起動の種類はメトリックに直接影響するので、それらを区別することが重要です。 Apple は、リリースタイプを決定するための直接の API を提供しませんので、各チームは独自の方法でそれを行います。 冷たい打ち上げ アプリの完全なダウンロードは、起動の最も遅いタイプです。アプリのプロセスは存在しませんでした、キャッシュはありません、すべてはゼロから初期化されます。 Cold Launch はバージョンごとに比較して結論を出すことができる唯一の正直なメトリクスです。 温かい打ち上げ アプリは最近終了したため、プロセスは存在しません。しかし、アプリの一部は部分的にメモリに残ります - システムキャッシュとダイナミックリンクのキャッシュはまだ温暖です. システムは新しいプロセスを生み出し、ランタイムを再起動しなければなりませんが、冷たい打ち上げで必要なI/O作業の多くを省き、顕著に速くなります。 これは、メモリからアプリをダウンロードし、ほぼすぐに再び開くときに起こり、顕著に速く開きます。 熱い打ち上げ アプリは単に最小化され、再び開いた。ここで測定するものは何もありません、アプリはすぐに開き、いかなる道も通過しません、すでに正しい場所にあります。 初心者 iOS自体は事前にアプリを「バックグラウンドで」開始し、ユーザーがこのアプリをすぐに開くことを予測します。これは役に立つように聞こえますが、それは多くの問題を引き起こします:私たちは15分と10時間の「リリース」を持っていたので、プレーマックは事前にプロセスを作成し、メインファイルで作成された変数から時間を計算しました。 最初は、エラーがどこにあるのか、どのようにして打ち上げ時間が時間に等しいのか理解できず、他のデータがMSで測定され、時間の指標が実際に画像を混乱させたため、統計もひどく損なわれました。 気をつけてください、プレアムは統計を完全に台無しにすることができます。 App Launch Time、TTI、Hang Time 物事を加速させる作業を始めたとき、誰もが基本的な用語さえ違って理解していたことが明らかになったので、共通の定義を開発しなければならなかった。 App スタート時間 重要:これはすぐに表示されるシステムのスタートスクリーンではなく、ルートビューコントローラーについてです - ライブラリリンクが完了した瞬間、AppDelegate方法が終了し、ビューコントローラーが作成され、ビューDidAppear方法が起動しました。 TTI(Time to Interactive) アプリのアイコンをタップするまでの時間は、ユーザーがアプリと相互作用できる時までです。これは、注文の作成や処理の開始に影響を与え、ビジネスメトリックに影響を与えるため、重要なパスと呼ぶこともできます。 For example in our case: 乗客にとっては、地図が表示され、ユーザーは「どこへ行くか」をタップすることができます。これが起こるほど、乗客が注文を作成する可能性が高くなります。 運転手にとっては、乗客命令の画面が表示される場合、これが起こるほど、運転手が時間通りに命令を確認し取る可能性が高くなります。 HANG タイム これは、インターフェイスが「凍結」する時 - ボタンがタップされますが、行動は瞬時に起こらない、例えば、それは1秒かかります。通常、これはメイントレードの重い計算の結果です。そして、はい、ハングタイムは直接TTIに影響を与えます、なぜなら、ユーザーがボタンをタップしたときにだけでなく、重要な経路の何かがメイントレードを占拠し、UIがレンダリング中に凍結するときにも凍結が表示されます。 平均値とパーセント値 何百万ものユーザーからデータを収集した後、あなたは何らかの方法でこれらの指標を解釈する必要があり、少なくとも2つのオプションがあります:平均値とパーチル。 なぜ平均は全体像を示さないのか 平均は便利で計算しやすいように見えますが、時には結論を出すことは困難です。 以下は一例です。 我々は5ユーザーと5ユーザーの起動時間 = 1, 2, 3, 9, 12 秒。 平均点は、5.4秒。 But this does not reflect reality: 5人のユーザーのうち3人は3秒以内にアプリを起動します。 2 ユーザーは非常に遅く、統計全体を歪めます。 そして、1回の打ち上げが「1時間」として記録される場合(またはメトリックエラーのせいで無限が来る)、平均は完全に無意味になります。 パーセントイルはどのように機能し、なぜそれらがより正確であるのか パーチルは、アプリがその時間内に適合するユーザーの X パーセントのためにどのくらいの時間を必要とするかを示します。 同じ5つの値: 1, 2, 3, 9, 12 50th percentile = 3 秒 → ユーザーの半数が 3 秒またはそれ以上でアプリを起動します。 80th percentile = 9 seconds → ユーザーの80%がアプリを ≤9秒で表示します。 99th percentile = 12 seconds → the "heaviest" users: weak devices, poor internet. 「最も重い」ユーザ:弱いデバイス、弱いインターネット。 50番、75番、90番、95番のパーティイルを参照します。ユーザーの主な量に加えて、我々は常に95番のパーティイルを加速させ、アプリが最も長くロードしている人々の生活を改善しようとしています。 By the way, I will note that we operate in 47 countries around the world, and we have many countries where many users have weak devices and poor unstable internet. What Affects Startup Time アプリを大幅に遅らせることができる主な領域を見てみましょう。 ネットワーク要求 チェーン内のリクエストの数、リクエストの順序、キャッシュの能力、インターネットの速度。 App 初期化 スタートアップでは、図書館リンク、Objective-C ランタイム、Swift Reflection、SDK 初期化(分析、広告、Firebase)、ロード dylib、dild、rebasing、シンボル結合、DI コンテナ初期化など、さまざまな操作が行われます。 主な脅威 メインストリーム上の重い操作はTTIを増加させます: JSONの大規模で複雑なモデルのデコード、同期通話、不要な計算は「スタートアップ時に」便利だったため、それらは別の場所に移動することができます。 デバイスの特徴 私たちは古いデバイスを加速することはできませんが、それらのための論理を調整したり、負荷量を減らしたりすることができます。 興味深い事実:弱いデバイスと遅いインターネットを持つ国では、MetaはFacebook Liteアプリをリリースしました。 App Acceleration サイクル アプリをスピードアップするには、「目で物事を直す」のではなく、体系的に測定し、改善する必要があります。 私たちのサイクルはこんな感じです: Mark up the app for data collection App Launch Splash TTI of all verticals (critical paths) Steps of the critical path Collect and visualize data Raw telemetry data Long-term storage, Big Query モニター Xcode Organizer Find places of slowdown Instruments: LaunchTime, Profile, Network, Hangs Timeline analysis Fix problems Restructure requests Cache Offload the main thread Remove everything unnecessary from startup 実験走行 結果を比較し、仮定を検証する Control startup time before a new release UI-performance tests on CI, alerts on degradations このようにして、バージョンごとに、アプリはより速く、より安定し、より予測可能になります。 加速する最も効果的な方法 最大の効果は、いくつかの明らかなしかし体系的に実行されたステップから来ています。 changing the order of network requests Caching Network リクエスト 主なトレードをダウンロード Startup で重いコンピュータを削除する方法 これらは私たちに最小限の努力と何かを壊すリスクと最大限のTTI改善を与えた変化です。 ツール、プロセス、状況 この章では、私たちのツールについて話し、いくつかの実際の例を参照し、その中で、1つのツールが他のツールをどのように補完し、それぞれがどう見えるかを示します。 測定なしでは最適化は不可能です. まずプロジェクトをマークする必要があります. 私たちのアプリ内では、主要なスタート段階がマークされています: AppLaunchTracker — 最初のコントローラが表示されるまで、アプリを起動します。 SplashTracker - さまざまなスプレッシュスクリーン段階の時間(機能トグル、プロフィール、場所の決定) CriticalPathTracker - ユーザーの重要なパスとステップ分解とそれらの間の時間 VerticalsTTITracker — TTI for each product vertical. VerticalsTTITracker — TTI for each product vertical. VerticalsTTITracker — TTI for each product vertical. All data is visualized in charts and dashboards (Kibana, Redash). さらに、我々はプロセスを確立しました: パフォーマンス比較版 CI での UI パフォーマンステスト 劣化に関する警告 定期的な週会議と加速するためのロードマップ ネイティブツールを使用する: Xcode Instruments - LaunchTime、プロフィール、ネットワーク、ハングス Xcode Organizer - Hangs, App Launch また、タスク実行の順序を理解するためのロードマップを作成することをお勧めします。 今、行動中のすべてを見ていきましょう。 ネットワーク リクエスト オーダー TTIを加速する最も効果的な方法の1つは、ネットワークリクエストの順序を再編成し、並行して独立したリクエストを実行することです。 For analysis we used Xcode Instruments → Network, where you can clearly see what requests are going and their dependencies. This is faster than digging through the codebase. 私たちがしたこと: ran the specific flow ネットワークリクエストの痕跡をキャプチャ overlaid the moments when screens appeared on it その後、製品チームへ行き、結果を示し、ネットワークリクエストの順序を最適化する方法を議論し、タスクを作成しました。 シンプルケース 最初のスクリーンが依存していたリクエストは遅すぎて送信されました。実際には依存性がないため、スクリーンライフサイクルの始まりに移動する可能性があります。結果として、ユーザーはより早く準備されたインターフェイスを表示し、TTIを速くすることを意味します。 Androidでは、古典的な「階段」を見つけました。 toggles → driver config → reasons → offers すべてのリクエストは厳密に順次に行われた。その後、注文のロードが始まった。 解決策: 複数のリクエストを 1 つに組み合わせるか、依存性がない場合にリクエストを並行して実行します。 最もシンプルで最も効果的な方法は、それを必要としていない場所で人工のシーケンスを削除することです. 開発者がこのシーケンスを便利か他の理由のために長い間書いた可能性があります,そしてそれは誰にも迷惑をかけることがなかったため、そのように留まりました. もっと複雑なケース: ドライバー Feed 最初は、ロードオーダーの前に、いくつかのリクエストの連鎖が実行されました: 一般タクシー設定 タクシー運転手の設定 Courier 設定 All of them went sequentially, and each request slowed down TTI, especially on slow internet this was noticeable, since each of the three requests increased the total path time. 我々はチームと協力し、スケジュールを大幅に簡素化しました: 独立要求は並行して開始された。 2 つの設定リクエストを 1 つのリクエストに結合 必要な最低限のデータを受け取った直後に注文の要求が始まりました. そして、稀なケースでは、並行要求を待つ必要がありますが、それでも、それは最初の後にゼロからではなく、最初の1と並行して開始されました。 結果として: 3つの連続的な要請の代わりに、最良の場合には1つがあった。 最悪の場合、以前より1件の要請が少なくなります。 After that we ran an experiment. Experiment result: TTI 加速 平均 0.5 ~ 0.8 秒 95th percentile - up to 2 seconds (第95パーセチル) タイムライン バージョンごとにグラフは、アプリが速くなったか遅くなったことを示していますが、なぜかという質問には答えていません。 この問題を解決するために、我々はタイムラインを使用します - 段階的にTTIの分割. それは、我々は、重要な経路のすべての主要な段階を収集します: app Launch 委任方法 getting toggles 取得場所 プロフィール取得 モジュールへの航行 module initialization screen rendering これにより、正確に見ることができます: どのステージがスピードアップ 回帰が現れた場所 究極のTTIに何が影響したか It is timelines that allow us to safely make large changes and understand their consequences. This also became the basis for decision-making and finding regressions or places for acceleration — the longest pieces interest us the most. これは非常に良い詳細なレベルですが、それらを詳細に、すぐに質の高いように計画するため、後でそれらを変更することは不便であり、以前の分解とグラフを比較することは不可能になります。 「App Launch Report and Removing Typhoon」 Xcode App Launch Report は、どのコードがリリースを遅らせているかを示します。 私のケースでは: DIコンテナ台風は、発射時間の30%を占めました。 いくつかの初期化は、一貫してトップの最も遅い場所に現れた。 台風を取り除くには: 初期段階の打ち上げは、バージョン 5.122 で ~1.3 秒から ~445 ms に約 3 倍短縮されました。 タイムラインでは、最初の段階がどのように3倍に加速したかを見ることができますが、全体的なTTIが大きくなりました。もし私たちが崩壊を持っていなかったら、私たちは何が起こっているのか、DIを削除する方法がどのようにしてすべてを遅らせることができるかを理解しませんでした。 HANG RATE 今では、一つのツールが他のツールを補完する方法を見ることができます - タイムラインは、リリースが加速したイメージを示しましたが、さらに何かが壊れ、いくつかの初期段階に影響を与えました。 アプリの新しいバージョンが大幅に減速し始めたことを示しました. 何かがメインストリームをブロックしていました. ツールは、ハングタイムの92%がサードパーティのSDKから来ていることを示しています. さらに、具体的な線を指摘している。 理由 - サーバーの応答を待つ最大10秒のメイントレードの第三者ライブラリからのネットワークリクエスト - 問題は遅いインターネット上でのみ再生されたので、それは私たちのテスターと開発者によって気づかず、誰もがラップトップやデバイスで高速インターネットを持っているので。 図書館に問題を提出し、修正後、Hang Rate が正常に戻りました。 研究 時々、私たちは異なるデバイスで実行することによって主なユーザーパスをプロフィールし、およそこのように見えます。 ここで我々はマイクロ凍結を発見し、それらがどの重要な経路の段階に影響を与えるかをすぐに見ることができる。 ユーザーの位置で作業するための特定のコードが主要なスレッドを重くロードしていることが判明しました。我々は問題を修正し、ランダムなマイクロ凍結が消え、ハング率がさらに低くなりました。 また、プロフィールのキャッチング段階は、480 ms から 21 ms に短縮されました。 UI パフォーマンステスト CI 問題を見つけて解決することは良いことですが、アプリがApp Storeに移行する前に、またバージョン回帰が始まる前に、遅れについて学ぶことがより良いでしょう。 私たちは、生産サーバー上で実際のシナリオを通過し、生産バージョンと同じメトリックを送信するネイティブUIテストを行いました。 キー画面への実際のユーザーパスを通過します。 夜走り ネットワークモックなしで複数回実行 メトリックをテレメトリーに送る 時間制限を超えると、Slack に警告が表示されます。 これは、現在、システム全体の最も重要な要素であるリリース前に問題を捕獲する能力を与えます。我々はまた、すべての同じタイムラインを見、テストは可能な限り実際の使用条件に近い。 また、当社のキャッシュがうまく機能することを確認するために、我々はCIにインターネットホートリングを追加しました - 最初にテストは正常な速度で実行され、すべてがキャッシュされ、それから3Gの遅いインターネットでも同じことをします。 私の意見では、これはスタートアップのスピードに取り組む上で最も重要な要素の1つであり、そのようなバージョンをリリースすることを許さないことです。 次に、同一のMac Miniでパフォーマンステストを実行して測定を正直に保つため、すべてのプルリクエストでこれを非同期的に実行したいのですが、合併後にこれを行うことができ、どのPRがスタートタイムを破ったかを理解できますので、変更を迅速に取り戻すことができます。 バージョン比較 プロジェクトに新しい依存性を追加する前に、バージョン比較プロセスがあり、開発者がアプリの現在のバージョンと新しい依存性を追加したバージョンを比較する方法について説明します。 一度、このような実行中に、アプリのスタートアップで250 msの増加が見られ、アプリを遅らせないように、この機能を一時的に、一部のユーザーにのみ導入することにしました。 Toggle キャッシュ 最後のケースを見てみましょう - タイムラインを見ると、私たちの道で最も長い段階の1つは、グラフ上のブラックバー(700 ms)です。 我々は進歩的なキャッシュを実装しました: トグルが特定の時間よりも新鮮であれば、我々はキャッシュを使用します。 もちろん、改善を示すA/B実験を実行します。 結果 : 転換段階は、バージョン126~127で削減された。 TTIは、アプリを頻繁に開くユーザーが目立って速くなり、インターネットが遅いユーザーが最も恩恵を受けました。 グラフはドライバーのトグルを示しています. それは非常に頻繁にアプリを開くために小さくなりました. 交通照明とスピードステートコントロール メトリックが多すぎると、集計が必要です. 我々は「トラフィックライト」を使用します。 彼らは毎日複雑な総合式を使用し、改善と劣化の自動的な評価をします. 私はこれらすべてを今、多くの時間がかかるので説明するつもりはありませんが、全体的には、「バージョンで何が起こったか」という質問への迅速な答えのためにすべてです。 たとえば、バージョンの1つでは、TTIの顕著な増加がありました - メインストリームのダウンロードの結果です。 Final Results 半年間の仕事の成果を見ていきましょう。 年初から9月まで(バージョン115 → 140):アプリのリリースから製品垂直への移行までの時間(50番目のパーティール)は、ほぼ3倍短縮されました: 2.8 sec → 1 sec The share of users with launch faster than 5 seconds: 58% → 84% ユーザーの75%が4秒でアプリを起動し、以前は6.2秒だった。 同時に、改良はバージョンごとに徐々に起こりましたが、1つのバグピークを除き、ツールのおかげですぐに捕まりました。 結論 アプリの加速は一度の最適化ではなく、継続的なプロセスです。 クリーンメトリック 理解可能なタイムライン automatic control before release 定期的な分析と実験 そして、最も重要なことは、数百ミリ秒の加速が実際にビジネスに影響を与えるということです。