From ef6875ab1584d8075402b53cf1242dca1ae52261 Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Mon, 6 Oct 2025 16:55:56 +0200 Subject: [PATCH 1/4] Fix compatibility with the Kotlin Gradle Plugin Migrate deprecated APIs to actual ones and cleanup imports. --- build.gradle.kts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 90de4ed..01b90b0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,24 +1,21 @@ @file:OptIn(KotlinxBenchmarkPluginInternalApi::class, ExperimentalWasmDsl::class) -import kotlinx.benchmark.gradle.* import de.undercouch.gradle.tasks.download.Download +import kotlinx.benchmark.gradle.* import kotlinx.benchmark.gradle.internal.KotlinxBenchmarkPluginInternalApi import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType -import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.targets.js.binaryen.BinaryenRootEnvSpec import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBinaryMode import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinWasmSubTargetContainerDsl -import org.jetbrains.kotlin.gradle.targets.js.ir.JsIrBinary -import org.jetbrains.kotlin.gradle.targets.js.d8.D8Plugin -import org.jetbrains.kotlin.gradle.targets.js.binaryen.BinaryenRootPlugin -import org.jetbrains.kotlin.gradle.targets.js.d8.D8EnvSpec -import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget +import org.jetbrains.kotlin.gradle.targets.js.ir.* import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsEnvSpec import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension -import org.jetbrains.kotlin.gradle.targets.js.ir.* +import org.jetbrains.kotlin.gradle.targets.wasm.binaryen.BinaryenPlugin +import org.jetbrains.kotlin.gradle.targets.wasm.d8.D8EnvSpec +import org.jetbrains.kotlin.gradle.targets.wasm.d8.D8Plugin buildscript { repositories { @@ -49,7 +46,7 @@ the().apply { version.set("23.6.0") } -apply() +apply() the().version.set("123") apply() From 7b37e96a5e9aa980ef70783e1e02740c059990b8 Mon Sep 17 00:00:00 2001 From: alex28sh Date: Tue, 30 Sep 2025 17:59:56 +0200 Subject: [PATCH 2/4] add WhenBenchmark --- .../kotlin/microBenchmarks/WhenBenchmark.kt | 194 ++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 src/commonMain/kotlin/microBenchmarks/WhenBenchmark.kt diff --git a/src/commonMain/kotlin/microBenchmarks/WhenBenchmark.kt b/src/commonMain/kotlin/microBenchmarks/WhenBenchmark.kt new file mode 100644 index 0000000..0fc6164 --- /dev/null +++ b/src/commonMain/kotlin/microBenchmarks/WhenBenchmark.kt @@ -0,0 +1,194 @@ +package microBenchmarks + +import kotlinx.benchmark.* + +@State(Scope.Benchmark) +class WhenBenchmark { + + private val integers = 100500..100510 + private val shorts = (10500..10510).map { it.toShort() } + private val bytes = (100..110).map { it.toByte() } + private val chars = 'a'..'k' + private val strings = ('a' .. 'k').flatMap {a -> ('a' .. 'k').map { b -> "$a$b" } } + + private val floatConst = 1.123.toFloat() + private val doubleConst = 1.123456789123456 + + private val floats = (1..10).map { it * floatConst } + private val doubles = (1..10).map { it * doubleConst } + + private val integersData = mutableListOf() + private val shortsData = mutableListOf() + private val bytesData = mutableListOf() + private val charsData = mutableListOf() + private val stringsData = mutableListOf() + private val floatsData = mutableListOf() + private val doublesData = mutableListOf() + + @Setup + fun setup() { + for (i in 1..BENCHMARK_SIZE) { + charsData.add(chars.random()) + shortsData.add(shorts.random()) + bytesData.add(bytes.random()) + integersData.add(integers.random()) + stringsData.add(strings.random()) + floatsData.add(floats.random()) + doublesData.add(doubles.random()) + } + } + + @Benchmark + fun charWhenDense(): Int { + var sum = 0 + for (char in charsData) { + when(char) { + 'a' -> sum += 13 + 'c' -> sum += 91 + 'e' -> sum += 34 + else -> sum += 29 + } + } + return sum + } + + @Benchmark + fun charWhenSparse(): Int { + var sum = 0 + for (char in charsData) { + sum += when(char) { + 'a' -> 13 + 'f' -> 34 + 'k' -> 91 + else -> 29 + } + } + return sum + } + + @Benchmark + fun intWhenDense(): Int { + var sum = 0 + for (i in integersData) { + sum += when(i) { + 100500 -> 13 + 100502 -> 91 + 100504 -> 34 + else -> 29 + } + } + return sum + } + + @Benchmark + fun intWhenSparse(): Int { + var sum = 0 + for (int in integersData) { + sum += when(int) { + 100500 -> 13 + 100505 -> 91 + 100510 -> 34 + else -> 29 + } + } + return sum + } + + @Benchmark + fun shortWhenDense(): Int { + var sum = 0 + for (short in shortsData) { + sum += when(short) { + 10500.toShort() -> 13 + 10502.toShort() -> 91 + 10504.toShort() -> 34 + else -> 29 + } + } + return sum + } + + @Benchmark + fun shortWhenSparse(): Int { + var sum = 0 + for (short in shortsData) { + sum += when(short) { + 10500.toShort() -> 13 + 10505.toShort() -> 91 + 10510.toShort() -> 34 + else -> 29 + } + } + return sum + } + + @Benchmark + fun byteWhenDense(): Int { + var sum = 0 + for (byte in bytesData) { + sum += when(byte) { + 100.toByte() -> 13 + 102.toByte() -> 91 + 104.toByte() -> 34 + else -> 29 + } + } + return sum + } + + @Benchmark + fun byteWhenSparse(): Int { + var sum = 0 + for (byte in bytesData) { + sum += when(byte) { + 100.toByte() -> 13 + 105.toByte() -> 91 + 110.toByte() -> 34 + else -> 29 + } + } + return sum + } + + @Benchmark + fun stringWhen(): Int { + var sum = 0 + for (string in stringsData) { + sum += when(string) { + "aa" -> 13 + "bk" -> 91 + "fg" -> 34 + else -> 29 + } + } + return sum + } + + @Benchmark + fun floatWhen(): Int { + var sum = 0 + for (float in floatsData) { + sum += when(float) { + floatConst -> 13 + floatConst * 3 -> 91 + floatConst * 4 -> 34 + else -> 29 + } + } + return sum + } + + @Benchmark + fun doubleWhen(): Int { + var sum = 0 + for (double in doublesData) { + sum += when(double) { + doubleConst -> 13 + doubleConst * 3 -> 91 + doubleConst * 4 -> 34 + else -> 29 + } + } + return sum + } +} \ No newline at end of file From 96d4e273b69ad2242cd27946c9bc01e39c03da31 Mon Sep 17 00:00:00 2001 From: alex28sh Date: Tue, 7 Oct 2025 12:47:10 +0200 Subject: [PATCH 3/4] move all data to be initialized in setup (WhenBenchmark) --- .../kotlin/microBenchmarks/WhenBenchmark.kt | 67 +++++++++++-------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/src/commonMain/kotlin/microBenchmarks/WhenBenchmark.kt b/src/commonMain/kotlin/microBenchmarks/WhenBenchmark.kt index 0fc6164..056b8eb 100644 --- a/src/commonMain/kotlin/microBenchmarks/WhenBenchmark.kt +++ b/src/commonMain/kotlin/microBenchmarks/WhenBenchmark.kt @@ -5,17 +5,17 @@ import kotlinx.benchmark.* @State(Scope.Benchmark) class WhenBenchmark { - private val integers = 100500..100510 - private val shorts = (10500..10510).map { it.toShort() } - private val bytes = (100..110).map { it.toByte() } - private val chars = 'a'..'k' - private val strings = ('a' .. 'k').flatMap {a -> ('a' .. 'k').map { b -> "$a$b" } } + private lateinit var integers: List + private lateinit var shorts: List + private lateinit var bytes: List + private lateinit var chars: List + private lateinit var strings: List - private val floatConst = 1.123.toFloat() - private val doubleConst = 1.123456789123456 + private var floatConst: Float = 0F + private var doubleConst: Double = 0.0 - private val floats = (1..10).map { it * floatConst } - private val doubles = (1..10).map { it * doubleConst } + private lateinit var floats: List + private lateinit var doubles: List private val integersData = mutableListOf() private val shortsData = mutableListOf() @@ -27,14 +27,27 @@ class WhenBenchmark { @Setup fun setup() { + + integers = (100500..100510).toList() + shorts = (10500..10510).map { it.toShort() } + bytes = (100..110).map { it.toByte() } + chars = ('a'..'k').toList() + strings = ('a' .. 'k').flatMap { a -> ('a' .. 'k').map { b -> "$a$b" } } + + floatConst = 1.123F + doubleConst = 1.123456789123456 + + floats = (1..10).map { it * floatConst } + doubles = (1..10).map { it * doubleConst } + for (i in 1..BENCHMARK_SIZE) { - charsData.add(chars.random()) - shortsData.add(shorts.random()) - bytesData.add(bytes.random()) - integersData.add(integers.random()) - stringsData.add(strings.random()) - floatsData.add(floats.random()) - doublesData.add(doubles.random()) + charsData.add(chars[i % chars.size]) + shortsData.add(shorts[i % shorts.size]) + bytesData.add(bytes[i % bytes.size]) + integersData.add(integers[i % integers.size]) + stringsData.add(strings[i % strings.size]) + floatsData.add(floats[i % floats.size]) + doublesData.add(doubles[i % doubles.size]) } } @@ -42,11 +55,11 @@ class WhenBenchmark { fun charWhenDense(): Int { var sum = 0 for (char in charsData) { - when(char) { - 'a' -> sum += 13 - 'c' -> sum += 91 - 'e' -> sum += 34 - else -> sum += 29 + sum += when(char) { + 'a' -> 13 + 'c' -> 91 + 'e' -> 34 + else -> 29 } } return sum @@ -169,9 +182,9 @@ class WhenBenchmark { var sum = 0 for (float in floatsData) { sum += when(float) { - floatConst -> 13 - floatConst * 3 -> 91 - floatConst * 4 -> 34 + 1.123F -> 13 + 3.369F -> 91 + 4.492F -> 34 else -> 29 } } @@ -183,9 +196,9 @@ class WhenBenchmark { var sum = 0 for (double in doublesData) { sum += when(double) { - doubleConst -> 13 - doubleConst * 3 -> 91 - doubleConst * 4 -> 34 + 1.123 -> 13 + 3.369 -> 91 + 4.492 -> 34 else -> 29 } } From 44f60d991ef3bb729c9aa974f0ddbe9c1997c3f4 Mon Sep 17 00:00:00 2001 From: alex28sh Date: Thu, 9 Oct 2025 11:11:40 +0200 Subject: [PATCH 4/4] make branch transitions even + reduce allocation influence --- .../kotlin/microBenchmarks/WhenBenchmark.kt | 154 +++++++++++------- 1 file changed, 98 insertions(+), 56 deletions(-) diff --git a/src/commonMain/kotlin/microBenchmarks/WhenBenchmark.kt b/src/commonMain/kotlin/microBenchmarks/WhenBenchmark.kt index 056b8eb..1a557f3 100644 --- a/src/commonMain/kotlin/microBenchmarks/WhenBenchmark.kt +++ b/src/commonMain/kotlin/microBenchmarks/WhenBenchmark.kt @@ -5,60 +5,80 @@ import kotlinx.benchmark.* @State(Scope.Benchmark) class WhenBenchmark { - private lateinit var integers: List - private lateinit var shorts: List - private lateinit var bytes: List - private lateinit var chars: List - private lateinit var strings: List + private lateinit var integersDense: IntArray + private lateinit var integersSparse: IntArray + private lateinit var shortsDense: ShortArray + private lateinit var shortsSparse: ShortArray + private lateinit var bytesDense: ByteArray + private lateinit var bytesSparse: ByteArray + private lateinit var charsDense: CharArray + private lateinit var charsSparse: CharArray + private lateinit var strings: Array private var floatConst: Float = 0F private var doubleConst: Double = 0.0 - private lateinit var floats: List - private lateinit var doubles: List - - private val integersData = mutableListOf() - private val shortsData = mutableListOf() - private val bytesData = mutableListOf() - private val charsData = mutableListOf() - private val stringsData = mutableListOf() - private val floatsData = mutableListOf() - private val doublesData = mutableListOf() + private lateinit var floats: FloatArray + private lateinit var doubles: DoubleArray + + private lateinit var integersDataDense: IntArray + private lateinit var integersDataSparse: IntArray + private lateinit var shortsDataDense: ShortArray + private lateinit var shortsDataSparse: ShortArray + private lateinit var bytesDataDense: ByteArray + private lateinit var bytesDataSparse: ByteArray + private lateinit var charsDataDense: CharArray + private lateinit var charsDataSparse: CharArray + private lateinit var stringsData: Array + private lateinit var floatsData: FloatArray + private lateinit var doublesData: DoubleArray @Setup fun setup() { - integers = (100500..100510).toList() - shorts = (10500..10510).map { it.toShort() } - bytes = (100..110).map { it.toByte() } - chars = ('a'..'k').toList() - strings = ('a' .. 'k').flatMap { a -> ('a' .. 'k').map { b -> "$a$b" } } + integersDense = intArrayOf(100500, 100502, 100504, 100506) + integersSparse = intArrayOf(100500, 100505, 100510, 100512) + shortsDense = shortArrayOf(10500, 10502, 10504, 10506) + shortsSparse = shortArrayOf(10500, 10505, 10510, 10512) + bytesDense = byteArrayOf(100, 102, 104, 106) + bytesSparse = byteArrayOf(100, 105, 110, 112) + charsDense = charArrayOf('a', 'b', 'c', 'e', 'f', 'h') + charsSparse = charArrayOf('a', 'b', 'f', 'k', 't', 'z') + strings = arrayOf("aa", "bk", "fg", "eg") floatConst = 1.123F doubleConst = 1.123456789123456 - floats = (1..10).map { it * floatConst } - doubles = (1..10).map { it * doubleConst } - - for (i in 1..BENCHMARK_SIZE) { - charsData.add(chars[i % chars.size]) - shortsData.add(shorts[i % shorts.size]) - bytesData.add(bytes[i % bytes.size]) - integersData.add(integers[i % integers.size]) - stringsData.add(strings[i % strings.size]) - floatsData.add(floats[i % floats.size]) - doublesData.add(doubles[i % doubles.size]) - } + floats = FloatArray(10) { (it + 1) * floatConst } + doubles = DoubleArray(10) { (it + 1) * doubleConst } + + val size = BENCHMARK_SIZE * 10 + + integersDataDense = IntArray(size) { i -> integersDense[i % integersDense.size] } + integersDataSparse = IntArray(size) { i -> integersSparse[i % integersSparse.size] } + charsDataDense = CharArray(size) { i -> charsDense[i % charsDense.size] } + charsDataSparse = CharArray(size) { i -> charsSparse[i % charsSparse.size] } + shortsDataDense = ShortArray(size) { i -> shortsDense[i % shortsDense.size] } + shortsDataSparse = ShortArray(size) { i -> shortsSparse[i % shortsSparse.size] } + bytesDataDense = ByteArray(size) { i -> bytesDense[i % bytesDense.size] } + bytesDataSparse = ByteArray(size) { i -> bytesSparse[i % bytesSparse.size] } + stringsData = Array(size) { i -> strings[i % strings.size] } + floatsData = FloatArray(size) { i -> floats[i % floats.size] } + doublesData = DoubleArray(size) { i -> doubles[i % doubles.size] } } @Benchmark fun charWhenDense(): Int { var sum = 0 - for (char in charsData) { - sum += when(char) { + val data = charsDataDense + for (i in 0 until data.size) { + val char = data[i] + sum += when (char) { 'a' -> 13 - 'c' -> 91 - 'e' -> 34 + 'b' -> 91 + 'c' -> 34 + 'e' -> 231 + 'h' -> 233 else -> 29 } } @@ -68,11 +88,15 @@ class WhenBenchmark { @Benchmark fun charWhenSparse(): Int { var sum = 0 - for (char in charsData) { - sum += when(char) { + val data = charsDataSparse + for (i in 0 until data.size) { + val char = data[i] + sum += when (char) { 'a' -> 13 'f' -> 34 'k' -> 91 + 't' -> 231 + 'z' -> 233 else -> 29 } } @@ -82,8 +106,10 @@ class WhenBenchmark { @Benchmark fun intWhenDense(): Int { var sum = 0 - for (i in integersData) { - sum += when(i) { + val data = integersDataDense + for (i in 0 until data.size) { + val v = data[i] + sum += when (v) { 100500 -> 13 100502 -> 91 100504 -> 34 @@ -96,8 +122,10 @@ class WhenBenchmark { @Benchmark fun intWhenSparse(): Int { var sum = 0 - for (int in integersData) { - sum += when(int) { + val data = integersDataSparse + for (i in 0 until data.size) { + val v = data[i] + sum += when (v) { 100500 -> 13 100505 -> 91 100510 -> 34 @@ -110,8 +138,10 @@ class WhenBenchmark { @Benchmark fun shortWhenDense(): Int { var sum = 0 - for (short in shortsData) { - sum += when(short) { + val data = shortsDataDense + for (i in 0 until data.size) { + val v = data[i] + sum += when (v) { 10500.toShort() -> 13 10502.toShort() -> 91 10504.toShort() -> 34 @@ -124,8 +154,10 @@ class WhenBenchmark { @Benchmark fun shortWhenSparse(): Int { var sum = 0 - for (short in shortsData) { - sum += when(short) { + val data = shortsDataSparse + for (i in 0 until data.size) { + val v = data[i] + sum += when (v) { 10500.toShort() -> 13 10505.toShort() -> 91 10510.toShort() -> 34 @@ -138,8 +170,10 @@ class WhenBenchmark { @Benchmark fun byteWhenDense(): Int { var sum = 0 - for (byte in bytesData) { - sum += when(byte) { + val data = bytesDataDense + for (i in 0 until data.size) { + val v = data[i] + sum += when (v) { 100.toByte() -> 13 102.toByte() -> 91 104.toByte() -> 34 @@ -152,8 +186,10 @@ class WhenBenchmark { @Benchmark fun byteWhenSparse(): Int { var sum = 0 - for (byte in bytesData) { - sum += when(byte) { + val data = bytesDataSparse + for (i in 0 until data.size) { + val v = data[i] + sum += when (v) { 100.toByte() -> 13 105.toByte() -> 91 110.toByte() -> 34 @@ -166,8 +202,10 @@ class WhenBenchmark { @Benchmark fun stringWhen(): Int { var sum = 0 - for (string in stringsData) { - sum += when(string) { + val data = stringsData + for (i in 0 until data.size) { + val s = data[i] + sum += when (s) { "aa" -> 13 "bk" -> 91 "fg" -> 34 @@ -180,8 +218,10 @@ class WhenBenchmark { @Benchmark fun floatWhen(): Int { var sum = 0 - for (float in floatsData) { - sum += when(float) { + val data = floatsData + for (i in 0 until data.size) { + val v = data[i] + sum += when (v) { 1.123F -> 13 3.369F -> 91 4.492F -> 34 @@ -194,8 +234,10 @@ class WhenBenchmark { @Benchmark fun doubleWhen(): Int { var sum = 0 - for (double in doublesData) { - sum += when(double) { + val data = doublesData + for (i in 0 until data.size) { + val v = data[i] + sum += when (v) { 1.123 -> 13 3.369 -> 91 4.492 -> 34