2013年5月25日土曜日

performance analytics in android

最近、全くブログ書いてなかったのでたまには更新しようと思います。
一回書くと満足してまた書かなくなりそうなので、何を書くのかある程度宣言して自分にやる気を出させようかと思う。
前々から、Androidについては、まとめたいとは思っていたことがいくつかあるのでそれをあげてみると
  • パフォーマンスの計測と改善について
  • メモリー、通信量、CPU利用率の計測
  • ファイルダウンロードとそのキャッシュ
  • 通信などのI/OとAsyncTaskについて
  • アプリケーションを作成する際のThread構成とかとHandlerについて
  • google-guiceについて
といった感じ。最近だとDrawerLayoutとかかな。順番はばらつきそうだけどそれぞれについてなんか書く。

今回は、パフォーマンスの計測と改善について書こうかと思う。
Androidの開発に限らないと思いますが、最初はサクサク動いていたけど進めていくうちに重くなってきたりすることあると思います。最初は軽いからいいやと思って放置していたら、いろんな人の修正が積み重なった結果、気がついたらとんでもないことに。。。とかとか。
そうなる前に日々計測をしていたいところですが、ガンガン作り込んでいる時には、そうもいかないもんです。ある程度つくったら見直せばいい気がします。

パフォーマンスの計測にはandroid.os.Debug.startMethodTracing()を使って計測します。
こんな感じでコンテンツをgetする時にUserAgentをWebViewと同じものを指定したいときもあると思います。まあ、あんまないと思いますが。
そこで、AsyncTaskを作るところから、コンテンツを取得するところまでを計測してみる。
結果は、
$ adb pull /sdcard/profileSample.trace .
$ traceview profileSample.trace
を実行してtraceviewで結果を見る。
分析する場合には、まあ、上から順番に詳細を見ていくしかない訳です。基本的にはAndroidのフレームワーク部分は無視しても良いと思います。どうしても必要な処理があったりもするし、フレームワーク側で時間がかかる処理があったりもするが、それが問題になる場合には改善すべきはアプリケーション側からのそのコードの呼び出し回数やタイミングであるからだ。
パフォーマンスで気になるのは、目的にもよるけど大抵はUI threadでの実行についてなので、UI Threadでかつアプリケーション側の処理で一番時間がかかっているのを探す。今回は2.3.3のemulatorで実行してみた。
時間がかかっているのはgetUserAgent()であることが分かる。ていゆか、上のコードだとほぼそれしか関数がないので、当たり前なんだけどね。
で、その関数内で何が行われているのかをみるとほぼすべてWebViewのインスタンスを生成に使われていることが分かる。
この関数ではUserAgentさえとれれば良いわけでWebViewのコンテンツを表示させたい訳ではないので、改善しようと思ったらWebViewをインスタンスかしないでUserAgentを取得する方法があれば良い。API Level 17以上であればgetDefaultUserAgentてのがあるが、それ以下だとだめなのであきらめる。というのではなく、WebViewのソースを見たりとかしつつ、インスタンス化の回避策を探すとreflectionを使えばいけるのである。そこでgetUserAgent2()とかいう関数を作ってみる。
で、同じように計測した結果が、以下になる
これによって、だいぶUI Threadでの処理が節約できているのが分かると思う。
android.os.Debug.startMethodTracing()自体は、計測の度に数値が変わるので全体の傾向しか調べることしか出来ないが、Incl CPU timeが1/7くらいにはなっている。ちなみにInclは、関数内で実行されている他の関数も含めた全体の実行時間になる。要はEnter/Exitの間の実行時間になる。
こんな感じで、実行時間がかかっているところの一番ざっくりした関数を探して、その中にどんどん潜っていきつつ。処理の代替手段を探すのがよい。そもそも設計が間違っていたりする場合もあるが、たいていは代替手段を探すことになると思う。ここで上げたようにstaticな情報であれば大抵は、reflectionを利用することでインスタンス化をさけたりできると思います。

ここでの例では、WebViewを生成しないことによって、CookieやWebViewWorkerのthreadを生成していないので、コンテキストスイッチが走らない分早かったりもする。

0 件のコメント:

コメントを投稿