FastKFunction
is a wrapper library for fast calls to KFunction
.
With just this description, you can call the KFunction
faster.
data class Sample(
val arg1: Int,
val arg2: Int,
val arg3: Int,
val arg4: Int,
val arg5: Int
)
val function: KFunction<Sample> = ::Sample
val fastKFunction: FastKFunction<Sample> = FastKFunction.of(function)
// call by vararg
val result: Sample = fastKFunction.call(1, 2, 3, 4, 5)
// call by Collection
val result: Sample = fastKFunction.callByCollection(listOf(1, 2, 3, 4, 5))
// call by ArgumentBucket
val result: Sample = fastKFunction.generateBucket()
.apply { (0 until 5).forEach { this[it] = it + 1 }}
.let { fastKFunction.callBy(it) }
Calling the constructor is more than 1.2 times faster than calling KFunction
with call
,
and more than 6 times faster than calling it with callBy
.
You can get the same speed as reflection
in Java
.
ops/s | Ratio | |
---|---|---|
Java Constructor | 104267558.4 |
6.7 |
FastKFunction(call) | 102948283.4 |
6.6 |
FastKFunction(callBy) | 105609306.2 |
6.8 |
KFunction(call) | 77096714.2 |
5.0 |
KFunction(callBy) | 15519730.2 |
1 |
*This is the score I got on a Ryzen7 3700X
in a Windows 10
environment, with 3b8687 committed.
You can get full benchmark score and some other graphs here.
I have a blog post on the mechanism of fast invocation (in Japanese).
You can run the benchmark with the ./gradlew jmh
.
Please note that it will take about 5 hours in total if executed with the default settings.
./gradlew jmh
FastKFunction
is published on JitPack.
You can use this library on Maven
, gradle
and any other build tools.
Please see here for the introduction method.
If you call an instance function, you can expect a faster call with instance parameter
.
data class Sample(
val arg1: Int,
val arg2: Int
) {
fun instanceFun(arg3: Int): Int = arg1 + arg2 + arg3
}
val sample = Sample(1, 2)
val fastKFunction = FastKFunction.of(sample::instanceFun, sample)
Depending on how you get the KFunction
, the instance parameter
may be required.
Even if the instance parameter
is not required, passing an instance parameter
will make the call faster.
FastKFunction
supports two major types of calls.
Calling with vararg
or Collection
is faster if you don't need to use the default arguments and
can get them in the order in which they are defined.
val fastKFunction: FastKFunction<Sample> = FastKFunction.of(function)
// call by vararg
val result: Sample = fastKFunction.call(1, 2, 3, 4, 5)
// call by Collection
val result: Sample = fastKFunction.callByCollection(listOf(1, 2, 3, 4, 5))
If the default argument is expected to be used, a call using ArgumentBucket
is available.
ArgumentBucket
has interfaces like MutableMap<KParameter, Any?>
, which can be used, for example, as follows.
data class Sample(
val arg1: Int,
val arg2: Int = 0,
val arg3: String? = null
)
val fastKFunction: FastKFunction<Sample> = FastKFunction.of(::Sample)
fun map(src: Map<String, Any?>): Sample {
return fastKFunction.generateBucket()
.apply {
fastKFunction.valueParameters.forEach {
if (src.containsKey(it.name!!)) this[it] = src.getValue(it.name!!)
}
}.let { fastKFunction.callBy(it) }
}
For a function that can be called with a single argument, you can use the SingleArgFastKFunction
.
data class Sample(val arg: Int)
val fastKFunction: SingleArgFastKFunction<Sample> = SingleArgFastKFunction.of(::Sample)
val result: Sample = fastKFunction.call(1)