LoginSignup
5
4

More than 3 years have passed since last update.

【Kotlin】リフレクションの高速化テクニック雑まとめ

Posted at

リフレクションを弄る中で見つけた「これをやると遅くなる/これをやると速くなる」のまとめです。
間違い等有れば指摘大歓迎です。

kotlin.reflect.*をimportするようなことをしない

Kotlinのリフレクションははっきり言って遅いです。
なので、kotlin.reflectから何かをimportするような処理はできるだけ避けた方が良いです。

具体的には、コンストラクタを取得する際はclass.primaryConstructor!!とかはせず、::classなどの方法でメソッドリファレンスを取得した方が高速です。

リフレクションの呼び出し回数を減らす/取得結果を使いまわす

前述の通りKotlinのリフレクションは遅いため、アクセス回数は当然減らした方が良いです。
また、リフレクションのプロパティへアクセスする場合でも、その実態はカスタムゲッターなどで、アクセスの度に処理を回していたりします。

このような作りになっているのは何かしら理由が有るのでしょうが、何も考えずにガンガン参照してしまうとその分だけ動作が重くなるので、実装を確認しつつ不具合が出ないならできる限り結果を使いまわした方が効率がいいです。

KFunction.callを使う/callByを使わない

KFunctionの呼び出しにはcallcallByの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 { /* 取得条件 */ }
手段1
val result = property.call(foo)
手段2
// プロパティのゲッターを取得
val getter: KProperty1.Getter<T, *> = property.getter
val result = getter.call(foo)

この2つの方法は、自分が試した限りではそれほど性能が変わりませんでした。
一方、以下のjavaGetterで行う方法では有意な高速化が確認できました。

/* 手段3 */
val javaGetter = property.javaGetter!!
val result = javaGetter.invoke(src)
5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4