Skip to content
Snippets Groups Projects
Commit 93ec2862 authored by Sören Henning's avatar Sören Henning
Browse files

Merge branch 'master' into feature/147-add-beam-implementations

parents eee35715 6654f2ce
No related branches found
No related tags found
1 merge request!187Migrate Beam benchmark implementation
Pipeline #5950 passed
......@@ -22,7 +22,7 @@ public final class LoadGenerator {
private static final int THREADS_DEFAULT = 4;
private static final String SCHEMA_REGISTRY_URL_DEFAULT = "http://localhost:8081";
private static final String KAFKA_TOPIC_DEFAULT = "input";
private static final String KAFKA_BOOTSTRAP_SERVERS_DEFAULT = "localhost:19092"; // NOPMD
private static final String KAFKA_BOOTSTRAP_SERVERS_DEFAULT = "localhost:9092"; // NOPMD
private ClusterConfig clusterConfig;
private WorkloadDefinition loadDefinition;
......
package theodolite.strategies.searchstrategy
import io.quarkus.runtime.annotations.RegisterForReflection
import theodolite.util.Resource
/**
* Base class for the implementation of Guess strategies. Guess strategies are strategies to determine the resource
* demand we start with in our initial guess search strategy.
*/
@RegisterForReflection
abstract class GuessStrategy {
/**
* Computing the resource demand for the initial guess search strategy to start with.
*
* @param resources List of all possible [Resource]s.
* @param lastLowestResource Previous resource demand needed for the given load.
*
* @return Returns the resource demand to start the initial guess search strategy with or null
*/
abstract fun firstGuess(resources: List<Resource>, lastLowestResource: Resource?): Resource?
}
\ No newline at end of file
package theodolite.strategies.searchstrategy
import mu.KotlinLogging
import theodolite.execution.BenchmarkExecutor
import theodolite.util.LoadDimension
import theodolite.util.Resource
import theodolite.util.Results
private val logger = KotlinLogging.logger {}
/**
* Search strategy implementation for determining the smallest suitable resource demand.
* Starting with a resource amount provided by a guess strategy.
*
* @param benchmarkExecutor Benchmark executor which runs the individual benchmarks.
* @param guessStrategy Strategy that provides us with a guess for the first resource amount.
* @param results current results of all previously performed benchmarks.
*/
class InitialGuessSearchStrategy(benchmarkExecutor: BenchmarkExecutor, guessStrategy: GuessStrategy, results: Results) :
SearchStrategy(benchmarkExecutor, guessStrategy, results) {
override fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? {
if(resources.isEmpty()) {
logger.info { "You need to specify resources to be checked for the InitialGuessSearchStrategy to work." }
return null
}
if(guessStrategy == null){
logger.info { "Your InitialGuessSearchStrategy doesn't have a GuessStrategy. This is not supported." }
return null
}
if(results == null){
logger.info { "The results need to be initialized." }
return null
}
var lastLowestResource : Resource? = null
// Getting the lastLowestResource from results and calling firstGuess() with it
if (!results.isEmpty()) {
val maxLoad: LoadDimension? = this.results.getMaxBenchmarkedLoad(load)
lastLowestResource = this.results.getMinRequiredInstances(maxLoad)
if (lastLowestResource.get() == Int.MAX_VALUE) lastLowestResource = null
}
lastLowestResource = this.guessStrategy.firstGuess(resources, lastLowestResource)
if (lastLowestResource != null) {
val resourcesToCheck: List<Resource>
val startIndex: Int = resources.indexOf(lastLowestResource)
logger.info { "Running experiment with load '${load.get()}' and resources '${lastLowestResource.get()}'" }
// If the first experiment passes, starting downward linear search
// otherwise starting upward linear search
if (this.benchmarkExecutor.runExperiment(load, lastLowestResource)) {
resourcesToCheck = resources.subList(0, startIndex).reversed()
if (resourcesToCheck.isEmpty()) return lastLowestResource
var currentMin: Resource = lastLowestResource
for (res in resourcesToCheck) {
logger.info { "Running experiment with load '${load.get()}' and resources '${res.get()}'" }
if (this.benchmarkExecutor.runExperiment(load, res)) {
currentMin = res
}
}
return currentMin
}
else {
if (resources.size <= startIndex + 1) {
logger.info{ "No more resources left to check." }
return null
}
resourcesToCheck = resources.subList(startIndex + 1, resources.size)
for (res in resourcesToCheck) {
logger.info { "Running experiment with load '${load.get()}' and resources '${res.get()}'" }
if (this.benchmarkExecutor.runExperiment(load, res)) return res
}
}
}
else {
logger.info { "InitialGuessSearchStrategy called without lastLowestResource value, which is needed as a " +
"starting point!" }
}
return null
}
}
\ No newline at end of file
package theodolite.strategies.searchstrategy
import theodolite.util.Resource
/**
* This Guess strategy takes the minimal resource demand of the previous load, which is given as an argument for the
* firstGuess function.
*/
class PrevResourceMinGuess() : GuessStrategy(){
/**
* @param resources List of all possible [Resource]s.
* @param lastLowestResource Previous resource demand needed for the given load.
*
* @return the value of lastLowestResource if given otherwise the first element of the resource list or null
*/
override fun firstGuess(resources: List<Resource>, lastLowestResource: Resource?): Resource? {
if (lastLowestResource != null) return lastLowestResource
else if(resources.isNotEmpty()) return resources[0]
else return null
}
}
\ No newline at end of file
......@@ -4,14 +4,18 @@ import io.quarkus.runtime.annotations.RegisterForReflection
import theodolite.execution.BenchmarkExecutor
import theodolite.util.LoadDimension
import theodolite.util.Resource
import theodolite.util.Results
/**
* Base class for the implementation for SearchStrategies. SearchStrategies determine the smallest suitable number of instances.
*
* @param benchmarkExecutor Benchmark executor which runs the individual benchmarks.
* @param guessStrategy Guess strategy for the initial resource amount in case the InitialGuessStrategy is selected.
* @param results the [Results] object.
*/
@RegisterForReflection
abstract class SearchStrategy(val benchmarkExecutor: BenchmarkExecutor) {
abstract class SearchStrategy(val benchmarkExecutor: BenchmarkExecutor, val guessStrategy: GuessStrategy? = null,
val results: Results? = null) {
/**
* Find smallest suitable resource from the specified resource list for the given load.
*
......
......@@ -3,7 +3,7 @@ package theodolite.util
import io.quarkus.runtime.annotations.RegisterForReflection
/**
* Central class that saves the state of a execution of Theodolite. For an execution, it is used to save the result of
* Central class that saves the state of an execution of Theodolite. For an execution, it is used to save the result of
* individual experiments. Further, it is used by the RestrictionStrategy to
* perform the [theodolite.strategies.restriction.RestrictionStrategy].
*/
......@@ -44,16 +44,16 @@ class Results {
* If no experiments have been marked as either successful or unsuccessful
* yet, a Resource with the constant value Int.MIN_VALUE is returned.
*/
fun getMinRequiredInstances(load: LoadDimension?): Resource? {
fun getMinRequiredInstances(load: LoadDimension?): Resource {
if (this.results.isEmpty()) {
return Resource(Int.MIN_VALUE, emptyList())
}
var minRequiredInstances: Resource? = Resource(Int.MAX_VALUE, emptyList())
var minRequiredInstances = Resource(Int.MAX_VALUE, emptyList())
for (experiment in results) {
// Get all successful experiments for requested load
if (experiment.key.first == load && experiment.value) {
if (minRequiredInstances == null || experiment.key.second.get() < minRequiredInstances.get()) {
if (experiment.key.second.get() < minRequiredInstances.get()) {
// Found new smallest resources
minRequiredInstances = experiment.key.second
}
......@@ -83,4 +83,13 @@ class Results {
}
return maxBenchmarkedLoad
}
/**
* Checks whether the results are empty.
*
* @return true if [results] is empty.
*/
fun isEmpty(): Boolean{
return results.isEmpty()
}
}
package theodolite
import io.quarkus.test.junit.QuarkusTest
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import theodolite.benchmark.BenchmarkExecution
import theodolite.strategies.searchstrategy.InitialGuessSearchStrategy
import theodolite.util.LoadDimension
import theodolite.util.Resource
import theodolite.util.Results
import mu.KotlinLogging
import theodolite.strategies.searchstrategy.PrevResourceMinGuess
private val logger = KotlinLogging.logger {}
@QuarkusTest
class InitialGuessSearchStrategyTest {
@Test
fun testInitialGuessSearch() {
val mockResults = arrayOf(
arrayOf(true, true, true, true, true, true, true),
arrayOf(false, false, true, true, true, true, true),
arrayOf(false, false, true, true, true, true, true),
arrayOf(false, false, false, true, true, true, true),
arrayOf(false, false, false, false, true, true, true),
arrayOf(false, false, false, false, false, false, true),
arrayOf(false, false, false, false, false, false, false)
)
val mockLoads: List<LoadDimension> = (0..6).map { number -> LoadDimension(number, emptyList()) }
val mockResources: List<Resource> = (0..6).map { number -> Resource(number, emptyList()) }
val results = Results()
val benchmark = TestBenchmark()
val guessStrategy = PrevResourceMinGuess()
val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo()
val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, listOf(sloChecker), 0, 0, 5)
val strategy = InitialGuessSearchStrategy(benchmarkExecutor,guessStrategy, results)
val actual: ArrayList<Resource?> = ArrayList()
val expected: ArrayList<Resource?> = ArrayList(listOf(0, 2, 2, 3, 4, 6).map { x -> Resource(x, emptyList()) })
expected.add(null)
for (load in mockLoads) {
val returnVal : Resource? = strategy.findSuitableResource(load, mockResources)
if(returnVal != null) {
logger.info { "returnVal '${returnVal.get()}'" }
}
else {
logger.info { "returnVal is null." }
}
actual.add(returnVal)
}
assertEquals(actual, expected)
}
@Test
fun testInitialGuessSearchLowerResourceDemandHigherLoad() {
val mockResults = arrayOf(
arrayOf(true, true, true, true, true, true, true),
arrayOf(false, false, true, true, true, true, true),
arrayOf(false, false, true, true, true, true, true),
arrayOf(false, true, true, true, true, true, true),
arrayOf(false, false, false, false, true, true, true),
arrayOf(false, false, false, false, false, false, true),
arrayOf(false, false, false, false, false, false, false)
)
val mockLoads: List<LoadDimension> = (0..6).map { number -> LoadDimension(number, emptyList()) }
val mockResources: List<Resource> = (0..6).map { number -> Resource(number, emptyList()) }
val results = Results()
val benchmark = TestBenchmark()
val guessStrategy = PrevResourceMinGuess()
val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo()
val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, listOf(sloChecker), 0, 0, 5)
val strategy = InitialGuessSearchStrategy(benchmarkExecutor,guessStrategy, results)
val actual: ArrayList<Resource?> = ArrayList()
val expected: ArrayList<Resource?> = ArrayList(listOf(0, 2, 2, 1, 4, 6).map { x -> Resource(x, emptyList()) })
expected.add(null)
for (load in mockLoads) {
val returnVal : Resource? = strategy.findSuitableResource(load, mockResources)
if(returnVal != null) {
logger.info { "returnVal '${returnVal.get()}'" }
}
else {
logger.info { "returnVal is null." }
}
actual.add(returnVal)
}
assertEquals(actual, expected)
}
@Test
fun testInitialGuessSearchFirstNotDoable() {
val mockResults = arrayOf(
arrayOf(false, false, false, false, false, false, false),
arrayOf(false, false, true, true, true, true, true),
arrayOf(false, false, false, true, true, true, true),
arrayOf(true, true, true, true, true, true, true),
arrayOf(false, false, false, false, true, true, true),
arrayOf(false, false, false, false, false, false, true),
arrayOf(false, false, false, false, false, false, false)
)
val mockLoads: List<LoadDimension> = (0..6).map { number -> LoadDimension(number, emptyList()) }
val mockResources: List<Resource> = (0..6).map { number -> Resource(number, emptyList()) }
val results = Results()
val benchmark = TestBenchmark()
val guessStrategy = PrevResourceMinGuess()
val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo()
val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, listOf(sloChecker), 0, 0, 5)
val strategy = InitialGuessSearchStrategy(benchmarkExecutor, guessStrategy, results)
val actual: ArrayList<Resource?> = ArrayList()
var expected: ArrayList<Resource?> = ArrayList(listOf(2, 3, 0, 4, 6).map { x -> Resource(x, emptyList()) })
expected.add(null)
expected = ArrayList(listOf(null) + expected)
for (load in mockLoads) {
val returnVal : Resource? = strategy.findSuitableResource(load, mockResources)
if(returnVal != null) {
logger.info { "returnVal '${returnVal.get()}'" }
}
else {
logger.info { "returnVal is null." }
}
actual.add(returnVal)
}
assertEquals(actual, expected)
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment