Skip to content

Commit

Permalink
Add experiment
Browse files Browse the repository at this point in the history
  • Loading branch information
aartdem committed Mar 31, 2024
1 parent d7c58f6 commit d14215b
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 24 deletions.
2 changes: 1 addition & 1 deletion work1/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ repositories {
}

dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines:0.19.2")
implementation("org.apache.commons:commons-math3:3.6.1")
testImplementation("org.jetbrains.kotlinx:lincheck:2.26")
testImplementation("org.jetbrains.kotlin:kotlin-test")
}
Expand Down
69 changes: 50 additions & 19 deletions work1/src/main/kotlin/stack/Main.kt
Original file line number Diff line number Diff line change
@@ -1,26 +1,57 @@
package stack

import stack.elimination.ConcurrentStackWithElimination
import kotlin.random.Random
import org.apache.commons.math3.geometry.euclidean.oned.Interval
import stack.benchmark.MeasureScenario
import stack.benchmark.Operation
import stack.benchmark.StackBenchmark
import stack.simple.ConcurrentTreiberStack

fun main() {
val st = ConcurrentStackWithElimination<Int>()
val random = Random(System.nanoTime())

val threads = List(2) {
Thread {
repeat(100000000) {
val r = random.nextInt(3)
when (r) {
0 -> st.push(random.nextInt(10))
1 -> st.pop()
2 -> st.top()
}
private val Int.pretty: String
get() {
var zeroCount = 0
var curNum = this
while (curNum % 10 == 0) {
zeroCount++
curNum /= 10
}
if (zeroCount <= 3) {
return this.toString()
} else {
var afterComma = ""
while (curNum > 9) {
afterComma += (curNum % 10)
}
return curNum.toString() + afterComma.reversed() + "0".repeat(zeroCount)
}

}

fun main() {
// prepare cases
val threadNums = listOf(1, 2, 4, 6)
val operations = buildList {
for (i in 1e7.toInt()..1e8.toInt() step 1e7.toInt()) {
add(i)
}
}
// calculating
val stackBenchmark = StackBenchmark<Int>()
val results = mutableListOf(mutableListOf<Interval>())
for (threadNum in threadNums) {
val currentResult = mutableListOf<Interval>()
for (opCount in operations) {
val singleThreadRandomMeasureScenario = MeasureScenario(
threadNum, opCount / threadNum,
ConcurrentTreiberStack(),
Operation.Push(0),
MeasureScenario.randomScenarioIntGenerator
)
// stackBenchmark.loadScenario(singleThreadRandomMeasureScenario)
// val workTime = stackBenchmark.startAndMeasure(20) ?: throw IllegalStateException()
// currentResult.add(workTime)
println("Threads: $threadNum | Operations: $opCount | DONE")
}
results.add(currentResult)
}
val initTime = System.currentTimeMillis()
threads.forEach { it.start() }
threads.forEach { it.join() }
println(System.currentTimeMillis() - initTime)
TODO("print results")
}
30 changes: 30 additions & 0 deletions work1/src/main/kotlin/stack/benchmark/MeasureScenario.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package stack.benchmark

import stack.common.ConcurrentStack
import kotlin.random.Random

class MeasureScenario<T>(
val threadsNum: Int,
val operationsPerThread: Int,
val initialStack: ConcurrentStack<T>,
val firstOperation: Operation<T>,
val operationGenerator: (Operation<T>) -> Operation<T>
) {
companion object {
val randomScenarioIntGenerator: (Operation<Int>) -> Operation<Int> = { _ ->
when (Random.nextInt(3)) {
0 -> Operation.Push(Random.nextInt())
1 -> Operation.Pop()
2 -> Operation.Top()
else -> throw IllegalStateException()
}
}
val pushPopIntGenerator: (Operation<Int>) -> Operation<Int> = { last ->
when (last) {
is Operation.Push -> Operation.Pop()
is Operation.Pop -> Operation.Push(Random.nextInt())
is Operation.Top -> Operation.Push(Random.nextInt())
}
}
}
}
15 changes: 15 additions & 0 deletions work1/src/main/kotlin/stack/benchmark/Operation.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package stack.benchmark

import stack.common.ConcurrentStack

sealed interface Operation<T> {
class Push<T>(val value: T) : Operation<T>
class Pop<T> : Operation<T>
class Top<T> : Operation<T>

fun toStackOperation(): ConcurrentStack<T>.() -> Unit = when (this) {
is Push<T> -> fun ConcurrentStack<T>.() = (::push)(value)
is Pop<T> -> fun ConcurrentStack<T>.() { (::pop)() }
is Top<T> -> fun ConcurrentStack<T>.() { (::top)() }
}
}
72 changes: 72 additions & 0 deletions work1/src/main/kotlin/stack/benchmark/StackBenchmark.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package stack.benchmark

import org.apache.commons.math3.distribution.TDistribution
import org.apache.commons.math3.geometry.euclidean.oned.Interval
import org.apache.commons.math3.stat.descriptive.SummaryStatistics
import kotlin.concurrent.thread
import kotlin.math.sqrt
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.DurationUnit


class StackBenchmark<T> {
private val threads: MutableList<Thread> = mutableListOf()

private fun clearScenario() {
threads.clear()
}

/**
* Load the scenario. If scenario was already loaded, overrides it
*/
fun loadScenario(measureScenario: MeasureScenario<T>) {
clearScenario()

val operations = buildList(measureScenario.operationsPerThread) {
add(measureScenario.firstOperation)
repeat(measureScenario.operationsPerThread - 1) {
add(measureScenario.operationGenerator(this.last()))
}
}.map { it.toStackOperation() }

repeat(measureScenario.threadsNum) {
threads.add(thread(false) {
operations.forEach { it.invoke(measureScenario.initialStack) }
})
}
}

/**
* Execute loaded scenario [n] times and returns work time in milliseconds. Returns null if scenario was not loaded
*/
fun startAndMeasure(n: Int): Interval? {
if (threads.isEmpty()) return null

val results = buildList(n) {
val initialTime = System.currentTimeMillis()
threads.forEach { it.start() }
threads.forEach { it.join() }
val workTimeMills = (System.currentTimeMillis() - initialTime)
add(workTimeMills.milliseconds.toDouble(DurationUnit.SECONDS))
}

return calculateConfidenceInterval(results)
}

/** Implemented using [source](https://gist.github.com/gcardone/5536578) */
private fun calculateConfidenceInterval(results: List<Double>): Interval {
val stats = SummaryStatistics().apply {
results.forEach { this.addValue(it) }
}
val tDist = TDistribution(stats.n.toDouble())
val crVal = tDist.inverseCumulativeProbability(0.975)
val ci = crVal * stats.getStandardDeviation() / sqrt(stats.n.toDouble())
return Interval(stats.mean - ci, stats.mean + ci)
}

fun printLoadedScenario() {
threads.forEach {
println(it.toString())
}
}
}
6 changes: 2 additions & 4 deletions work1/src/test/kotlin/stack/ConcurrentStackTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import org.jetbrains.kotlinx.lincheck.check
import org.jetbrains.kotlinx.lincheck.strategy.managed.modelchecking.ModelCheckingOptions
import org.jetbrains.kotlinx.lincheck.strategy.stress.StressOptions
import stack.common.ConcurrentStack
import stack.elimination.ConcurrentStackWithElimination
import stack.simple.ConcurrentTreiberStack
import kotlin.test.Test

abstract class ConcurrentStackTests(private val stack: ConcurrentStack<Int>) {
Expand Down Expand Up @@ -48,6 +46,6 @@ abstract class ConcurrentStackTests(private val stack: ConcurrentStack<Int>) {
.check(this::class)
}

class ConcurrentStackWithEliminationTests : ConcurrentStackTests(ConcurrentTreiberStack())
//class ConcurrentStackWithEliminationTests : ConcurrentStackTests(ConcurrentTreiberStack())

class ConcurrentTreiberStackTests : ConcurrentStackTests(ConcurrentTreiberStack())
//class ConcurrentTreiberStackTests : ConcurrentStackTests(ConcurrentTreiberStack())

0 comments on commit d14215b

Please sign in to comment.