Android上でのプログラミングではリフレクションは忌避すべきと言われている。
PCに比べるとリソースが制限されているスマートフォンではこれは正しい。しかしNexus oneやXperia等、周波数1Ghzのプロセッサと500MB以上のRAMを搭載した実機を使ってきた感覚だと、性能をシビアに追求するようなアプリケーション(主にゲームやリアルタイムシミュレーション)以外であれば、無駄に繰り返さない、結果を上手くキャッシュする等、使い方を注意すれば別に避けるものではないと考えている。(感覚としては10年前のIntel Pentium3が搭載されたPC上でJava2(JavaSE1.3〜1.4)を動作させている状態に近いと思う。)
なぜこんなことを書くかというと、今更Javaでリフレクションを否定されると何もできないからだ。自分が今書いているライブラリィ、フレームワークではリフレクションを使いたい(というか既に使っているし)
そこでAndroid 2.2のリフレクションによるメソッド実行の性能はどうなのかちょっとだけ調べてみることにした。
リフレクションの性能を調べるとには同様の処理をリフレクションの有無で比較すれば良い訳だが、はてなで同様にリフレクションの性能を試験されている方がいらっしゃったので、これを利用させて頂くことにした。 (unageanu氏にはテストコードの利用とその公開を快諾して頂きまして感謝します。)
[Java] リフレクションはやっぱり遅いのか? - うなの日記
ここで書かれているコードはテストするクラスの操作をタスクというインタフェースで定義しておき、それを統合、回数を指定してまとめで実行できるものだ。また内部では同じテストを10回実行しており環境によるサンプルのばらつきがなるべく出ないように工夫されている。
これをAndroidの開発環境上に移して動作を確認し、全く同じテストコードをAndroidエミュレータ、実機のNexus one、そしてPC上で動作しているJava SE6で実行しその時間の差を比較してみることにする。
テストコードの解説はうなの日記を見て頂くとして、メソッドの実行時間の差を計測する以外にAndroidに関してはちょっと気になることがあったので、フィールドに関しても同様に「通常」「リフレクション(検索時間含まず)」「リフレクション(検索時間含む)」のテストコードを追加してその時間を計測した。※
- 実行結果 (各テストは10万回繰返しているが、内部では更に10回実行された平均値がサンプリングされる)
[実行環境] ・Nexus one CPU Qualcomm® QSD8250™ 1GHz MEM 512MB OS Android 2.2 build FRF91 ・JavaSE6/Emulator CPU Intel® Xeon™ X5450 3.0Ghz (×2) MEM 4GB OS Windows® Vista™ with service pack 2/Android SDK 2.2 FRF91 Java Java™SE1.6 build 1.6.0_20-b02 [テスト結果]
| タスク | Emulator | Nexus one | JavaSE6 |
|---|---|---|---|
| メソッド呼び出し | 118 msec | 36 msec | 1 msec未満 |
| フィールドアクセス | 87 msec | 31 msec | 1 msec未満 |
| リフレクション-メソッド(検索時間を含まず) | 2358 msec | 505 msec | 43 msec |
| リフレクション-メソッド(検索時間を含む ) | 7532 msec | 1997 msec | 193 msec |
| リフレクション-フィールド(検索時間を含まず) | 686 msec | 163 msec | 67 msec |
| リフレクション-フィールド(検索時間を含む ) | 3065 msec | 662 msec | 202 msec |
-
- JavaSE6は圧倒的
- エミュレータは遅い
- Nexus oneは健闘している
- ReflectionFieldTask
static class ReflectionFieldTask implements Task {
private Kitten mii = new Kitten();
private Field f;
ReflectionFieldTask() {
try {
f = mii.getClass().getDeclaredField("name");
} catch ( SecurityException e ) {
e.printStackTrace();
} catch ( NoSuchFieldException e ) {
e.printStackTrace();
}
}
public void execute () throws Throwable {
f.set( mii, "mii" );
}
public String getName () {
return "リフレクション-フィールド(検索時間を含まず)";
}
}
メソッドでのベンチマークと同様にフィールドの検索時間を含んだタスクも書いているが、変わらないので省略する。