-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
172 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<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) | ||
} | ||
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() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)() } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(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<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.standardDeviation / sqrt(stats.n.toDouble()) | ||
return Interval(stats.mean - ci, stats.mean + ci) | ||
} | ||
|
||
fun printLoadedScenario() { | ||
threads.forEach { | ||
println(it.toString()) | ||
} | ||
} | ||
} |