From 9add83ed4f616d665fa3629dce73cb9919ec1e84 Mon Sep 17 00:00:00 2001 From: Shreck Ye Date: Tue, 29 Oct 2024 02:07:04 +0800 Subject: [PATCH] Try fixing the performance issues of the "vertx-web-kotlinx" portion in the "single query" and "JSON serialization" tests in the [Continuous Benchmarking results](https://tfb-status.techempower.com/) by using static kotlinx.serialization serializers The "vertx-web-kotlinx" results in the Continuous Benchmarking results are much lower than those of the "vertx-web-kotlin-coroutines" portion. See [the latest results](https://www.techempower.com/benchmarks/#section=test&runid=592cab59-a9db-463b-a9c9-33d2f9484e92&hw=ph&test=db) for example. Looking at the "single query" results, I first suspected that it was caused by there being not enough memory for the JVM runtime, so I added some logging code that prints the memory usage using `Runtime.totalMemory` and `Runtime.maxMemory`. It showed that there was about 7 GB max memory available during the benchmark runs, and the program only used 400 MB to 1 GB. I then tried allocating a 4 GB array during the run to ensure that the memory was usable and it worked with no problem. Then looking at the "JSON serialization" results again, I saw that "vertx-web-kotlinx" performs a lot worse in this test too, and decided that this is more likely to be the bottleneck. Therefore, the static serializers are provided explicitly and the performance is improved slightly as tested on my machine. (Also, see commit 315b4e359cde7909810a6a842ae3c7d49233291f for an attempt before.) I then copied the "JSON serialization" test code from "vertx-web-kotlin-coroutines" and ran the benchmark to see if there were other factors, such as project configuration differences, affecting the performance, and the answer was no. On my machine, the "JSON serialization" performance of "vertx-web-kotlinx" is about 80% - 85% of that of "vertx-web-kotlin-coroutines". And I think the bottleneck possibly lies in kotlinx.serialization serializing an object to a byte array first and then copying it to a Vert.x buffer. Remove the broken tag in "vertx-web-kotlin-coroutines" BTW, which was added in commit e53e0260e522106b316d8fce2e378a056d866356, for the benchmark runs without problems now as I tested. --- .../benchmark_config.json | 3 +-- .../src/main/kotlin/MainVerticle.kt | 16 +++++++++------- .../src/main/kotlin/Serializers.kt | 7 +++++++ 3 files changed, 17 insertions(+), 9 deletions(-) create mode 100644 frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/Serializers.kt diff --git a/frameworks/Kotlin/vertx-web-kotlin-coroutines/benchmark_config.json b/frameworks/Kotlin/vertx-web-kotlin-coroutines/benchmark_config.json index d2bab14abb7..2b8ebd26c83 100644 --- a/frameworks/Kotlin/vertx-web-kotlin-coroutines/benchmark_config.json +++ b/frameworks/Kotlin/vertx-web-kotlin-coroutines/benchmark_config.json @@ -18,8 +18,7 @@ "database_os": "Linux", "display_name": "vertx-web-kotlin-coroutines", "notes": "", - "versus": "vertx-web", - "tags": ["broken"] + "versus": "vertx-web" }, "postgres": { "db_url": "/db", diff --git a/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/MainVerticle.kt b/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/MainVerticle.kt index 35ea26b9ed3..25a6fd1ba51 100644 --- a/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/MainVerticle.kt +++ b/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/MainVerticle.kt @@ -20,7 +20,7 @@ import kotlinx.coroutines.launch import kotlinx.html.* import kotlinx.html.stream.appendHTML import kotlinx.serialization.Serializable -import kotlinx.serialization.encodeToString +import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.json.Json import java.net.SocketException import java.time.ZonedDateTime @@ -111,11 +111,13 @@ class MainVerticle(val hasDb: Boolean) : CoroutineVerticle() { putHeader(HttpHeaders.CONTENT_TYPE, "application/json") } - inline fun Route.jsonResponseHandler(crossinline requestHandler: suspend (RoutingContext) -> @Serializable T) = + inline fun Route.jsonResponseHandler( + serializer: SerializationStrategy, crossinline requestHandler: suspend (RoutingContext) -> @Serializable T + ) = checkedCoroutineHandlerUnconfined { it.response().run { putJsonResponseHeader() - end(Json.encodeToString(requestHandler(it)))/*.coAwait()*/ + end(Json.encodeToString(serializer, requestHandler(it)))/*.coAwait()*/ } } @@ -127,16 +129,16 @@ class MainVerticle(val hasDb: Boolean) : CoroutineVerticle() { } fun Router.routes() { - get("/json").jsonResponseHandler { + get("/json").jsonResponseHandler(Serializers.message) { jsonSerializationMessage } - get("/db").jsonResponseHandler { + get("/db").jsonResponseHandler(Serializers.world) { val rowSet = selectWorldQuery.execute(Tuple.of(randomIntBetween1And10000())).coAwait() rowSet.single().toWorld() } - get("/queries").jsonResponseHandler { + get("/queries").jsonResponseHandler(Serializers.worlds) { val queries = it.request().getQueries() selectRandomWorlds(queries) } @@ -178,7 +180,7 @@ class MainVerticle(val hasDb: Boolean) : CoroutineVerticle() { } } - get("/updates").jsonResponseHandler { + get("/updates").jsonResponseHandler(Serializers.worlds) { val queries = it.request().getQueries() val worlds = selectRandomWorlds(queries) val updatedWorlds = worlds.map { it.copy(randomNumber = randomIntBetween1And10000()) } diff --git a/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/Serializers.kt b/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/Serializers.kt new file mode 100644 index 00000000000..c975fc07fdd --- /dev/null +++ b/frameworks/Kotlin/vertx-web-kotlinx/src/main/kotlin/Serializers.kt @@ -0,0 +1,7 @@ +import kotlinx.serialization.serializer + +object Serializers { + val message = serializer() + val world = serializer() + val worlds = serializer>() +} \ No newline at end of file