From 5470d9d0ddc0f4473409fc471f3fae98c817cffb Mon Sep 17 00:00:00 2001 From: Marcel Becker <stu117960@mail.uni-kiel.de> Date: Wed, 15 Dec 2021 16:34:53 +0100 Subject: [PATCH] Using result object for InitialGuessSearchStrategy implementation --- .../strategies/searchstrategy/BinarySearch.kt | 7 +--- .../searchstrategy/CompositeStrategy.kt | 3 +- .../strategies/searchstrategy/FullSearch.kt | 6 +-- .../searchstrategy/GuessStrategy.kt | 1 + .../InitialGuessSearchStrategy.kt | 39 ++++++++++++++----- .../strategies/searchstrategy/LinearSearch.kt | 6 +-- .../searchstrategy/SearchStrategy.kt | 6 ++- .../main/kotlin/theodolite/util/Results.kt | 17 ++++++-- .../InitialGuessSearchStrategyTest.kt | 21 +++------- 9 files changed, 58 insertions(+), 48 deletions(-) diff --git a/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/BinarySearch.kt b/theodolite/src/main/kotlin/theodolite/strategies/searchstrategy/BinarySearch.kt index d2298fe6f..ba7ac4553 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 777a87c15..41cc5c325 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 3a58bd29a..acdf6eab9 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 786a3baf1..e2a961167 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 432f08fde..43ccdf735 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 09f431e9f..f860f2496 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 e37cc32b5..357b1d22a 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 60641ea02..2221c2e64 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 f7f52a6da..1af6f548b 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) -- GitLab