[K/N] Benches for parallel mark: life + splay with mark helpers

Merge-request: KOTLIN-MR-669
Merged-by: Aleksej Glushko <aleksei.glushko@jetbrains.com>
This commit is contained in:
Aleksei.Glushko
2023-05-02 10:24:25 +00:00
committed by Space Cloud
parent 8173091fee
commit cfa481d38f
3 changed files with 184 additions and 0 deletions
@@ -62,6 +62,7 @@ class RingLauncher : Launcher() {
"Singleton.access" to BenchmarkEntryWithInit.create(::SingletonBenchmark, { access() }),
"Splay" to BenchmarkEntryWithInitAndValidation.create(::SplayBenchmark, { runSplay() }, { splayTearDown() }),
"SplayWithWorkers" to BenchmarkEntryWithInitAndValidation.create(::SplayBenchmarkUsingWorkers, { runSplayWorkers() }, { splayTearDownWorkers() }),
"SplayWithMarkHelpers" to BenchmarkEntryWithInitAndValidation.create(::SplayBenchmarkWithMarkHelpers, { runSplayWithMarkHelpers() }, { splayTearDownMarkHelpers() }),
"String.stringConcat" to BenchmarkEntryWithInit.create(::StringBenchmark, { stringConcat() }),
"String.stringBuilderConcat" to BenchmarkEntryWithInit.create(::StringBenchmark, { stringBuilderConcat() }),
"String.stringBuilderConcatNullable" to BenchmarkEntryWithInit.create(::StringBenchmark, { stringBuilderConcatNullable() }),
@@ -206,6 +207,8 @@ class RingLauncher : Launcher() {
"Lambda.mutatingLambda" to BenchmarkEntryWithInit.create(::LambdaBenchmark, { mutatingLambda() }),
"Lambda.mutatingLambdaNoInline" to BenchmarkEntryWithInit.create(::LambdaBenchmark, { mutatingLambdaNoInline() }),
"Lambda.methodReferenceNoInline" to BenchmarkEntryWithInit.create(::LambdaBenchmark, { methodReferenceNoInline() }),
"Life" to BenchmarkEntryWithInit.create(::LifeBenchmark, { bench() }),
"LifeWithMarkHelpers" to BenchmarkEntryWithInitAndValidation.create(::LifeWithMarkHelpersBenchmark, { bench() }, { terminate() }),
"Loop.arrayIndexLoop" to BenchmarkEntryWithInit.create(::LoopBenchmark, { arrayIndexLoop() }),
"Loop.arrayListLoop" to BenchmarkEntryWithInit.create(::LoopBenchmark, { arrayListLoop() }),
"ParameterNotNull.invokeOneArgWithNullCheck" to BenchmarkEntryWithInit.create(::ParameterNotNullAssertionBenchmark, { invokeOneArgWithNullCheck() }),
@@ -242,6 +245,8 @@ class RingLauncher : Launcher() {
@OptIn(kotlin.ExperimentalStdlibApi::class)
if (!isExperimentalMM()) {
baseBenchmarksSet -= listOf("SplayWithWorkers")
baseBenchmarksSet -= listOf("SplayWithMarkHelpers")
extendedBenchmarksSet -= listOf("LifeWithMarkHelpers")
}
}
}
@@ -0,0 +1,135 @@
package org.jetbrains.ring
import kotlin.native.concurrent.*
import kotlin.concurrent.*
import org.jetbrains.benchmarksLauncher.Blackhole
import org.jetbrains.benchmarksLauncher.Random
data class Pos(val i: Int, val j: Int)
data class Cell(val isAlive: Boolean = false)
class Generation(private val width: Int, private val height: Int) {
val cells = Array(height) { _ -> Array(width) { _ -> Cell() } }
fun evolve(): Generation {
val newGen = Generation(width, height)
for (i in 0 until height) {
for (j in 0 until width) {
val neighborhood = mutableListOf<Pos>()
for (di in -1..1) {
for (dj in -1..1) {
if (di != 0 || dj != 0) {
neighborhood.add(Pos(i + di, j + dj))
}
}
}
assert(neighborhood.size == 8)
val aliveNeighbours = neighborhood
.map { wrapOverEdge(it) }
.map { cells[it.i][it.j] }
.count { it.isAlive }
val newAlive =
if (cells[i][j].isAlive) aliveNeighbours in 2..3
else aliveNeighbours == 3
newGen.cells[i][j] = Cell(newAlive)
}
}
return newGen
}
// good luck in scalar replacement?
private fun wrapOverEdge(orig: Pos) =
if (orig.i in 0 until height && orig.j in 0 until width) orig
else Pos((orig.i + height) % height, (orig.j + width) % width)
override fun equals(other: Any?): Boolean = when {
other is Generation -> {
other.cells.size == cells.size &&
cells.zip(other.cells).all { (a, b) -> a contentEquals b }
}
else -> false
}
companion object {
fun random(width: Int, height: Int): Generation {
val gen = Generation(width, height)
for (i in 0 until height) {
for (j in 0 until width) {
gen.cells[i][j] = Cell(Random.nextInt() % 2 == 0)
}
}
return gen
}
}
}
class Universe(val width: Int, val height: Int) {
var gen = Generation.random(width, height)
fun evolve() {
gen = gen.evolve()
}
}
fun run(space: Int, time: Int) {
val width = space
val height = space
val universe = Universe(width, height)
for (i in 0 until time) {
universe.evolve()
}
Blackhole.consume(universe)
}
open class LifeBenchmark {
val spaceScale = BENCHMARK_SIZE / 40
val timeScale = 5
fun bench() {
run(spaceScale, timeScale)
}
}
class LifeWithMarkHelpersBenchmark : LifeBenchmark() {
val numberOfMarkHelpers = 5;
@Volatile
var done = false
val markHelpers = Array(numberOfMarkHelpers, { _ -> Worker.start() })
val markHelperJobs = markHelpers.map {
it.execute(TransferMode.SAFE, { this }) {
// run some thread-local work in a loop without allocations or external calls
fun fib(n: Int): Int {
if (n == 0) return 0
var prev = 0
var cur = 1
for (i in 2..n) {
val next = cur + prev
prev = cur
cur = next
}
return cur
}
var sum = 0
while (!it.done) {
sum += fib(100)
}
return@execute sum
}
}
fun terminate() {
done = true
markHelperJobs.forEach { it.result }
markHelpers.forEach { it.requestTermination().result }
}
}
@@ -42,6 +42,7 @@
// graph.
import kotlin.native.concurrent.*
import kotlin.concurrent.*
import kotlin.random.Random
// A splay tree is a self-balancing binary search tree with the additional
@@ -319,3 +320,46 @@ class SplayBenchmarkUsingWorkers {
workers.forEach { it.requestTermination().result }
}
}
class SplayBenchmarkWithMarkHelpers {
val numberOfMarkHelpers = 5;
val markHelpers = Array(numberOfMarkHelpers, { _ -> Worker.start() })
@Volatile
var done = false
val markHelperJobs = markHelpers.map {
it.execute(TransferMode.SAFE, { this }) {
// run some thread-local work in a loop without allocations or external calls
fun fib(n: Int): Int {
if (n == 0) return 0
var prev = 0
var cur = 1
for (i in 2..n) {
val next = cur + prev
prev = cur
cur = next
}
return cur
}
var sum = 0
while (!it.done) {
sum += fib(100)
}
return@execute sum
}
}
val splay = SplayBenchmark()
fun runSplayWithMarkHelpers() {
splay.runSplay()
}
fun splayTearDownMarkHelpers() {
done = true
splay.splayTearDown()
markHelperJobs.forEach { it.result }
markHelpers.forEach { it.requestTermination().result }
}
}