Performance Team, SRE and Android as Distributed Systems

Android には Performance Team というのがあって、「なんか遅い」みたいなバグに対してアプリ開発者が音を上げると登場して triage して責任者を突き止め、場合によっては自分で OS を直して帰っていく。最初は懐疑的だった自分もなかなかの仕事ぶりに評価を改め、いまは頼りになる人たちだと思っている。サーバ側でいうところの SRE みたいなもの、というとわかりやすい。

なんでクライアントサイドに SRE (的な仕事)がいるのかというと、結局システムとしての Android には分散システム的な側面があるからだと思う。Binder は RPC みたいなもんでたまに congestion を起こすし、ハードウェアの故障はともかくプロセスは LMK で死ぬ。CPU もメモリもいつも足りておらずプロセスは資源を奪い合っている。本物の分散システムとの大きな違いの一つは network partition がないところだけど、OS 本体はともかくアプリから見ると offline 状態というのは network partition みたいなもので、それなりに graceful に扱わないといけない。

そしてなんか電話機/アプリが遅いというとき、それはしばしばシステムとしての問題である。すなわち overload や contention のような system-wide で inter-component な問題が遅さを引き起こしている。もちろんアプリの出来が悪いだけということも多いけれども。

というわけでクライアントサイド OS の開発組織に SRE じゃなかった Performance Team があり、そうした system-wide の性能について日々考えているというのは、まあまあ理にかなっている。


クライアントサイドの性能問題は、いくつかの点でサーバサイドより簡単である。

まずなんだかんだで本当の分散システムではない: 多くの問題は単一デバイスに閉じている。だから理論上 OS は system-global view を持っている。もちろん production には millions of devices があってそいつらには long tail の問題があるわけだが、少なくともデバイス同士が干渉しあうことは、そんなにない。(クライアントであるデバイスが送るリクエストがサーバ側のレイテンシを生み出すことはよくあるが、ちょっと脇においておく。)

あと、突然の高負荷でシステム全体がダウン、みたいなことも起こりにくい。なのでふつう pager で呼ばれたりはしない。まあサーバサイドで flag flip をしたら突然アプリがバタバタと死にはじめることはあり、そういうときはアプリの人も呼び出されるわけだけれども、flag flip は昼間やってちょうだいね、ということで。

一方、サーバサイドにない難しさもある。

まずなんといってもモニタリングがヘボい。これは半分は本質的な問題で、半分は特定の実装のできの悪さだと思う。

本質的な問題。モバイルデバイス、とにかく計算資源がない。CPU 遅い、メモリ少ない、ストレージ少ない、ネットワーク帯域細い。なんでとりあえず Prometheus の agent をインストールしましょう、というわけにはいかない。モニタリングをするにあたってもかなりケチくさくやらざるをえない。

実装のへぼさ。 つまり、そうはいってももうちょっとなんかないの?というね・・・。Performance Team もなんらかの clue がないとできることは限られてしまうし、ましてアプリ開発者(自分)はもうわけわかんないログ睨むの疲れたよ・・・。ただ最近は Perfetto というプロジェクトでそのへんをなんとかしようとしているらしいので、首を長くして待っている。

クライアントサイド固有の難しさそのに。Production の制限の多さ。要するに我々は SRE と違って世の中の電話機に adb したりできないのである。いや、できたら困るんですよ。わかってますよ。でも出来なくて困ることもあるんだよ!Systrace できないじゃん!みたいな話です。

まあ Lambda や App Engine とかも開発者はコンテナにアクセスできないので似たような面はある。すると app-level の instrumentation がいよいよ重要になってくるため、結局は計算資源がボトルネックといえる。

クライアントサイド固有の難しさそのさん、といえるかどうかはわからないが違うところとして、ロードが burst 的というのがある。だから広い sliding window で time series の log をとる、とかやると重要な情報すなわち burst/spike を見逃してしまう。これは実際に時系列のデータを見て意見してるわけではないので間違いかもしれないが・・・。

スマホ、大半の時間は寝て過ごしている。画面をつついている時間も大多数は平和なものだ。アプリを起動する瞬間、データをロードする瞬間、カメラのシャッターを切る瞬間、なんらかのデータを submit する瞬間、モードを切り替える瞬間など、ほんのゼロコンマ数秒から数秒の間に起こる色々な細部が重要で、その数秒に問題があるとアプリが固まったり描画が乱れたりする。これはたくさんのクライアントから並列でリクエストが飛んできて結果として全体のマクロな負荷は平滑化されて見えるサーバの皆さんの典型的なロード特性とは異なる。

Burst 的、別の言い方をするとロードが時系列方向に heterogeneous である、と言えるかもしれない。なんでサーバサイドと同じようなモニタリングしてもダメだと思うのだよな。ではどうするか、は、アプリ次第だけれども、system-wide でいうと AcitivityManager や LMK の活動は重要な指標になりうる。Historian はそのへんよくできており、こいつやるな、と思う(エラそう)。

他の切り口でいうと、homogeneous な負荷の世界ではスタックトレースのサンプルをソートした framegraph が重要で, 負荷の heterogeneous な世界では普通の時系列 trace が重要。クライアントサイドでも、たとえば IPC のスループットをなんとかしたい、みたいな話になると tracing より framegraph が役に立つ。むかし Chrome の IPC 載せ替えを手伝っていたときは tracing より perf と framegraph が友達だった。

とにかく Android アプリは app-level および system-wide 双方が structured  で resource efficient な continuous かつ in-production の instrumentation をがんばらないといけない、ということです。もっとかっこいいツールとかダッシュボードとかみながらタタターンってかんじで性能改善したいじゃん。させろ!