-
-
Notifications
You must be signed in to change notification settings - Fork 180
Description
Use case
If the arguments are fully specified (= don't have to use Default arguments
), can speed up the call to jackson-module-kotlin
by making a call with KFunction.call
.
Describe the solution you'd like
Currently jackson-module-kotlin
does mapping by calling KFunction.callBy
.
On the other hand, KFunction.callBy
has about 1/5 the performance of KFunction.call
, so you can expect speedup by doing KFunction.call
as much as possible.
Describe alternatives you've considered
For example, you can switch between call
and callBy
with low overhead by using the following data structure.
import kotlin.reflect.KParameter
internal class ArgumentBucket(private val parameters: List<KParameter>) : Map<KParameter, Any?> {
val valueArray: Array<Any?> = arrayOfNulls(parameters.size)
private val initializationStatuses: MutableSet<Int> = HashSet(parameters.size)
class Entry internal constructor(
override val key: KParameter,
override var value: Any?
) : Map.Entry<KParameter, Any?>
operator fun set(key: KParameter, value: Any?): Any? {
return valueArray[key.index].apply {
valueArray[key.index] = value
initializationStatuses.add(key.index)
}
}
fun isFullInitialized(): Boolean = parameters.size == initializationStatuses.size
override val entries: Set<Map.Entry<KParameter, Any?>>
get() = valueArray.withIndex()
.filter { (i, _) -> initializationStatuses.contains(i) }
.map { (i, arg) -> Entry(parameters[i], arg) }
.toSet()
override val keys: Set<KParameter>
get() = parameters
.filterIndexed { i, _ -> initializationStatuses.contains(i) }
.toSet()
override val size: Int
get() = initializationStatuses.size
override val values: Collection<Any?>
get() = valueArray.filterIndexed { i, _ -> initializationStatuses.contains(i) }
override fun containsKey(key: KParameter): Boolean = initializationStatuses.contains(key.index)
override fun containsValue(value: Any?): Boolean = valueArray.withIndex()
.any { (i, arg) -> initializationStatuses.contains(i) && value == arg }
override fun get(key: KParameter): Any? = valueArray[key.index]
override fun isEmpty(): Boolean = initializationStatuses.isEmpty()
}
This is used as follows (rewriting from line 134 of KotlinValueInstantiator.kt
).
ArgumentBucket(callable.parameters).apply {
callableParameters.forEachIndexed { idx, paramDef ->
if (paramDef != null) {
this[paramDef] = jsonParamValueList[idx]
}
}
}.let {
if (it.isFullInitialized())
callable.call(*it.valueArray)
else
callable.callBy(it)
}
Assuming that there are few situations where Default arguments
is needed, I think that this alone can speed up the process.
Additional context
I have published the contents as ProjectMapK/FastKFunction that incorporates further speed-up methods such as avoiding the spread operator and calling Java reflection directly, so I hope you can refer to that as well.
This is my first time posting issue and I am not good at English, so I'm sorry if there is something wrong with it.