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

Commit c72e933

Browse files
authored
Merge pull request #5 from k163377/add_benchmark
Add benchmark.
2 parents c7e3035 + 9899ada commit c72e933

File tree

8 files changed

+363
-0
lines changed

8 files changed

+363
-0
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,11 @@ fun map(src: Map<String, Any?>): Sample {
104104
}.let { fastKFunction.callBy(it) }
105105
}
106106
```
107+
108+
## Benchmark
109+
You can run the benchmark with the `./gradlew jmh`.
110+
Please note that it will take about 2 hours in total if executed with the default settings.
111+
112+
```bash
113+
./gradlew jmh
114+
```

build.gradle.kts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
plugins {
22
id("maven")
33
kotlin("jvm") version "1.4.20"
4+
// プロダクションコード以外
45
id("org.jlleitschuh.gradle.ktlint") version "9.4.1"
56
id("jacoco")
7+
id("me.champeau.gradle.jmh") version "0.5.2"
68
}
79

810
group = "com.mapk"
@@ -20,6 +22,8 @@ dependencies {
2022
testImplementation(group = "org.junit.jupiter", name = "junit-jupiter", version = "5.7.0") {
2123
exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
2224
}
25+
26+
implementation(group = "org.openjdk.jmh", name = "jmh-core", version = "1.26")
2327
}
2428

2529
tasks {
@@ -34,6 +38,10 @@ tasks {
3438
jvmTarget = "1.8"
3539
}
3640
}
41+
// https://qiita.com/wrongwrong/items/16fa10a7f78a31830ed8
42+
jmhJar {
43+
exclude("META-INF/versions/9/module-info.class")
44+
}
3745
jacocoTestReport {
3846
reports {
3947
xml.isEnabled = true
@@ -47,3 +55,11 @@ tasks {
4755
finalizedBy(jacocoTestReport)
4856
}
4957
}
58+
59+
jmh {
60+
fork = 3
61+
iterations = 3
62+
threads = 3
63+
warmupBatchSize = 3
64+
warmupIterations = 3
65+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.mapk.fastkfunction
2+
3+
import com.mapk.fastkfunction.argumentbucket.ArgumentBucket
4+
import com.mapk.fastkfunction.benchmarktargets.Constructor5
5+
import org.openjdk.jmh.annotations.Benchmark
6+
import org.openjdk.jmh.annotations.Scope
7+
import org.openjdk.jmh.annotations.State
8+
import java.lang.reflect.Constructor
9+
import kotlin.reflect.KFunction
10+
import kotlin.reflect.KParameter
11+
import kotlin.reflect.jvm.javaConstructor
12+
13+
@State(Scope.Benchmark)
14+
open class CallConstructorBenchmark {
15+
private val function: KFunction<Constructor5> = ::Constructor5
16+
private val argumentMap: Map<KParameter, Any?> = function.parameters.associateWith { it.index + 1 }
17+
18+
private val javaConstructor: Constructor<Constructor5> = function.javaConstructor!!
19+
private val fastKFunction: FastKFunction<Constructor5> = FastKFunction(function, null)
20+
private val argumentBucket: ArgumentBucket = fastKFunction.generateBucket()
21+
.apply { (0 until 5).forEach { this[it] = it + 1 } }
22+
23+
@Benchmark
24+
fun normalCall(): Constructor5 = Constructor5(1, 2, 3, 4, 5)
25+
26+
@Benchmark
27+
fun kFunctionCall(): Constructor5 = function.call(1, 2, 3, 4, 5)
28+
29+
@Benchmark
30+
fun kFunctionCallBy(): Constructor5 = function.callBy(argumentMap)
31+
32+
@Benchmark
33+
fun javaConstructor(): Constructor5 = javaConstructor.newInstance(1, 2, 3, 4, 5)
34+
35+
@Benchmark
36+
fun fastKFunctionCall(): Constructor5 = fastKFunction.call(1, 2, 3, 4, 5)
37+
38+
@Benchmark
39+
fun fastKFunctionCallBy(): Constructor5 = fastKFunction.callBy(argumentBucket)
40+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.mapk.fastkfunction
2+
3+
import com.mapk.fastkfunction.argumentbucket.ArgumentBucket
4+
import com.mapk.fastkfunction.benchmarktargets.Constructor5
5+
import org.openjdk.jmh.annotations.Benchmark
6+
import org.openjdk.jmh.annotations.Scope
7+
import org.openjdk.jmh.annotations.State
8+
import java.lang.reflect.Method
9+
import kotlin.reflect.KFunction
10+
import kotlin.reflect.KParameter
11+
import kotlin.reflect.jvm.javaMethod
12+
13+
@State(Scope.Benchmark)
14+
open class CallInstanceMethodBenchmark {
15+
private val instance = Constructor5(1, 2, 3, 4, 5)
16+
private val function: KFunction<Constructor5> = instance::instanceFun5
17+
private val argumentMap: Map<KParameter, Any?> = function.parameters.associateWith { it.index + 1 }
18+
19+
private val javaMethod: Method = function.javaMethod!!
20+
private val fastKFunctionWithoutInstance: FastKFunction<Constructor5> = FastKFunction(function, null)
21+
private val fastKFunctionWithInstance: FastKFunction<Constructor5> = FastKFunction(function, instance)
22+
private val argumentBucket: ArgumentBucket = fastKFunctionWithoutInstance.generateBucket()
23+
.apply { (0 until 5).forEach { this[it] = it + 1 } }
24+
25+
@Benchmark
26+
fun normalCall(): Constructor5 = instance.instanceFun5(1, 2, 3, 4, 5)
27+
28+
@Benchmark
29+
fun kFunctionCall(): Constructor5 = function.call(1, 2, 3, 4, 5)
30+
31+
@Benchmark
32+
fun kFunctionCallBy(): Constructor5 = function.callBy(argumentMap)
33+
34+
@Benchmark
35+
fun javaMethod(): Constructor5 = javaMethod.invoke(instance, 1, 2, 3, 4, 5) as Constructor5
36+
37+
@Benchmark
38+
fun fastKFunctionWithoutInstanceCall(): Constructor5 = fastKFunctionWithoutInstance.call(1, 2, 3, 4, 5)
39+
40+
@Benchmark
41+
fun fastKFunctionWithoutInstanceCallBy(): Constructor5 = fastKFunctionWithoutInstance.callBy(argumentBucket)
42+
43+
@Benchmark
44+
fun fastKFunctionWithInstanceCall(): Constructor5 = fastKFunctionWithInstance.call(1, 2, 3, 4, 5)
45+
46+
@Benchmark
47+
fun fastKFunctionWithInstanceCallBy(): Constructor5 = fastKFunctionWithInstance.callBy(argumentBucket)
48+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package com.mapk.fastkfunction
2+
3+
import com.mapk.fastkfunction.argumentbucket.ArgumentBucket
4+
import com.mapk.fastkfunction.benchmarktargets.Constructor5
5+
import org.openjdk.jmh.annotations.Benchmark
6+
import org.openjdk.jmh.annotations.Scope
7+
import org.openjdk.jmh.annotations.State
8+
import java.lang.reflect.Method
9+
import kotlin.reflect.KFunction
10+
import kotlin.reflect.KParameter
11+
import kotlin.reflect.full.companionObjectInstance
12+
import kotlin.reflect.full.functions
13+
import kotlin.reflect.jvm.javaMethod
14+
15+
@State(Scope.Benchmark)
16+
open class CallObjectMethodBenchmark {
17+
private val objectInstance = Constructor5::class.companionObjectInstance!!
18+
19+
private val functionByMethodReference: KFunction<Constructor5> = (Constructor5)::companionObjectFun5
20+
@Suppress("UNCHECKED_CAST")
21+
private val functionByReflection: KFunction<Constructor5> = objectInstance::class
22+
.functions
23+
.first { it.name == "companionObjectFun5" } as KFunction<Constructor5>
24+
25+
private val argumentMap: Map<KParameter, Any?> = functionByMethodReference.parameters.associateWith { it.index + 1 }
26+
27+
private val javaMethod: Method = functionByMethodReference.javaMethod!!
28+
29+
private val fastKFunctionByMethodReferenceWithoutInstance: FastKFunction<Constructor5> =
30+
FastKFunction(functionByMethodReference)
31+
private val fastKFunctionByMethodReferenceWithInstance: FastKFunction<Constructor5> =
32+
FastKFunction(functionByMethodReference, objectInstance)
33+
34+
private val fastKFunctionByReflectionWithoutInstance: FastKFunction<Constructor5> =
35+
FastKFunction(functionByReflection)
36+
private val fastKFunctionByReflectionWithInstance: FastKFunction<Constructor5> =
37+
FastKFunction(functionByReflection, objectInstance)
38+
39+
private val argumentBucket: ArgumentBucket = fastKFunctionByMethodReferenceWithoutInstance.generateBucket()
40+
.apply { (0 until 5).forEach { this[it] = it + 1 } }
41+
42+
@Benchmark
43+
fun normalCall(): Constructor5 = Constructor5.companionObjectFun5(1, 2, 3, 4, 5)
44+
45+
@Benchmark
46+
fun functionByMethodReferenceCall(): Constructor5 = functionByMethodReference.call(1, 2, 3, 4, 5)
47+
48+
@Benchmark
49+
fun functionByMethodReferenceCallBy(): Constructor5 = functionByMethodReference.callBy(argumentMap)
50+
51+
@Benchmark
52+
fun functionByReflectionCall(): Constructor5 = functionByReflection.call(1, 2, 3, 4, 5)
53+
54+
@Benchmark
55+
fun functionByReflectionCallBy(): Constructor5 = functionByReflection.callBy(argumentMap)
56+
57+
@Benchmark
58+
fun javaMethod(): Constructor5 = javaMethod.invoke(objectInstance, 1, 2, 3, 4, 5) as Constructor5
59+
60+
@Benchmark
61+
fun fastKFunctionByMethodReferenceWithoutInstanceCall(): Constructor5 =
62+
fastKFunctionByMethodReferenceWithoutInstance.call(1, 2, 3, 4, 5)
63+
64+
@Benchmark
65+
fun fastKFunctionByMethodReferenceWithoutInstanceCallBy(): Constructor5 =
66+
fastKFunctionByMethodReferenceWithoutInstance.callBy(argumentBucket)
67+
68+
@Benchmark
69+
fun fastKFunctionByMethodReferenceWithInstanceCall(): Constructor5 =
70+
fastKFunctionByMethodReferenceWithInstance.call(1, 2, 3, 4, 5)
71+
72+
@Benchmark
73+
fun fastKFunctionByMethodReferenceWithInstanceCallBy(): Constructor5 =
74+
fastKFunctionByMethodReferenceWithInstance.callBy(argumentBucket)
75+
76+
@Benchmark
77+
fun fastKFunctionByReflectionWithoutInstanceCall(): Constructor5 =
78+
fastKFunctionByReflectionWithoutInstance.call(1, 2, 3, 4, 5)
79+
80+
@Benchmark
81+
fun fastKFunctionByReflectionWithoutInstanceCallBy(): Constructor5 =
82+
fastKFunctionByReflectionWithoutInstance.callBy(argumentBucket)
83+
84+
@Benchmark
85+
fun fastKFunctionByReflectionWithInstanceCall(): Constructor5 =
86+
fastKFunctionByReflectionWithInstance.call(1, 2, 3, 4, 5)
87+
88+
@Benchmark
89+
fun fastKFunctionByReflectionWithInstanceCallBy(): Constructor5 =
90+
fastKFunctionByReflectionWithInstance.callBy(argumentBucket)
91+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package com.mapk.fastkfunction
2+
3+
import com.mapk.fastkfunction.argumentbucket.ArgumentBucket
4+
import com.mapk.fastkfunction.benchmarktargets.Constructor5
5+
import com.mapk.fastkfunction.benchmarktargets.topLevelExtensionFun5
6+
import org.openjdk.jmh.annotations.Benchmark
7+
import org.openjdk.jmh.annotations.Scope
8+
import org.openjdk.jmh.annotations.State
9+
import java.lang.reflect.Method
10+
import kotlin.reflect.KFunction
11+
import kotlin.reflect.KParameter
12+
import kotlin.reflect.jvm.javaMethod
13+
14+
@State(Scope.Benchmark)
15+
open class CallTopLevelExtensionFunBenchmark {
16+
private val receiverInstance = Constructor5(1, 2, 3, 4, 5)
17+
18+
private val functionByMethodReference: KFunction<Constructor5> = receiverInstance::topLevelExtensionFun5
19+
private val functionFromClass: KFunction<Constructor5> = Constructor5::topLevelExtensionFun5
20+
21+
private val argumentMap: Map<KParameter, Any?> = functionByMethodReference.parameters.associateWith { it.index + 1 }
22+
23+
private val javaMethod: Method = functionByMethodReference.javaMethod!!
24+
25+
private val fastKFunctionByMethodReferenceWithoutInstance: FastKFunction<Constructor5> =
26+
FastKFunction(functionByMethodReference)
27+
private val fastKFunctionByMethodReferenceWithInstance: FastKFunction<Constructor5> =
28+
FastKFunction(functionByMethodReference, receiverInstance)
29+
30+
private val fastKFunctionFromClass: FastKFunction<Constructor5> = FastKFunction(functionFromClass, receiverInstance)
31+
32+
private val argumentBucket: ArgumentBucket = fastKFunctionByMethodReferenceWithoutInstance.generateBucket()
33+
.apply { (0 until 5).forEach { this[it] = it + 1 } }
34+
35+
@Benchmark
36+
fun normalCall(): Constructor5 = receiverInstance.topLevelExtensionFun5(1, 2, 3, 4, 5)
37+
38+
@Benchmark
39+
fun functionByMethodReferenceCall(): Constructor5 = functionByMethodReference.call(1, 2, 3, 4, 5)
40+
41+
@Benchmark
42+
fun functionByMethodReferenceCallBy(): Constructor5 = functionByMethodReference.callBy(argumentMap)
43+
44+
@Benchmark
45+
fun functionFromClassCall(): Constructor5 = functionFromClass.call(1, 2, 3, 4, 5)
46+
47+
@Benchmark
48+
fun functionFromClassCallBy(): Constructor5 = functionFromClass.callBy(argumentMap)
49+
50+
@Benchmark
51+
fun javaMethod(): Constructor5 = javaMethod.invoke(null, receiverInstance, 1, 2, 3, 4, 5) as Constructor5
52+
53+
@Benchmark
54+
fun fastKFunctionByMethodReferenceWithoutInstanceCall(): Constructor5 =
55+
fastKFunctionByMethodReferenceWithoutInstance.call(1, 2, 3, 4, 5)
56+
57+
@Benchmark
58+
fun fastKFunctionByMethodReferenceWithoutInstanceCallBy(): Constructor5 =
59+
fastKFunctionByMethodReferenceWithoutInstance.callBy(argumentBucket)
60+
61+
@Benchmark
62+
fun fastKFunctionByMethodReferenceWithInstanceCall(): Constructor5 =
63+
fastKFunctionByMethodReferenceWithInstance.call(1, 2, 3, 4, 5)
64+
65+
@Benchmark
66+
fun fastKFunctionByMethodReferenceWithInstanceCallBy(): Constructor5 =
67+
fastKFunctionByMethodReferenceWithInstance.callBy(argumentBucket)
68+
69+
@Benchmark
70+
fun fastKFunctionFromClassCall(): Constructor5 =
71+
fastKFunctionFromClass.call(1, 2, 3, 4, 5)
72+
73+
@Benchmark
74+
fun fastKFunctionFromClassCallBy(): Constructor5 =
75+
fastKFunctionFromClass.callBy(argumentBucket)
76+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.mapk.fastkfunction
2+
3+
import com.mapk.fastkfunction.argumentbucket.ArgumentBucket
4+
import com.mapk.fastkfunction.benchmarktargets.Constructor5
5+
import com.mapk.fastkfunction.benchmarktargets.topLevelFun5
6+
import org.openjdk.jmh.annotations.Benchmark
7+
import org.openjdk.jmh.annotations.Scope
8+
import org.openjdk.jmh.annotations.State
9+
import java.lang.reflect.Method
10+
import kotlin.reflect.KFunction
11+
import kotlin.reflect.KParameter
12+
import kotlin.reflect.jvm.javaMethod
13+
14+
@State(Scope.Benchmark)
15+
open class CallTopLevelFunBenchmark {
16+
private val function: KFunction<Constructor5> = ::topLevelFun5
17+
private val argumentMap: Map<KParameter, Any?> = function.parameters.associateWith { it.index + 1 }
18+
19+
private val javaMethod: Method = function.javaMethod!!
20+
private val fastKFunction: FastKFunction<Constructor5> = FastKFunction(function, null)
21+
private val argumentBucket: ArgumentBucket = fastKFunction.generateBucket()
22+
.apply { (0 until 5).forEach { this[it] = it + 1 } }
23+
24+
@Benchmark
25+
fun normalCall(): Constructor5 = topLevelFun5(1, 2, 3, 4, 5)
26+
27+
@Benchmark
28+
fun kFunctionCall(): Constructor5 = function.call(1, 2, 3, 4, 5)
29+
30+
@Benchmark
31+
fun kFunctionCallBy(): Constructor5 = function.callBy(argumentMap)
32+
33+
@Benchmark
34+
fun javaMethod(): Constructor5 = javaMethod.invoke(null, 1, 2, 3, 4, 5) as Constructor5
35+
36+
@Benchmark
37+
fun fastKFunctionCall(): Constructor5 = fastKFunction.call(1, 2, 3, 4, 5)
38+
39+
@Benchmark
40+
fun fastKFunctionCallBy(): Constructor5 = fastKFunction.callBy(argumentBucket)
41+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.mapk.fastkfunction.benchmarktargets
2+
3+
data class Constructor5(
4+
val arg1: Int,
5+
val arg2: Int,
6+
val arg3: Int,
7+
val arg4: Int,
8+
val arg5: Int
9+
) {
10+
fun instanceFun5(arg1: Int, arg2: Int, arg3: Int, arg4: Int, arg5: Int) = Constructor5(
11+
this.arg1 + arg1,
12+
this.arg2 + arg2,
13+
this.arg3 + arg3,
14+
this.arg4 + arg4,
15+
this.arg5 + arg5
16+
)
17+
18+
companion object {
19+
fun companionObjectFun5(
20+
arg1: Int,
21+
arg2: Int,
22+
arg3: Int,
23+
arg4: Int,
24+
arg5: Int
25+
) = Constructor5(arg1, arg2, arg3, arg4, arg5)
26+
}
27+
}
28+
29+
fun topLevelFun5(
30+
arg1: Int,
31+
arg2: Int,
32+
arg3: Int,
33+
arg4: Int,
34+
arg5: Int
35+
) = Constructor5(arg1, arg2, arg3, arg4, arg5)
36+
37+
fun Constructor5.topLevelExtensionFun5(arg1: Int, arg2: Int, arg3: Int, arg4: Int, arg5: Int) = Constructor5(
38+
this.arg1 + arg1,
39+
this.arg2 + arg2,
40+
this.arg3 + arg3,
41+
this.arg4 + arg4,
42+
this.arg5 + arg5
43+
)

0 commit comments

Comments
 (0)