リフレクションを弄る中で見つけた「これをやると遅くなる/これをやると速くなる」のまとめです。
間違い等有れば指摘大歓迎です。
kotlin.reflect.*をimportするようなことをしない
Kotlin
のリフレクションははっきり言って遅いです。
なので、kotlin.reflect
から何かをimport
するような処理はできるだけ避けた方が良いです。
具体的には、コンストラクタを取得する際はclass.primaryConstructor!!
とかはせず、::class
などの方法でメソッドリファレンスを取得した方が高速です。
リフレクションの呼び出し回数を減らす/取得結果を使いまわす
前述の通りKotlin
のリフレクションは遅いため、アクセス回数は当然減らした方が良いです。
また、リフレクションのプロパティへアクセスする場合でも、その実態はカスタムゲッターなどで、アクセスの度に処理を回していたりします。
このような作りになっているのは何かしら理由が有るのでしょうが、何も考えずにガンガン参照してしまうとその分だけ動作が重くなるので、実装を確認しつつ不具合が出ないならできる限り結果を使いまわした方が効率がいいです。
KFunction.callを使う/callByを使わない
KFunction
の呼び出しにはcall
とcallBy
の2種類のインターフェースが用意されています。
この2つは以下のような特性が有ります。
-
call
- 引数を可変長引数で渡す
- 引数の順番が正しい必要が有る
- オーバーヘッドが少なく高速
-
callBy
- 引数を
Map<KParameter, Any?>
で渡す - 引数の順番は順不同
- オーバーヘッドが大きく低速(最終的には
call
が呼ばれる)
- 引数を
何も考えない場合callBy
の方が扱いやすいですが、オーバーヘッドの大きさから、高速化を考える場合はcall
一択で考えた方が良いです。
プロパティを読み出す時はjavaGetterで行う
インスタンスからプロパティを読み出す場合、Kotlin
のリフレクションのみを用いると大まかに以下2通りの方法で取得ができます。
val foo = /* 何かしらのインスタンス */
// クラス内のプロパティを取得
val properties: Collection<KProperty1<T, *>> = foo::class.memberProperties
// 値を得たいプロパティを取得
val property: KProperty1<T, *> = properties.first { /* 取得条件 */ }
val result = property.call(foo)
// プロパティのゲッターを取得
val getter: KProperty1.Getter<T, *> = property.getter
val result = getter.call(foo)
この2つの方法は、自分が試した限りではそれほど性能が変わりませんでした。
一方、以下のjavaGetter
で行う方法では有意な高速化が確認できました。
/* 手段3 */
val javaGetter = property.javaGetter!!
val result = javaGetter.invoke(src)