140 lines
5.1 KiB
Kotlin
140 lines
5.1 KiB
Kotlin
/*
|
|
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
|
|
package net.sergeych.lyng
|
|
|
|
import net.sergeych.lyng.obj.Obj
|
|
import net.sergeych.lyng.obj.ObjInt
|
|
import java.io.File
|
|
import kotlin.system.measureNanoTime
|
|
import kotlin.test.Test
|
|
|
|
/**
|
|
* A/B micro-benchmark for index WRITE paths (Map[String] put, List[Int] set).
|
|
* Measures OFF vs ON for INDEX_PIC and then size 2 vs 4 (INDEX_PIC_SIZE_4).
|
|
* Produces [DEBUG_LOG] output in lynglib/build/index_write_ab_results.txt
|
|
*/
|
|
class IndexWritePathABTest {
|
|
|
|
private fun outFile(): File = File("lynglib/build/index_write_ab_results.txt")
|
|
|
|
private fun writeHeader(f: File) {
|
|
if (!f.parentFile.exists()) f.parentFile.mkdirs()
|
|
f.writeText("[DEBUG_LOG] Index WRITE PIC A/B results\n")
|
|
}
|
|
|
|
private fun appendLine(f: File, s: String) { f.appendText(s + "\n") }
|
|
|
|
private suspend fun buildMapWriteScript(keys: Int, iters: Int): Script {
|
|
// Construct map with keys k0..k{keys-1} and then perform writes in a tight loop
|
|
val initEntries = (0 until keys).joinToString(", ") { i -> "\"k$i\" => $i" }
|
|
val src = """
|
|
var acc = 0
|
|
val m = Map($initEntries)
|
|
for(i in 0..${iters - 1}) {
|
|
val k = "k" + (i % $keys)
|
|
m[k] = i
|
|
acc += (m[k] ?: 0)
|
|
}
|
|
acc
|
|
""".trimIndent()
|
|
return Compiler.compile(Source("<idx-map-write>", src), Script.defaultImportManager)
|
|
}
|
|
|
|
private suspend fun buildListWriteScript(len: Int, iters: Int): Script {
|
|
val initList = (0 until len).joinToString(", ") { i -> i.toString() }
|
|
val src = """
|
|
var acc = 0
|
|
val a = [$initList]
|
|
for(i in 0..${iters - 1}) {
|
|
val j = i % $len
|
|
a[j] = i
|
|
acc += a[j]
|
|
}
|
|
acc
|
|
""".trimIndent()
|
|
return Compiler.compile(Source("<idx-list-write>", src), Script.defaultImportManager)
|
|
}
|
|
|
|
private suspend fun runOnce(script: Script): Long {
|
|
val scope = Script.newScope()
|
|
var result: Obj? = null
|
|
val t = measureNanoTime { result = script.execute(scope) }
|
|
if (result !is ObjInt) println("[DEBUG_LOG] result=${result?.javaClass?.simpleName}")
|
|
return t
|
|
}
|
|
|
|
@Test
|
|
fun ab_index_write_paths() = runTestBlocking {
|
|
val f = outFile()
|
|
writeHeader(f)
|
|
|
|
val savedIndexPic = PerfFlags.INDEX_PIC
|
|
val savedIndexSize4 = PerfFlags.INDEX_PIC_SIZE_4
|
|
val savedCounters = PerfFlags.PIC_DEBUG_COUNTERS
|
|
|
|
try {
|
|
val iters = 250_000
|
|
val mapKeys = 256
|
|
val listLen = 1024
|
|
val mScript = buildMapWriteScript(mapKeys, iters)
|
|
val lScript = buildListWriteScript(listLen, iters)
|
|
|
|
fun header(which: String) { appendLine(f, "[DEBUG_LOG] A/B on $which (iters=$iters)") }
|
|
|
|
// Baseline OFF
|
|
PerfFlags.PIC_DEBUG_COUNTERS = true
|
|
PerfStats.resetAll()
|
|
PerfFlags.INDEX_PIC = false
|
|
PerfFlags.INDEX_PIC_SIZE_4 = false
|
|
header("Map[String] write, INDEX_PIC=OFF")
|
|
val tMOff = runOnce(mScript)
|
|
header("List[Int] write, INDEX_PIC=OFF")
|
|
val tLOff = runOnce(lScript)
|
|
appendLine(f, "[DEBUG_LOG] OFF counters: indexHit=${PerfStats.indexPicHit} indexMiss=${PerfStats.indexPicMiss}")
|
|
|
|
// PIC ON, size 2
|
|
PerfStats.resetAll()
|
|
PerfFlags.INDEX_PIC = true
|
|
PerfFlags.INDEX_PIC_SIZE_4 = false
|
|
val tMOn2 = runOnce(mScript)
|
|
val tLOn2 = runOnce(lScript)
|
|
appendLine(f, "[DEBUG_LOG] ON size=2 counters: indexHit=${PerfStats.indexPicHit} indexMiss=${PerfStats.indexPicMiss}")
|
|
|
|
// PIC ON, size 4
|
|
PerfStats.resetAll()
|
|
PerfFlags.INDEX_PIC = true
|
|
PerfFlags.INDEX_PIC_SIZE_4 = true
|
|
val tMOn4 = runOnce(mScript)
|
|
val tLOn4 = runOnce(lScript)
|
|
appendLine(f, "[DEBUG_LOG] ON size=4 counters: indexHit=${PerfStats.indexPicHit} indexMiss=${PerfStats.indexPicMiss}")
|
|
|
|
// Report
|
|
appendLine(f, "[DEBUG_LOG] Map[String] WRITE OFF=$tMOff ns, ON(2)=$tMOn2 ns, ON(4)=$tMOn4 ns")
|
|
appendLine(f, "[DEBUG_LOG] List[Int] WRITE OFF=$tLOff ns, ON(2)=$tLOn2 ns, ON(4)=$tLOn4 ns")
|
|
} finally {
|
|
PerfFlags.INDEX_PIC = savedIndexPic
|
|
PerfFlags.INDEX_PIC_SIZE_4 = savedIndexSize4
|
|
PerfFlags.PIC_DEBUG_COUNTERS = savedCounters
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun runTestBlocking(block: suspend () -> Unit) {
|
|
kotlinx.coroutines.runBlocking { block() }
|
|
}
|