Browse Source

Some more small performance tweaks to the `vertx-web-kotlinx` portion (#10399)

* Fix the `updates` test performance

The details of the performance issue is mentioned in the commit message of commit 6b531dd2382f6ee99194f5346be602f44bf53b19.

Changes that mostly certainly affect performance:

1. Fix capitalization and formatting in the SQL strings.
1. Use the Coroutine `awaitAll` vararg variant in `selectRandomWorlds`.

Changes that might affect performance, but I am not sure:

1. Conditionally adding only needed routes.

* Add spaces around the equal sign to make the format consistent

* Use one Kotlin `XorWowRandom` `Random` instance every `Verticle` instead of the default global one (companion object `Random`) which delegates dynamically to Java `ThreadLocalRandom` to improve some performance

Looks like there is a 1%-3% improvement in the single query test result with "32 threads and 512 connections" as tested on my device.
Yongshun Ye 4 days ago
parent
commit
98485682be

+ 2 - 2
frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/Database.kt

@@ -1,8 +1,8 @@
 import io.vertx.sqlclient.Row
 
-const val SELECT_WORLD_SQL = "SELECT id, randomnumber from WORLD where id = $1"
+const val SELECT_WORLD_SQL = "SELECT id, randomnumber FROM world WHERE id = $1"
 const val UPDATE_WORLD_SQL = "UPDATE world SET randomnumber = $1 WHERE id = $2"
-const val SELECT_FORTUNE_SQL = "SELECT id, message from FORTUNE"
+const val SELECT_FORTUNE_SQL = "SELECT id, message FROM fortune"
 
 
 fun Row.toWorld() =

+ 31 - 20
frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/MainVerticle.kt

@@ -20,6 +20,8 @@ import io.vertx.sqlclient.Row
 import io.vertx.sqlclient.RowSet
 import io.vertx.sqlclient.Tuple
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.async
+import kotlinx.coroutines.awaitAll
 import kotlinx.datetime.UtcOffset
 import kotlinx.datetime.format.DateTimeComponents
 import kotlinx.datetime.format.format
@@ -32,6 +34,7 @@ import kotlinx.serialization.SerializationStrategy
 import kotlinx.serialization.json.Json
 import kotlinx.serialization.json.io.encodeToSink
 import java.net.SocketException
+import kotlin.random.Random
 import kotlin.time.Clock
 
 class MainVerticle(val hasDb: Boolean) : CoroutineVerticle(), CoroutineRouterSupport {
@@ -51,6 +54,8 @@ class MainVerticle(val hasDb: Boolean) : CoroutineVerticle(), CoroutineRouterSup
     lateinit var selectFortuneQuery: PreparedQuery<RowSet<Row>>
     lateinit var updateWorldQuery: PreparedQuery<RowSet<Row>>
 
+    val random = Random(0)
+
     fun setCurrentDate() {
         date = DateTimeComponents.Formats.RFC_1123.format {
             // We don't need a more complicated system `TimeZone` here (whose offset depends dynamically on the actual time due to DST) since UTC works.
@@ -156,22 +161,38 @@ class MainVerticle(val hasDb: Boolean) : CoroutineVerticle(), CoroutineRouterSup
             }
         }
 
+    suspend fun selectRandomWorld() =
+        selectWorldQuery.execute(Tuple.of(random.nextIntBetween1And10000())).coAwait()
+            .single().toWorld()
 
-    suspend fun selectRandomWorlds(queries: Int): List<World> {
-        val rowSets = List(queries) {
-            selectWorldQuery.execute(Tuple.of(randomIntBetween1And10000()))
-        }.awaitAll()
-        return rowSets.map { it.single().toWorld() }
-    }
+    suspend fun selectRandomWorlds(queries: Int): List<World> =
+    //List(queries) { async { selectRandomWorld() } }.awaitAll()
+        // This should be slightly more efficient.
+        awaitAll(*Array(queries) { async { selectRandomWorld() } })
+
+
+    fun Router.routes() =
+        if (!hasDb) routesWithoutDb() else routesWithDb()
 
-    fun Router.routes() {
+    fun Router.routesWithoutDb() {
         get("/json").jsonResponseCoHandler(Serializers.message) {
             Message("Hello, World!")
         }
 
+        get("/plaintext").coHandlerUnconfined {
+            it.response().run {
+                headers().run {
+                    addCommonHeaders()
+                    add(HttpHeaders.CONTENT_TYPE, HttpHeaderValues.textPlain)
+                }
+                end("Hello, World!")/*.coAwait()*/
+            }
+        }
+    }
+
+    fun Router.routesWithDb() {
         get("/db").jsonResponseCoHandler(Serializers.world) {
-            val rowSet = selectWorldQuery.execute(Tuple.of(randomIntBetween1And10000())).coAwait()
-            rowSet.single().toWorld()
+            selectRandomWorld()
         }
 
         get("/queries").jsonResponseCoHandler(Serializers.worlds) {
@@ -222,7 +243,7 @@ class MainVerticle(val hasDb: Boolean) : CoroutineVerticle(), CoroutineRouterSup
         get("/updates").jsonResponseCoHandler(Serializers.worlds) {
             val queries = it.request().getQueries()
             val worlds = selectRandomWorlds(queries)
-            val updatedWorlds = worlds.map { it.copy(randomNumber = randomIntBetween1And10000()) }
+            val updatedWorlds = worlds.map { it.copy(randomNumber = random.nextIntBetween1And10000()) }
 
             // Approach 1
             // The updated worlds need to be sorted first to avoid deadlocks.
@@ -238,15 +259,5 @@ class MainVerticle(val hasDb: Boolean) : CoroutineVerticle(), CoroutineRouterSup
 
             updatedWorlds
         }
-
-        get("/plaintext").coHandlerUnconfined {
-            it.response().run {
-                headers().run {
-                    addCommonHeaders()
-                    add(HttpHeaders.CONTENT_TYPE, HttpHeaderValues.textPlain)
-                }
-                end("Hello, World!")/*.coAwait()*/
-            }
-        }
     }
 }

+ 2 - 2
frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/Models.kt

@@ -7,7 +7,7 @@ class Message(val message: String)
 @Serializable
 data class World(val id: Int, val randomNumber: Int)
 
-fun randomIntBetween1And10000() =
-    Random.nextInt(1, 10001)
+fun Random.nextIntBetween1And10000() =
+    nextInt(1, 10001)
 
 class Fortune(val id: Int, val message: String)