diff --git a/work1/README.md b/work1/README.md new file mode 100644 index 0000000..e69de29 diff --git a/work1/build.gradle.kts b/work1/build.gradle.kts index 3e1041c..ca0b0d0 100644 --- a/work1/build.gradle.kts +++ b/work1/build.gradle.kts @@ -1,5 +1,6 @@ plugins { kotlin("jvm") version "1.9.22" + application } group = "stack" @@ -23,4 +24,8 @@ tasks.test { kotlin { jvmToolchain(17) +} + +application { + mainClass = "stack.MainKt" } \ No newline at end of file diff --git a/work1/src/main/kotlin/stack/Main.kt b/work1/src/main/kotlin/stack/Main.kt new file mode 100644 index 0000000..68f4239 --- /dev/null +++ b/work1/src/main/kotlin/stack/Main.kt @@ -0,0 +1,50 @@ +package stack + +import de.m3y.kformat.Table +import de.m3y.kformat.table +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() { + // prepare cases +// val threadNums = listOf(1, 2, 4, 6) +// val operations = buildList { +// for (i in 1e7.toInt()..1e8.toInt() step 1e7.toInt()) { +// add(i) +// } +// } + val threadNums = listOf(6) + val operations = listOf(100) + // 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) + } + table { + header(listOf("") + operations.map { it.toString() }) + + // TODO("add my rows") + row(10, "b...1", 2.1f, "foo") + + hints { + borderStyle = Table.BorderStyle.SINGLE_LINE // or NONE + } + }.print() +} 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..da7f980 --- /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(start = 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.standardDeviation / 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