diff --git a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/BinarySearch.kt b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/BinarySearch.kt index d2298fe6f8ef45700086622257c043fac5050de1..ba7ac45537cd9292a3cf5bab0ddc1fbe3451d11c 100644 --- a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/BinarySearch.kt +++ b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/BinarySearch.kt @@ -13,12 +13,7 @@ private val logger = KotlinLogging.logger {} * @param benchmarkExecutor Benchmark executor which runs the individual benchmarks. */ class BinarySearch(benchmarkExecutor: BenchmarkExecutor) : SearchStrategy(benchmarkExecutor) { - override fun findSuitableResource(load: LoadDimension, resources: List<Resource>, - lastLowestResource: Resource?): Resource? { - - if (lastLowestResource != null) { - logger.info { "Running LinearSearch with a set lastLowestResource value doesn't make sense." } - } + override fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? { val result = binarySearch(load, resources, 0, resources.size - 1) if (result == -1) { diff --git a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/CompositeStrategy.kt b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/CompositeStrategy.kt index 777a87c15d32ae6b10adff5c4e684b1b81f9b4c2..41cc5c325163ade54469398e815fdb8d95c6e6cd 100644 --- a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/CompositeStrategy.kt +++ b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/CompositeStrategy.kt @@ -20,8 +20,7 @@ class CompositeStrategy( val restrictionStrategies: Set<RestrictionStrategy> ) : SearchStrategy(benchmarkExecutor) { - override fun findSuitableResource(load: LoadDimension, resources: List<Resource>, - lastLowestResource: Resource?): Resource? { + override fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? { var restrictedResources = resources.toList() for (strategy in this.restrictionStrategies) { restrictedResources = restrictedResources.intersect(strategy.apply(load, resources)).toList() diff --git a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/FullSearch.kt b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/FullSearch.kt index 3a58bd29a0fed1995393a252f86f62a133ada929..acdf6eab9658458c3c02a2daf87607425dcc9c1a 100644 --- a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/FullSearch.kt +++ b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/FullSearch.kt @@ -17,11 +17,7 @@ private val logger = KotlinLogging.logger {} */ class FullSearch(benchmarkExecutor: BenchmarkExecutor) : SearchStrategy(benchmarkExecutor) { - override fun findSuitableResource(load: LoadDimension, resources: List<Resource>, - lastLowestResource: Resource?): Resource? { - if (lastLowestResource != null) { - logger.info { "Running LinearSearch with a set lastLowestResource value doesn't make sense." } - } + override fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? { var minimalSuitableResources: Resource? = null for (res in resources) { diff --git a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/GuessStrategy.kt b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/GuessStrategy.kt index 786a3baf159e94841c1f76c696f030718e8f768f..e2a961167cc6b0c193a819d1953e303c7adc6afa 100644 --- a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/GuessStrategy.kt +++ b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/GuessStrategy.kt @@ -2,6 +2,7 @@ package theodolite.strategies.searchstrategy import io.quarkus.runtime.annotations.RegisterForReflection import theodolite.util.Resource +import theodolite.util.Results /** * Base class for the implementation of Guess strategies. Guess strategies are strategies to determine the resource diff --git a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/InitialGuessSearchStrategy.kt b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/InitialGuessSearchStrategy.kt index 432f08fdedeaee6f9e32ff840fb34b0245b23593..43ccdf7352d74ca2e9c918bca5a55d8e51b2da6a 100644 --- a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/InitialGuessSearchStrategy.kt +++ b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/InitialGuessSearchStrategy.kt @@ -4,6 +4,7 @@ import mu.KotlinLogging import theodolite.execution.BenchmarkExecutor import theodolite.util.LoadDimension import theodolite.util.Resource +import theodolite.util.Results private val logger = KotlinLogging.logger {} @@ -14,31 +15,51 @@ private val logger = KotlinLogging.logger {} * @param benchmarkExecutor Benchmark executor which runs the individual benchmarks. * @param guessStrategy Strategy that provides us with a guess for the first resource amount. */ -class InitialGuessSearchStrategy(benchmarkExecutor: BenchmarkExecutor, guessStrategy: GuessStrategy) : SearchStrategy(benchmarkExecutor, guessStrategy) { +class InitialGuessSearchStrategy(benchmarkExecutor: BenchmarkExecutor, guessStrategy: GuessStrategy, results: Results) : + SearchStrategy(benchmarkExecutor, guessStrategy, results) { - override fun findSuitableResource(load: LoadDimension, resources: List<Resource>, lastLowestResource: Resource?): Resource? { + 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 } - var lastLowestResourceToUse = this.guessStrategy.firstGuess(resources, lastLowestResource) + 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 (lastLowestResourceToUse != null) { + if (lastLowestResource != null) { val resourcesToCheck: List<Resource> - val startIndex: Int = resources.indexOf(lastLowestResourceToUse) + val startIndex: Int = resources.indexOf(lastLowestResource) - logger.info { "Running experiment with load '${load.get()}' and resources '${lastLowestResourceToUse.get()}'" } + 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, lastLowestResourceToUse)) { + if (this.benchmarkExecutor.runExperiment(load, lastLowestResource)) { resourcesToCheck = resources.subList(0, startIndex).reversed() - if (resourcesToCheck.isEmpty()) return lastLowestResourceToUse + if (resourcesToCheck.isEmpty()) return lastLowestResource - var currentMin: Resource = lastLowestResourceToUse + var currentMin: Resource = lastLowestResource for (res in resourcesToCheck) { logger.info { "Running experiment with load '${load.get()}' and resources '${res.get()}'" } diff --git a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/LinearSearch.kt b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/LinearSearch.kt index 09f431e9f551c56e1af7426bb43f09f0fc654048..f860f2496be05e12cb26dee4abebef7449482d36 100644 --- a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/LinearSearch.kt +++ b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/LinearSearch.kt @@ -14,11 +14,7 @@ private val logger = KotlinLogging.logger {} */ class LinearSearch(benchmarkExecutor: BenchmarkExecutor) : SearchStrategy(benchmarkExecutor) { - override fun findSuitableResource(load: LoadDimension, resources: List<Resource>, - lastLowestResource: Resource?): Resource? { - if (lastLowestResource != null) { - logger.info { "Running LinearSearch with a set lastLowestResource value doesn't make sense." } - } + override fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? { for (res in resources) { diff --git a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/SearchStrategy.kt b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/SearchStrategy.kt index e37cc32b59542b0793f4c9fe6a6cee658a0fa065..357b1d22a03731a68527e926204a49db060dc0b9 100644 --- a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/SearchStrategy.kt +++ b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/SearchStrategy.kt @@ -4,6 +4,7 @@ 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. @@ -12,7 +13,8 @@ import theodolite.util.Resource * @param guessStrategy Guess strategy for the initial resource amount in case the InitialGuessStrategy is selected. */ @RegisterForReflection -abstract class SearchStrategy(val benchmarkExecutor: BenchmarkExecutor, val guessStrategy: GuessStrategy? = null) { +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. * @@ -22,5 +24,5 @@ abstract class SearchStrategy(val benchmarkExecutor: BenchmarkExecutor, val gues * * @return suitable resource for the specified load, or null if no suitable resource exists. */ - abstract fun findSuitableResource(load: LoadDimension, resources: List<Resource>, lastLowestResource: Resource? = null): Resource? + abstract fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? } diff --git a/theodolite/src/main/kotlin/theodolite/util/Results.kt b/theodolite/src/main/kotlin/theodolite/util/Results.kt index 60641ea0248435de53aaaaf362da7be995b391c5..2221c2e64f6dbc1776122f20793aa8d04d621d9d 100644 --- a/theodolite/src/main/kotlin/theodolite/util/Results.kt +++ b/theodolite/src/main/kotlin/theodolite/util/Results.kt @@ -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() + } } diff --git a/theodolite/src/test/kotlin/theodolite/InitialGuessSearchStrategyTest.kt b/theodolite/src/test/kotlin/theodolite/InitialGuessSearchStrategyTest.kt index f7f52a6da1cc82b040d4ad3f4804864feba991ae..1af6f548b219697009c688ace712a9f7f5620bd0 100644 --- a/theodolite/src/test/kotlin/theodolite/InitialGuessSearchStrategyTest.kt +++ b/theodolite/src/test/kotlin/theodolite/InitialGuessSearchStrategyTest.kt @@ -34,24 +34,21 @@ class InitialGuessSearchStrategyTest { 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) + 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) - var currentResource : Resource? = mockResources[0] for (load in mockLoads) { - val returnVal : Resource? = strategy.findSuitableResource(load, mockResources, currentResource) + val returnVal : Resource? = strategy.findSuitableResource(load, mockResources) if(returnVal != null) { logger.info { "returnVal '${returnVal.get()}'" } } else { logger.info { "returnVal is null." } } - actual.add(returnVal) - currentResource = returnVal } assertEquals(actual, expected) @@ -75,24 +72,21 @@ class InitialGuessSearchStrategyTest { 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) + 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) - var currentResource : Resource? = mockResources[0] for (load in mockLoads) { - val returnVal : Resource? = strategy.findSuitableResource(load, mockResources, currentResource) + val returnVal : Resource? = strategy.findSuitableResource(load, mockResources) if(returnVal != null) { logger.info { "returnVal '${returnVal.get()}'" } } else { logger.info { "returnVal is null." } } - actual.add(returnVal) - currentResource = returnVal } assertEquals(actual, expected) @@ -116,25 +110,22 @@ class InitialGuessSearchStrategyTest { 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) + 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) - var currentResource : Resource? = mockResources[0] for (load in mockLoads) { - val returnVal : Resource? = strategy.findSuitableResource(load, mockResources, currentResource) + val returnVal : Resource? = strategy.findSuitableResource(load, mockResources) if(returnVal != null) { logger.info { "returnVal '${returnVal.get()}'" } } else { logger.info { "returnVal is null." } } - actual.add(returnVal) - currentResource = returnVal } assertEquals(actual, expected)