Skip to content
This repository was archived by the owner on Jan 20, 2023. It is now read-only.

Repository files navigation

LicenseLint, Test, upload Coveradge.codecov


FastKFunction

FastKFunction is a wrapper library for fast calls to KFunction.

Demo code

With just this description, you can call the KFunction faster.

data classSample(
valarg1:Int,
valarg2:Int,
valarg3:Int,
valarg4:Int,
valarg5:Int
)
val function:KFunction<Sample> = ::Sampleval fastKFunction:FastKFunction<Sample> =FastKFunction.of(function)
// call by varargval result:Sample= fastKFunction.call(1, 2, 3, 4, 5)
// call by Collectionval result:Sample= fastKFunction.callByCollection(listOf(1, 2, 3, 4, 5))
// call by ArgumentBucketval result:Sample= fastKFunction.generateBucket()
.apply { (0 until 5).forEach { this[it] = it +1 }}
.let { fastKFunction.callBy(it) }

How Fast?

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/sRatio
Java Constructor104267558.46.7
FastKFunction(call)102948283.46.6
FastKFunction(callBy)105609306.26.8
KFunction(call)77096714.25.0
KFunction(callBy)15519730.21

ConstructorBenchmarkResultGraph.png

This score was measured with Ryzen7 3700X, Windows10, 3b8687 committed code and benchmark settings.
It is currently a little faster with small improvements.

Raw data, and other comparisons

You can get full benchmark score and some other graphs here.

Mechanism

FastKFunction realizes high speed by the following ingenuity.

  • Call KFunction with call if the argument is fully initialized.
  • If possible, call JavaMethod or Constructor directly for further speedup.
  • Efficient retention of arguments and switching between call/callBy calls by devising a data structure.
  • Avoid using spread operator as much as possible.

I have a blog post on the mechanism of fast invocation (in Japanese).

Benchmarking

You can run the benchmark with the ./gradlew jmh.
It takes about 45 minutes to run a complete benchmark.

./gradlew jmh

Installation

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.

How to use FastKFunction

Initialization

In some cases, instance parameter is required to initialize FastKFunction. Even if the instance parameter is not required, passing it may speed up the process.

The following is the correspondence table.

instance parameterdescription
ConstructorUnnecessary
Top level functionUnnecessary
Method reference from instanceOptionalPassing the instance parameter will speed up the call.
Function defined for the objectOptionalPassing instance parameter will speed up initialization.
Top level extension functionRequired
Method reference from classRequired

Calling the constructor of an inner class or an extension function defined in an instance is currently not supported.

How to call

FastKFunction supports two major types of calls.

Call by ArgumentBucket

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 classSample(
valarg1:Int,
valarg2:Int = 0,
valarg3:String? = null
)
val fastKFunction:FastKFunction<Sample> =FastKFunction.of(::Sample)
funmap(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) }
}

Call by vararg or Collection

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 varargval result:Sample= fastKFunction.call(1, 2, 3, 4, 5)
// call by Collectionval result:Sample= fastKFunction.callByCollection(listOf(1, 2, 3, 4, 5))

For functions that can be called from a single argument

For a function that can be called with a single argument, you can use the SingleArgFastKFunction.

data classSample(valarg:Int)
val fastKFunction:SingleArgFastKFunction<Sample> =SingleArgFastKFunction.of(::Sample)
val result:Sample= fastKFunction.call(1)

Releases

Packages

Contributors

Languages