From 263cc5c6d7ea7b1a0be02ed68b7df5965d0d534a Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 20 Nov 2024 04:40:33 +0800 Subject: [PATCH 1/4] Print the number of processors and add a comment in `multiThreadMultiConnectionEach10KLocalTransactions` --- .../huanshankeji/exposed/benchmark/TransactionBenchmark.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt b/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt index 928295c..e2183c0 100644 --- a/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt +++ b/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt @@ -57,7 +57,10 @@ class TransactionBenchmark : WithContainerizedDatabaseBenchmark() { @Benchmark fun multiThreadMultiConnectionEach10KLocalTransactions() { - List(Runtime.getRuntime().availableProcessors()) { + // Note that on a device with heterogeneous architecture some threads may finish earlier than others. + List(Runtime.getRuntime().availableProcessors().also { + println("Number of processors: $it") + }) { thread { val database = databaseConnect() repeat(`10K`) { transaction(database) {} } From 210529d7c845f1d7a24287e32c11b333161128a3 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 20 Nov 2024 05:49:57 +0800 Subject: [PATCH 2/4] Add `multiThreadConcurrent10KTransactionsWithThreadLocalDatabases` and `multiThreadConcurrent10KTransactionsWithImplicitThreadLocalDatabases` into the benchmark code, fix `singleThreadConcurrent10KTransactions` which actually runs on multiple threads instead of one BTW, and improve `multiThreadConcurrent10KTransactionsWithSharedDatabase` BTW --- .../exposed/benchmark/TransactionBenchmark.kt | 63 ++++++++++++++----- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt b/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt index e2183c0..cf9c0bd 100644 --- a/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt +++ b/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt @@ -1,13 +1,13 @@ package com.huanshankeji.exposed.benchmark import com.huanshankeji.kotlinx.coroutine.awaitAny -import kotlinx.benchmark.Benchmark -import kotlinx.benchmark.Scope -import kotlinx.benchmark.State +import kotlinx.benchmark.* import kotlinx.coroutines.* +import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction import org.jetbrains.exposed.sql.transactions.experimental.suspendedTransactionAsync import org.jetbrains.exposed.sql.transactions.transaction +import java.util.concurrent.Executors import kotlin.concurrent.thread @State(Scope.Benchmark) @@ -32,17 +32,16 @@ class TransactionBenchmark : WithContainerizedDatabaseBenchmark() { } @Benchmark - fun singleThreadConcurrent10KTransactions() = runBlocking { - awaitAsync10KTransactions() - } + fun singleThreadConcurrent10KTransactions() = + @OptIn(ExperimentalCoroutinesApi::class, DelicateCoroutinesApi::class) + runBlocking(newSingleThreadContext("single thread")) { + awaitAsync10KTransactions() + } @Benchmark - fun multiThreadConcurrent10KTransactions() = runBlocking { - withContext(Dispatchers.Default) { - awaitAsync10KTransactions() - } - } + fun multiThreadConcurrent10KTransactionsWithSharedDatabase() = + runBlocking { awaitAsync10KTransactions() } @Benchmark @@ -55,12 +54,15 @@ class TransactionBenchmark : WithContainerizedDatabaseBenchmark() { List(`10K`) { suspendedTransactionAsync(db = database) {} }.awaitAny() } + private fun numProcessors() = + Runtime.getRuntime().availableProcessors().also { + println("Number of processors: $it") + } + @Benchmark fun multiThreadMultiConnectionEach10KLocalTransactions() { // Note that on a device with heterogeneous architecture some threads may finish earlier than others. - List(Runtime.getRuntime().availableProcessors().also { - println("Number of processors: $it") - }) { + List(numProcessors()) { thread { val database = databaseConnect() repeat(`10K`) { transaction(database) {} } @@ -69,4 +71,37 @@ class TransactionBenchmark : WithContainerizedDatabaseBenchmark() { it.join() } } + + + val databaseThreadLocal = ThreadLocal() + lateinit var dispatcherWithThreadLocalDatabases: ExecutorCoroutineDispatcher + + @Setup + fun setUpThreadLocalDatabases() { + dispatcherWithThreadLocalDatabases = Executors.newFixedThreadPool(numProcessors()) { + Thread { + it.run() + databaseThreadLocal.set(databaseConnect()) + } + }.asCoroutineDispatcher() + } + + @TearDown + fun teardownDispatcherWithThreadLocalDatabases() { + dispatcherWithThreadLocalDatabases.close() + } + + @Benchmark + fun multiThreadConcurrent10KTransactionsWithThreadLocalDatabases() { + runBlocking(dispatcherWithThreadLocalDatabases) { + List(`10K`) { async { transaction(databaseThreadLocal.get()) {} } }.awaitAll() + } + } + + @Benchmark + fun multiThreadConcurrent10KTransactionsWithImplicitThreadLocalDatabases() { + runBlocking(dispatcherWithThreadLocalDatabases) { + List(`10K`) { async { transaction {} } }.awaitAll() + } + } } \ No newline at end of file From 3715550f6f5dfda78678a33e7008b67d6253d53b Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 20 Nov 2024 05:51:22 +0800 Subject: [PATCH 3/4] Fix a bug that `awaitAny` is used --- .../com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt b/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt index cf9c0bd..82303b8 100644 --- a/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt +++ b/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt @@ -1,6 +1,5 @@ package com.huanshankeji.exposed.benchmark -import com.huanshankeji.kotlinx.coroutine.awaitAny import kotlinx.benchmark.* import kotlinx.coroutines.* import org.jetbrains.exposed.sql.Database @@ -51,7 +50,7 @@ class TransactionBenchmark : WithContainerizedDatabaseBenchmark() { @Benchmark fun _10KSuspendedTransactionAsyncs() = runBlocking { - List(`10K`) { suspendedTransactionAsync(db = database) {} }.awaitAny() + List(`10K`) { suspendedTransactionAsync(db = database) {} }.awaitAll() } private fun numProcessors() = From 920627f9e8713ec203c96584ac3783cfb1b1770d Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 20 Nov 2024 06:00:35 +0800 Subject: [PATCH 4/4] Make `CoroutineScope` the receiver in `awaitAsync10KTransactions` and extract `awaitAsync10K` --- .../exposed/benchmark/TransactionBenchmark.kt | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt b/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt index 82303b8..2a29d3f 100644 --- a/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt +++ b/integrated/src/benchmarks/kotlin/com/huanshankeji/exposed/benchmark/TransactionBenchmark.kt @@ -25,10 +25,13 @@ class TransactionBenchmark : WithContainerizedDatabaseBenchmark() { repeat(`10K`) { transaction(database) {} } } - private suspend fun awaitAsync10KTransactions() = - coroutineScope { - List(`10K`) { async { transaction(database) {} } }.awaitAll() - } + @Suppress("SuspendFunctionOnCoroutineScope") + private suspend inline fun CoroutineScope.awaitAsync10K(crossinline block: () -> Unit) = + List(`10K`) { async { block() } }.awaitAll() + + @Suppress("SuspendFunctionOnCoroutineScope") + private suspend fun CoroutineScope.awaitAsync10KTransactions() = + awaitAsync10K { transaction(database) {} } @Benchmark fun singleThreadConcurrent10KTransactions() = @@ -93,14 +96,14 @@ class TransactionBenchmark : WithContainerizedDatabaseBenchmark() { @Benchmark fun multiThreadConcurrent10KTransactionsWithThreadLocalDatabases() { runBlocking(dispatcherWithThreadLocalDatabases) { - List(`10K`) { async { transaction(databaseThreadLocal.get()) {} } }.awaitAll() + awaitAsync10K { transaction(databaseThreadLocal.get()) {} } } } @Benchmark fun multiThreadConcurrent10KTransactionsWithImplicitThreadLocalDatabases() { runBlocking(dispatcherWithThreadLocalDatabases) { - List(`10K`) { async { transaction {} } }.awaitAll() + awaitAsync10K { transaction {} } } } } \ No newline at end of file