From d14215b2d9e565efd7449ab547a82e764897de8b Mon Sep 17 00:00:00 2001 From: Artem Demchenko Date: Sun, 31 Mar 2024 19:48:30 +0300 Subject: [PATCH] Add experiment --- work1/build.gradle.kts | 2 +- work1/src/main/kotlin/stack/Main.kt | 69 +++++++++++++----- .../kotlin/stack/benchmark/MeasureScenario.kt | 30 ++++++++ .../main/kotlin/stack/benchmark/Operation.kt | 15 ++++ .../kotlin/stack/benchmark/StackBenchmark.kt | 72 +++++++++++++++++++ .../test/kotlin/stack/ConcurrentStackTests.kt | 6 +- 6 files changed, 170 insertions(+), 24 deletions(-) create mode 100644 work1/src/main/kotlin/stack/benchmark/MeasureScenario.kt create mode 100644 work1/src/main/kotlin/stack/benchmark/Operation.kt create mode 100644 work1/src/main/kotlin/stack/benchmark/StackBenchmark.kt diff --git a/work1/build.gradle.kts b/work1/build.gradle.kts index d4a2cb3..b884252 100644 --- a/work1/build.gradle.kts +++ b/work1/build.gradle.kts @@ -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") } diff --git a/work1/src/main/kotlin/stack/Main.kt b/work1/src/main/kotlin/stack/Main.kt index 5f651fc..d2e0ec1 100644 --- a/work1/src/main/kotlin/stack/Main.kt +++ b/work1/src/main/kotlin/stack/Main.kt @@ -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() - 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() + val results = mutableListOf(mutableListOf()) + for (threadNum in threadNums) { + val currentResult = mutableListOf() + 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") } diff --git a/work1/src/main/kotlin/stack/benchmark/MeasureScenario.kt b/work1/src/main/kotlin/stack/benchmark/MeasureScenario.kt new file mode 100644 index 0000000..cae4a26 --- /dev/null +++ b/work1/src/main/kotlin/stack/benchmark/MeasureScenario.kt @@ -0,0 +1,30 @@ +package stack.benchmark + +import stack.common.ConcurrentStack +import kotlin.random.Random + +class MeasureScenario( + val threadsNum: Int, + val operationsPerThread: Int, + val initialStack: ConcurrentStack, + val firstOperation: Operation, + val operationGenerator: (Operation) -> Operation +) { + companion object { + val randomScenarioIntGenerator: (Operation) -> Operation = { _ -> + when (Random.nextInt(3)) { + 0 -> Operation.Push(Random.nextInt()) + 1 -> Operation.Pop() + 2 -> Operation.Top() + else -> throw IllegalStateException() + } + } + val pushPopIntGenerator: (Operation) -> Operation = { last -> + when (last) { + is Operation.Push -> Operation.Pop() + is Operation.Pop -> Operation.Push(Random.nextInt()) + is Operation.Top -> Operation.Push(Random.nextInt()) + } + } + } +} \ No newline at end of file diff --git a/work1/src/main/kotlin/stack/benchmark/Operation.kt b/work1/src/main/kotlin/stack/benchmark/Operation.kt new file mode 100644 index 0000000..2e2db54 --- /dev/null +++ b/work1/src/main/kotlin/stack/benchmark/Operation.kt @@ -0,0 +1,15 @@ +package stack.benchmark + +import stack.common.ConcurrentStack + +sealed interface Operation { + class Push(val value: T) : Operation + class Pop : Operation + class Top : Operation + + fun toStackOperation(): ConcurrentStack.() -> Unit = when (this) { + is Push -> fun ConcurrentStack.() = (::push)(value) + is Pop -> fun ConcurrentStack.() { (::pop)() } + is Top -> fun ConcurrentStack.() { (::top)() } + } +} \ No newline at end of file diff --git a/work1/src/main/kotlin/stack/benchmark/StackBenchmark.kt b/work1/src/main/kotlin/stack/benchmark/StackBenchmark.kt new file mode 100644 index 0000000..151b21d --- /dev/null +++ b/work1/src/main/kotlin/stack/benchmark/StackBenchmark.kt @@ -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 { + private val threads: MutableList = mutableListOf() + + private fun clearScenario() { + threads.clear() + } + + /** + * Load the scenario. If scenario was already loaded, overrides it + */ + fun loadScenario(measureScenario: MeasureScenario) { + 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): 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()) + } + } +} \ No newline at end of file diff --git a/work1/src/test/kotlin/stack/ConcurrentStackTests.kt b/work1/src/test/kotlin/stack/ConcurrentStackTests.kt index ccfe272..cdcfe3d 100644 --- a/work1/src/test/kotlin/stack/ConcurrentStackTests.kt +++ b/work1/src/test/kotlin/stack/ConcurrentStackTests.kt @@ -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) { @@ -48,6 +46,6 @@ abstract class ConcurrentStackTests(private val stack: ConcurrentStack) { .check(this::class) } -class ConcurrentStackWithEliminationTests : ConcurrentStackTests(ConcurrentTreiberStack()) +//class ConcurrentStackWithEliminationTests : ConcurrentStackTests(ConcurrentTreiberStack()) -class ConcurrentTreiberStackTests : ConcurrentStackTests(ConcurrentTreiberStack()) \ No newline at end of file +//class ConcurrentTreiberStackTests : ConcurrentStackTests(ConcurrentTreiberStack()) \ No newline at end of file