【REALITY】アプリ起動時間を最大60%カット!?【概要/サーバ編】
【祝】6/21のアップデートでREALITYの起動時間が短くなりました!!
今回の記事ではアプリの起動高速化に関して以下章立てでまとめていきます。執筆者は駆け出しエンジニアのうすぎぬです。今回はPM(企画担当)兼サーバエンジニアを担当しました。
なぜアプリの起動を高速化するのか
さて、早速ですが、android developperの起動時間についての記事にアプリの起動時間についての金言があったのでご覧ください
なるほど。起動の遅いアプリはユーザに失望され、起動の遅さはユーザの離脱に繋がるんですね。
まあ確かに、Webページを開こうとしたら何秒もロード画面のままで一向にページが表示されないのでブラウザバックした。という経験はありますね(多分読者の皆さんにもありますよね)。そしてこれと同じことはアプリにも言えそうだ〜。
また、Webページのロード時間と訪問者の直帰率(離脱率)の相関を調査した研究結果を見ると、ロード時間が3秒以上になると急激に直帰率が上昇することが確認できました。これはWebページの例なのでアプリに全く同じことが言えるかというと微妙ですが、アプリのスプラッシュ(起動時にロゴが出てるやつ)の長さもユーザに同様の影響を与えるだろうなと想像がつきます。
と、いう感じでアプリの起動時間が早いことはアプリにとって重要な要素の一つであり、これは全てのアプリ開発者が意識すべきことと思います。実際に、アプリの起動高速化について検索すると数多くのアプリが起動高速化の取り組みをしていることがわかりますし(Dropboxの例は面白かった)、FacebookやInstagramを開くと爆速でホーム画面まで到達している様子を確認できます。(きっと泥臭いエンジニアの努力の上に実現していることでしょう。)
そしてREALITYの起動時間はどうなんだ、というと(俺環境での計測)
Oh my god... まあ、REALITY iOSアプリは特に起動時にUnityライブラリを初期化していたりするので(ゲームアプリに近い)、他のSNSアプリや動画配信アプリと比較するのはどうなんだという気持ちはありつつも、更にアプリを拡大させたいという勢いの中これは由々しき事態ですね。
というわけで、起動時間短縮を目標にプロジェクトがスタートしました。
調査・課題設定
まずは、起動処理に問題があるのか?あるとしたらどういった改善が期待できるのか?を洗い出すことが必要ですね。ということで、iOSとAndroidの両OSについてアプリの起動フローの調査からスタートしました。
ここではiOSアプリの自動ログイン時の起動タイムシーケンスを紹介します。iOSエンジニアさんに「アプリアイコンタップから配信一覧表示まで」のタイムシーケンスを出してもらいました。
軽く流れを紹介すると、
1. ライブラリ等の初期化 (最初の空白1.8secのところ)
2. 自家製Unityライブラリの初期化開始
3. 2と同時に 認証API->ログインAPI への request
4. 配信一覧取得API への requestと配信一覧の初期化
という流れになります。タイムシーケンスを眺めるだけでも
・ライブラリの初期化が遅い
・認証APIとログインAPIの間に謎の空白時間がある
・配信一覧取得APIのレスポンスが遅い
などなどの問題を洗い出すことができました。企画時点で上記問題を解決したらこうなるな!!理想的にはこうだろ!!2秒は短くしたい!!という画像を作りました↓(切り貼りして画像編集しただけ
要所に差はあれど、概ねこれと同様の内容をAndroidでも実施するとして起動高速化プロジェクトの実装がスタートしました(途中で配信一覧をログインAPIのレスポンスに混ぜようとかいろいろ変更はありました。)
また、「アプリの起動時間周りのログを全く仕込んでいないためにユーザのアプリ起動時間を監視できない」という課題も見つかったので、監視のためのログを実装する計画などもここで盛り込みました。
事例紹介
前章で挙げた問題を踏まえて、本プロジェクトでは以下項目について取り組みました
1. 配信一覧取得APIのレイテンシ改善
2. アプリ起動周りのトレースログの実装
3. アプリ初期化処理の最適化 / API requestタイミングの最適化
4. Unityライブラリ初期化の高速化
本記事では1,2についてまとめます。(クライアントの詳しい解説は別記事で!
配信一覧取得APIの高速化
以前より、配信一覧取得APIのレイテンシが高いことはサーバチームの悩みでした。ログイン処理にも関わっていることはもちろん、配信一覧の読み込みが遅いことはUXの低下に直結します。また、サーバチームのKPIであるレイテンシ 99 percentileの悪化の原因であり、サーバ費用増大の要因にもなっています。
つまり、配信一覧取得APIの軽量化/高速化はアプリのUX改善、サーバ負荷軽減に繋がり全員ハッピーになります。改善せねば・・・!
というわけで、配信一覧取得APIが今どうなってるのかを確認しました〜!
(↓の図はリクエストが届いてから、返すまでの大まかなフローです。)
え、マジ・・?全部直列か・・・今までよくこれで配信一覧返してたな・・・。それは遅いわ。これはどうにかするべき。とりあえず並列処理を入れていこうかな?
ということでこうじゃ!としたのが↓の図
REALITYサーバはgolang製ですが、golangといえばgoroutineによる並列処理が花形ですからね。エンジニアブログなのでこれ見よがしにコード載せときます。
fetchを行う関数(xx取得)をすべて type FetchResourceFunction で定義し、それを全て並列で実行するコードのサンプルです。
// FetchResourceFunction internal serviceへのfetchを行う関数の型
type FetchResourceFunction func() error
type FetchResourceFunctions []FetchResourceFunction
// fetchHogehoge hogehogeをfetchする
func fetchHogehoge() error {
// internal serviceへのrequest処理など
}
// fetchResourcesInParallel 並列実行する
func fetchResourcesInParallel(funcs FetchResourceFunctions) {
var wg sync.WaitGroup
for _, f := range funcs {
wg.Add(1)
go func(fetch FetchResourceFunction) {
defer wg.Done()
fetch()
}(f)
}
wg.Wait()
}
//FetchResources 必要なデータを一括取得する
func FetchResources() {
// 並列でfetch
fetchFunctions := FetchResourceFunctions{
fetchLiveList,
fetchFollowList,
fetchFollowerList,
fetchRecommendUserList,
fetchGameList,
fetchHogehoge,
fetchFugaFuga,
// その他いろいろここに詰める
}
fetchResourcesInParallel(fetchFunctions, targets)
}
そしてさらに、ここから巨大なjson structのparseの高速化や、処理の簡単化など細かいチャレンジをしました。
効果測定ですが、REALITYのサーバはGCPで運用していますので、StackDriver Traceでレイテンシの推移を観察することになります。青が改善前、橙が改善後のレイテンシのヒストグラムです。だいたい 40% 程度レイテンシをカットしたことを確認できました!(数値伏せてすまん感)
そして、起動時初期化用にファーストビューの「おすすめカテゴリ」のみを返す実装などを入れて、最終的にログインに必要な配信一覧取得APIのレイテンシを従来より 80% 以上カットできました。
起動時間のトレースログ実装
さて、サーバ・クライアントともに改善を入れましたが、なんとREALITYにはユーザのアプリ起動時間を監視するログが実装されていませんでした。困った。。。。
(増住さん:iOSチーム。インフラからクライアントまで全部見てる人。2020年CEDEC登壇などの実績あり。)
おっす、あざす!調べてみるとアプリバージョンごと、国ごとに切り分けてログを表示したり、percentile別に落としたログを表示できるっぽい!さらにはBigQueryのエクスポートなんかもサポートされてる!神!
ということで、今回のプロジェクトでFirebase Performance Monitoringを用いた起動時間周りのカスタムトレースログを入れることにしました。ちなみにREALITYではPerformance Monitoringの積極的な利用はこれが初めてでした。
特に、今回はアプリのスプラッシュの裏で走っている起動処理を
・アプリ起動時間(デフォルトでログ落ちてる、詳しくはここをクリック)
・Unity初期化区間
・配信一覧初期化区間
の3区間に分けてカスタムトレースに落としました
で、起動時間改善が入ったアプリリリースの週のログがこれです!!
じゃじゃーん!!めっちゃ早くなっとるー!すっごーい!!
はい。て感じでユーザの起動時間を追うことが可能になりました。
まあ、細かい話ですが配信一覧初期化区間は、レスポンスををログインAPIに混ぜたりしてるので 89% ものカットを実現できたという経緯があります。
そして、このログを入れたことによる良い(?)エピソードが早速生まれたので紹介します。
これはとある日のAndroidの起動時間周りのカスタムトレースのログですが、突如起動時間が悪化した様子を確認できました。
これはAndroidの4.28.0あたりでいれた起動処理周りのリファクタの影響で悪化したものでした。当然、検知後は即座に修正を入れて修正版をリリースしましたが、カスタムトレースログを実装しなければ検知できなかったことでしょう。
ログ実装では、単にユーザの起動時間を監視できるようになったこと以外に、ログを入れることで継続的に起動時間周りを監視できる環境が整ったという観点で非常によかったと思っています。
追記:起動時間のDashboardを作ってもらって、6月のログ眺めたら改善具合が良い感じに可視化されていたので載せます。縦軸が起動時間(秒)です。(再:数値伏せてすまん感)
締め
REALITYでは新規機能開発やガチャのリリース以外にも、UX改善を目的とした改善施策が多く走っています。特に、自分はREALITYが言われがちな「よわよわサーバ」を脱却するために、この半年はAPIのレイテンシ改善をバシバシ入れていきました。そして、ユーザから「あれ、REALITYめっちゃ早くなったな」の声が上がるのを期待しつつ、これからも続けていきます。
そんなREALITYでは改善系やSRE的なポジションのエンジニアを大募集中です。サーバ、クライアント問わず、興味のある方は一緒に働きませんか?
(残ったクライアント実装のお話は、後日多分本エンジニアブログに上がります