
|-------------------------------|--------------------|
| Function call | 0.1 |
| Operating on a nullable value | 1 |
| Suspending function call | 1 |
| Synchronization | 10 |
| Function call with reflection | 10 |
| Printing / Logging | 10,000 |
| Making network request | 100,000,000 |
@State(Scope.Thread) open class Counter { var value = 0 @CompilerControl(CompilerControl.Mode.DONT_INLINE) fun increment() = value++ @CompilerControl(CompilerControl.Mode.DONT_INLINE) fun decrement() = value-- @CompilerControl(CompilerControl.Mode.DONT_INLINE) fun get() = value }
// Takes 0.5 ms @Benchmark fun regularCall(bh: Blackhole, counter: Counter) { repeat(1_000_000) { counter.increment() counter.decrement() counter.increment() counter.get() } bh.consume(counter) }
// Takes 0.6 ms @Benchmark fun simpleKotlinReflectionCall(bh: Blackhole, counter: Counter) { repeat(1_000_000) { (Counter::increment)(counter) (Counter::decrement)(counter) (Counter::increment)(counter) (Counter::get)(counter) } bh.consume(counter) }
// Takes 111 ms/op @Benchmark fun javaReflectionCall(bh: Blackhole, counter: Counter) { val increment = Counter::class.java.getDeclaredMethod("increment") val decrement = Counter::class.java.getDeclaredMethod("decrement") val get = Counter::class.java.getDeclaredMethod("get") repeat(1_000_000) { increment.invoke(counter) decrement.invoke(counter) increment.invoke(counter) get.invoke(counter) } bh.consume(counter) } // Takes 166 ms/op @Benchmark fun kotlinReflectionCall(bh: Blackhole, counter: Counter) { val increment = Counter::class.members.first { it.name == "increment" } val decrement = Counter::class.members.first { it.name == "decrement" } val get = Counter::class.members.first { it.name == "get" } repeat(1_000_000) { increment.call(counter) decrement.call(counter) increment.call(counter) get.call(counter) } bh.consume(counter) }
// Takes 168.951 ± 0.618 ms/op @Benchmark fun kotlinReflectionCallWithFinding(bh: Blackhole, counter: Counter) { repeat(1_000_000) { Counter::class.members.first { it.name == "increment" }.call(counter) Counter::class.members.first { it.name == "decrement" }.call(counter) Counter::class.members.first { it.name == "increment" }.call(counter) Counter::class.members.first { it.name == "get" }.call(counter) } bh.consume(counter) }
// Takes 14 ms/op @Benchmark fun nullableValueIncrement(bh: Blackhole, counter: Counter) { var counter: Int? = 0 repeat(1_000_000) { counter = counter?.inc() counter = counter?.dec() counter = counter?.inc() counter // This is optimized out, but it does influence conclusions } bh.consume(counter) }
// Takes 50 ms/op @Benchmark fun atomicCounterCall(bh: Blackhole, counter: AtomicCounter) { repeat(1_000_000) { counter.increment() counter.decrement() counter.increment() counter.get() } bh.consume(counter) } @State(Scope.Thread) open class AtomicCounter { var value = AtomicInteger(0) @CompilerControl(CompilerControl.Mode.DONT_INLINE) fun increment() = value.incrementAndGet() @CompilerControl(CompilerControl.Mode.DONT_INLINE) fun decrement() = value.decrementAndGet() @CompilerControl(CompilerControl.Mode.DONT_INLINE) fun get() = value }
// Takes 22 ms/op @Benchmark fun synchronizedCounterCall(bh: Blackhole, counter: SynchronizedCounter) { repeat(1_000_000) { counter.increment() counter.decrement() counter.increment() counter.get() } bh.consume(counter) } @State(Scope.Thread) open class SynchronizedCounter { var value = 0 @CompilerControl(CompilerControl.Mode.DONT_INLINE) fun increment() = synchronized(this) { value++ } @CompilerControl(CompilerControl.Mode.DONT_INLINE) fun decrement() = synchronized(this) { value-- } @CompilerControl(CompilerControl.Mode.DONT_INLINE) fun get() = synchronized(this) { value } }
println to method calls, and my computer quickly got drained out of memory. To avoid it, I made texts to print as short as possible, used print, and limited the number of iterations to 10,000 (from 1,000,000). It takes 192 ms, so for regular number of operations it should take around 19,204 ms. That is longer than reflection by several orders of magnitude, even unoptimized reflection.// Takes 192 ms/op // so for 1,000,000 operations it should take around 19,204 ms for the same number of operations @Benchmark fun printingCounterCall(bh: Blackhole, counter: PrintingCounter) { repeat(10_000) { counter.increment() counter.decrement() counter.increment() counter.get() } bh.consume(counter) } @State(Scope.Thread) open class PrintingCounter { var value = 0 fun increment() { print("I") value++ } fun decrement() { print("D") value-- } fun get() { print("G") value } }
|-------------------------------|--------------------|
| Function call | 0.1 |
| Operating on a nullable value | 1 |
| Suspending function call | 1 |
| Synchronization | 10 |
| Function call with reflection | 10 |
| Printing / Logging | 10,000 |
| Making network request | 100,000,000 |