fresh digitable

めんどくさかったなってことを振り返ったり振り返らなかったりするための記録

頭外音像定位アプリのプロジェクトにandroidx.benchmarkを導入してみた

これです

github.com

androidx.benchmarkは何となく気になっていたので、冬休みの間の宿題として、数年前に作ったあと放置していたリポジトリをほじくり返してこれに導入してみることにした。何をやっているのかは

akihito104.hatenablog.com

にだいたい書いてある。今回はFFTをやっているところと、畳み込み演算をやっているところのベンチマークをとってみることにした。

今回のポイント

公式のドキュメントを参考にしながら、benchmarkモジュールを追加するやつをやった。

developer.android.com

これ以外に、今回のプロジェクトで必要だった作業は次の二つ:

  • サポートライブラリを使っていたのだが、benchmarkはandroidxのライブラリなのでjetifyした。
  • シングルモジュールの構成になっていたのだが、benchmarkモジュールはlibraryモジュールなのでapplicationモジュールのプロジェクトに依存できない。ベンチマークをとりたいコードを別のモジュールに移した。

また、ドキュメントには./gradlew benchmark:connectedCheckをやるとプロジェクトのoutputフォルダに測定結果ファイルがコピーされると書いてあるが、ver. 1.0.0ではコピーするタスク(benchmark:benchmarkReport)で失敗する*1。結果ファイル自体は端末の外部ストレージに保存されているので自分でpullすればよい。

ベンチマークの測定結果と考察

ベンチマークの測定結果ファイルには、かかった時間の最大値、最小値、中央値、各イテレーションの実測値などが書いてある。また、ウォームアップの回数なんかもわかる。実際のファイルは上のPRに張り付けたので興味のある方は実際に見てみてほしい。

これによると、畳み込み演算の実行時間の中央値はFFTのものと比べて8倍程度になっている。想定では畳み込み演算の中でFFTは6回しかやっていないはずだし、なんなら内部的には畳み込み演算を並列に計算したりしているのだが、複素数の乗算や加算、スレッドの待ち合わせなどの分でFFTを2回余計にやるぐらいの時間がかかってしまっているというのがわかる。

もう少し詳しく見ていく必要はありそう*2だが、確かにFFTや畳み込み結果の待ち合わせ処理には改善の余地がありそうなので、アルゴリズムを見直したり、Kotlinのコルーチンを入れたりして様子を見ていきたい。

*1:何が原因なのかは洗い切れていない。

*2:FFT自体の性能とかどの計算にどれだけ使っているのかなど

2019年に見たアニメ

いつものやつをやります。例によって50音順です。カッコ[]付きは未視聴(録画消化待ち)です。今年は全体的にジャンプのアニメが強かったですね。

トルネの不調に泣かされた年でもあった。ほとんどのアニメを1クール遅れで視聴した*1のだがそのせいか円盤をほとんど買っていない*2ということに気が付いた。

冬クール

春クール

夏クール

シャミ子が悪いんだよ、僕は何度か聞いたような気がしたんだけどなあ。

秋クール

映画

ここは大体公開順に並べています。プロメアはたぶん人生で初めて劇場に2回見に行った作品ということになった。

参考: 2019年度のおすすめアニメランキング【あにこれβ】

*1:リアルタイムで見れたのはかぐや様とシンフォギアくらいかな

*2:TVシリーズはケムリクサのみ。劇場版はいくつか予約した

lottie-androidのgetCurrentKeyframe()の性能を改善するパッチが取り込まれた

github.com

モチベーション

BaseKeyframeAnimation.getCurrentKeyframe()は、アニメーションのプログレス(0から1の実数で表されるアニメーションの進捗度合い)の値にあうKeyframeを返すメソッド。このKeyframesetProgress()の際にキャッシュされている。処理のおおざっぱな流れは次の記事で書いた。

akihito104.hatenablog.com

getCurrentKeyframe()が呼ばれるたびに、キャッシュのヒット判定(現在のプログレスがKeyframeの時間幅に含まれているかチェックする)メソッドが呼ばれたり、リストから探す処理が走ったりする。何をするにもこれが呼ばれる。一度のメソッド呼び出しで何度も呼ばれることがある。

BaseKeyframeAnimationは0個以上のKeyframeを持っているのだが、たかだか1個しか持っていない場合には現在のキーフレームをキャッシュしたりプログレスが範囲に入っているかを判定する必要がない(0個の時も言うまでもない)。そこで、0個の場合、1個の場合、それより多い場合とに分けて不要な処理を行わないようにした。

何をしたのか

BaseKeyframeAnimationが持っているKeyframeのリストをラップしてそれぞれの場合に適した処理を行う静的な内部クラスを実装した。具体的には、

  • Keyframeが0個のときはなにもしない。
  • Keyframeが1個のときはキャッシュせず、キャッシュのヒット判定も行わない。
  • Keyframeが2個以上のときはこれまでと同じ処理をする。

Keyframeを1個しか持たないアニメーション部品が多いほど効果が出る。Keyframeが0個の時というのはほとんどないので効果もほとんどない。メモリ使用量が若干増える影響はあるものの、メソッド呼び出し回数が減ったため処理時間はせいぜい3割程度短くなった(アニメーションの構成による)。また処理の見通しが多少は良くなった。

抽象クラスの処理の変更はそれを継承しているクラスにも影響が出るかもしれないので難しかった。テストがちゃんとあってよかった。さらに最適化できそうだが効果がうすそうだったのでやらなかった。キャッシュの仕方に若干不満が残ってしまったのでもっといい形を思いつけば置き換えたい。

余談

じつは性能計測用のトレースメソッドをR8で消すほうが効果があるかもしれなかったりする