From c1ab4a8f576dc6b00dab2f88e10a5e2c9287dbdf Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 27 Nov 2024 11:26:03 +0800 Subject: [PATCH] Benchmark awaiting heavier computations in the benchmarks Some conclusions: 1. Calling `runBlocking` without a `CoroutineContext` should be equivalent to calling it with `Dispatchers.Unconfined`, both of which run on the original single thread of the caller. 1. Awaiting empty `async`s is dependent on single-core performance. 1. Both `Dispatchers.Default` and `Dispatchers.IO` utilize all the cores for computation. 1. `await` is much heavier on `Dispatchers.IO`. 1. `await` is lightest when calling `runBlocking` without a `CoroutineContext` or on `Dispatchers.Unconfined`. --- .../coroutines/benchmark/AbstractBenchmark.kt | 2 +- ...AbstractRunBlockingAwaitAsyncsBenchmark.kt | 7 +++++ .../coroutines/benchmark/AwaitAsyncs.kt | 15 +++++++++- ...eterizedRunBlockingAwaitAsyncsBenchmark.kt | 29 ++++++++++--------- .../RunBlockingAwaitAsyncsBenchmark.kt | 10 +++++-- 5 files changed, 45 insertions(+), 18 deletions(-) create mode 100644 integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/AbstractRunBlockingAwaitAsyncsBenchmark.kt diff --git a/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/AbstractBenchmark.kt b/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/AbstractBenchmark.kt index ad52aae..1220fa9 100644 --- a/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/AbstractBenchmark.kt +++ b/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/AbstractBenchmark.kt @@ -6,6 +6,6 @@ import kotlinx.benchmark.State import kotlinx.benchmark.Warmup @State(Scope.Benchmark) -@Warmup(time = 1, iterations = 8) +@Warmup(time = 1, iterations = 8) // 4 seems not enough @Measurement(time = 1, iterations = 8) abstract class AbstractBenchmark \ No newline at end of file diff --git a/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/AbstractRunBlockingAwaitAsyncsBenchmark.kt b/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/AbstractRunBlockingAwaitAsyncsBenchmark.kt new file mode 100644 index 0000000..a85c4c8 --- /dev/null +++ b/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/AbstractRunBlockingAwaitAsyncsBenchmark.kt @@ -0,0 +1,7 @@ +package com.huanshankeji.kotlinx.coroutines.benchmark + +abstract class AbstractRunBlockingAwaitAsyncsBenchmark : AbstractBenchmark() { + abstract fun runBlockingAwait1mAsyncs() + + abstract fun runBlockingAwait1KAsync1mSums() +} \ No newline at end of file diff --git a/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/AwaitAsyncs.kt b/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/AwaitAsyncs.kt index fc09983..ebebd26 100644 --- a/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/AwaitAsyncs.kt +++ b/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/AwaitAsyncs.kt @@ -5,6 +5,19 @@ import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll @Suppress("SuspendFunctionOnCoroutineScope") -internal suspend inline fun CoroutineScope.await1MAasyncs() { +internal suspend inline fun CoroutineScope.await1mAsyncs() { List(1 shl 20) { async {} }.awaitAll() } + +internal val `1mSizeList` = List(1 shl 20) { it } + +// It seems the loop get optimized and removed in this function. +@Suppress("SuspendFunctionOnCoroutineScope") +internal suspend inline fun CoroutineScope.await1kAsync1mLoops() { + List(1 shl 10) { async { repeat(1 shl 20) {} } }.awaitAll() +} + +@Suppress("SuspendFunctionOnCoroutineScope") +internal suspend inline fun CoroutineScope.await1kAsync1mSums() { + List(1 shl 10) { async { `1mSizeList`.sum() } }.awaitAll() +} diff --git a/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/ParameterizedRunBlockingAwaitAsyncsBenchmark.kt b/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/ParameterizedRunBlockingAwaitAsyncsBenchmark.kt index 548f16d..92239fa 100644 --- a/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/ParameterizedRunBlockingAwaitAsyncsBenchmark.kt +++ b/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/ParameterizedRunBlockingAwaitAsyncsBenchmark.kt @@ -4,7 +4,7 @@ import kotlinx.benchmark.Benchmark import kotlinx.benchmark.Param import kotlinx.coroutines.* -class ParameterizedRunBlockingAwaitAsyncsBenchmark : AbstractBenchmark() { +class ParameterizedRunBlockingAwaitAsyncsBenchmark : AbstractRunBlockingAwaitAsyncsBenchmark() { enum class DispatcherArgumentEnum { Default, /*Main,*/ Unconfined, IO, SingleThread } @@ -14,18 +14,21 @@ class ParameterizedRunBlockingAwaitAsyncsBenchmark : AbstractBenchmark() { @OptIn(ExperimentalCoroutinesApi::class, DelicateCoroutinesApi::class) val singleThreadContext = newSingleThreadContext("single thread") + val dispatcher + get() = when (dispatcherArgumentEnum) { + DispatcherArgumentEnum.Default -> Dispatchers.Default + //DispatcherArgumentEnum.Main -> Dispatchers.Main + DispatcherArgumentEnum.Unconfined -> Dispatchers.Unconfined + DispatcherArgumentEnum.IO -> Dispatchers.IO + DispatcherArgumentEnum.SingleThread -> singleThreadContext + } + @Benchmark - fun runBlockingAwait1MAsyncsWithDispatcherArgument() = - runBlocking( - when (dispatcherArgumentEnum) { - DispatcherArgumentEnum.Default -> Dispatchers.Default - //DispatcherArgumentEnum.Main -> Dispatchers.Main - DispatcherArgumentEnum.Unconfined -> Dispatchers.Unconfined - DispatcherArgumentEnum.IO -> Dispatchers.IO - DispatcherArgumentEnum.SingleThread -> singleThreadContext - } - ) { - await1MAasyncs() - } + override fun runBlockingAwait1mAsyncs() = + runBlocking(dispatcher) { await1mAsyncs() } + + @Benchmark + override fun runBlockingAwait1KAsync1mSums() = + runBlocking(dispatcher) { await1kAsync1mSums() } } \ No newline at end of file diff --git a/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/RunBlockingAwaitAsyncsBenchmark.kt b/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/RunBlockingAwaitAsyncsBenchmark.kt index ce218e7..f0608ce 100644 --- a/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/RunBlockingAwaitAsyncsBenchmark.kt +++ b/integrated/src/benchmarks/kotlin/com/huanshankeji/kotlinx/coroutines/benchmark/RunBlockingAwaitAsyncsBenchmark.kt @@ -3,8 +3,12 @@ package com.huanshankeji.kotlinx.coroutines.benchmark import kotlinx.benchmark.Benchmark import kotlinx.coroutines.runBlocking -class RunBlockingAwaitAsyncsBenchmark : AbstractBenchmark() { +class RunBlockingAwaitAsyncsBenchmark : AbstractRunBlockingAwaitAsyncsBenchmark() { @Benchmark - fun runBlockingAwait1MAsyncs() = - runBlocking { await1MAasyncs() } + override fun runBlockingAwait1mAsyncs() = + runBlocking { await1mAsyncs() } + + @Benchmark + override fun runBlockingAwait1KAsync1mSums() = + runBlocking { await1kAsync1mSums() } } \ No newline at end of file