diff options
| author | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2022-05-06 22:36:25 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-05-06 22:36:25 +0200 |
| commit | 09c6168f022245380d910cd35495b657ab34fbd1 (patch) | |
| tree | f40c1d040c84b322da42ce2f4285a214e0f27e6e /opendc-experiments/opendc-experiments-capelin | |
| parent | c3d8d967f82f39f1ef461d5687eb68fb867336c5 (diff) | |
| parent | 0cb3ff64074ba2bfd671c8ca945f54708ea66fe9 (diff) | |
merge: Restructure experiments and remove legacy harness (#82)
This pull request restructures the experiments present in the `opendc-experiments` directory
and removes the legacy OpenDC Harness. Previously, the experiments were written against
the OpenDC Harness, which facilitates generation and execution of scenarios.
However, the OpenDC Harness does not integrate well into the web-based workflow of OpenDC,
where users should be able to submit scenarios in the web interface and automatically simulate
it in the cloud, since the harness relied on a special Kotlin DSL to specify experiments.
In future pull request, we'll attempt to introduce a similar approach for specifying and
running experiments as we have done for the Radice experiments, where the entire
experiment is described in a serializable (JSON/YAML) format.
## Implementation Notes :hammer_and_pick:
* Add helper tools for FaaS simulations
* Fix infinite loop due to invalid rounding
* Convert experiment into integration test
* Add independent Capelin distribution
* Remove OpenDC Harness modules
* Remove unnecessary dependencies
## Breaking API Changes :warning:
* Removal of the OpenDC Harness modules. Instead, we now package each experiment individually.
We'll focus in the future on extracting common code from the Capelin and Radice experiments
so they can be re-used by other experiments as well.
Diffstat (limited to 'opendc-experiments/opendc-experiments-capelin')
16 files changed, 490 insertions, 305 deletions
diff --git a/opendc-experiments/opendc-experiments-capelin/LICENSE.txt b/opendc-experiments/opendc-experiments-capelin/LICENSE.txt new file mode 100644 index 00000000..3931600a --- /dev/null +++ b/opendc-experiments/opendc-experiments-capelin/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Georgios Andreadis, Fabian Mastenbroek + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/opendc-experiments/opendc-experiments-capelin/README.md b/opendc-experiments/opendc-experiments-capelin/README.md new file mode 100644 index 00000000..d86fe81d --- /dev/null +++ b/opendc-experiments/opendc-experiments-capelin/README.md @@ -0,0 +1,9 @@ +# Capelin +Data-Driven Compute Capacity Procurement for Cloud Datacenters Using Portfolios of Scenarios. + +See the [publication](https://arxiv.org/abs/2103.02060) and +[thesis](https://repository.tudelft.nl/islandora/object/uuid:d6d50861-86a3-4dd3-a13f-42d84db7af66?collection=education) for information. + +## License + +OpenDC and Capelin are distributed under the MIT license. See [LICENSE-OpenDC.txt](/LICENSE.txt) and [LICENSE.txt](LICENSE.txt). diff --git a/opendc-experiments/opendc-experiments-capelin/build.gradle.kts b/opendc-experiments/opendc-experiments-capelin/build.gradle.kts index 39cf101d..8320179a 100644 --- a/opendc-experiments/opendc-experiments-capelin/build.gradle.kts +++ b/opendc-experiments/opendc-experiments-capelin/build.gradle.kts @@ -24,28 +24,62 @@ description = "Experiments for the Capelin work" /* Build configuration */ plugins { - `experiment-conventions` + `kotlin-conventions` `testing-conventions` `jacoco-conventions` `benchmark-conventions` + distribution } dependencies { - api(projects.opendcHarness.opendcHarnessApi) api(projects.opendcCompute.opendcComputeWorkload) implementation(projects.opendcSimulator.opendcSimulatorCore) implementation(projects.opendcSimulator.opendcSimulatorCompute) implementation(projects.opendcCompute.opendcComputeSimulator) - implementation(libs.config) + implementation(libs.clikt) + implementation(libs.progressbar) implementation(libs.kotlin.logging) - implementation(libs.jackson.databind) - implementation(libs.jackson.module.kotlin) implementation(libs.jackson.dataformat.csv) - implementation(kotlin("reflect")) runtimeOnly(projects.opendcTrace.opendcTraceOpendc) + runtimeOnly(libs.log4j.slf4j) +} + +val createCapelinApp by tasks.creating(CreateStartScripts::class) { + dependsOn(tasks.jar) + + applicationName = "capelin" + mainClass.set("org.opendc.experiments.capelin.CapelinCli") + classpath = tasks.jar.get().outputs.files + configurations["runtimeClasspath"] + outputDir = project.buildDir.resolve("scripts") +} + +/* Create custom Capelin distribution */ +distributions { + main { + distributionBaseName.set("capelin") + + contents { + from("README.md") + from("LICENSE.txt") + from("../../LICENSE.txt") { + rename { "LICENSE-OpenDC.txt" } + } + + into("bin") { + from(createCapelinApp) + } + + into("lib") { + from(tasks.jar) + from(configurations["runtimeClasspath"]) + } - testImplementation(libs.log4j.slf4j) + into("input") { + from("input") + } + } + } } diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinCli.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinCli.kt new file mode 100644 index 00000000..875ae0dc --- /dev/null +++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinCli.kt @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +@file:JvmName("CapelinCli") +package org.opendc.experiments.capelin + +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.parameters.arguments.argument +import com.github.ajalt.clikt.parameters.options.* +import com.github.ajalt.clikt.parameters.types.choice +import com.github.ajalt.clikt.parameters.types.file +import com.github.ajalt.clikt.parameters.types.int +import com.github.ajalt.clikt.parameters.types.long +import me.tongfei.progressbar.ProgressBarBuilder +import me.tongfei.progressbar.ProgressBarStyle +import org.opendc.experiments.capelin.model.Scenario +import org.opendc.experiments.capelin.portfolio.* +import java.io.File +import java.util.concurrent.ForkJoinPool +import java.util.stream.LongStream + +/** + * Main entrypoint of the application. + */ +fun main(args: Array<String>): Unit = CapelinCommand().main(args) + +/** + * Represents the command for the Capelin experiments. + */ +internal class CapelinCommand : CliktCommand(name = "capelin") { + /** + * The path to the environment directory. + */ + private val envPath by option("--env-path", help = "path to environment directory") + .file(canBeDir = true, canBeFile = false) + .defaultLazy { File("input/environments") } + + /** + * The path to the trace directory. + */ + private val tracePath by option("--trace-path", help = "path to trace directory") + .file(canBeDir = true, canBeFile = false) + .defaultLazy { File("input/traces") } + + /** + * The path to the experiment output. + */ + private val outputPath by option("-O", "--output", help = "path to experiment output") + .file(canBeDir = true, canBeFile = false) + .defaultLazy { File("output") } + + /** + * Disable writing output. + */ + private val disableOutput by option("--disable-output", help = "disable output").flag() + + /** + * The number of threads to use for parallelism. + */ + private val parallelism by option("-p", "--parallelism", help = "number of worker threads") + .int() + .default(Runtime.getRuntime().availableProcessors() - 1) + + /** + * The number of repeats. + */ + private val repeats by option("-r", "--repeats", help = "number of repeats") + .int() + .default(128) + + /** + * The seed for seeding the random instances. + */ + private val seed by option("-s", "--seed", help = "initial seed for randomness") + .long() + .default(0) + + /** + * The portfolio to replay. + */ + private val portfolio by argument(help = "portfolio to replay") + .choice( + "test" to { TestPortfolio() }, + "composite-workload" to { CompositeWorkloadPortfolio() }, + "hor-ver" to { HorVerPortfolio() }, + "more-hpc" to { MoreHpcPortfolio() }, + "more-velocity" to { MoreVelocityPortfolio() }, + "op-phen" to { OperationalPhenomenaPortfolio() }, + ) + + /** + * The base partitions to use for the invocation + */ + private val basePartitions: Map<String, String> by option("-P", "--base-partitions").associate() + + override fun run() { + val runner = CapelinRunner(envPath, tracePath, outputPath.takeUnless { disableOutput }) + val scenarios = portfolio().scenarios.toList() + + val pool = ForkJoinPool(parallelism) + + echo("Detected ${scenarios.size} scenarios [$repeats replications]") + + for (scenario in scenarios) { + runScenario(runner, pool, scenario) + } + + pool.shutdown() + } + + /** + * Run a single scenario. + */ + private fun runScenario(runner: CapelinRunner, pool: ForkJoinPool, scenario: Scenario) { + val pb = ProgressBarBuilder() + .setInitialMax(repeats.toLong()) + .setStyle(ProgressBarStyle.ASCII) + .setTaskName("Simulating...") + .build() + + pool.submit { + LongStream.range(0, repeats.toLong()) + .parallel() + .forEach { repeat -> + val augmentedScenario = scenario.copy(partitions = basePartitions + scenario.partitions) + runner.runScenario(augmentedScenario, seed + repeat) + pb.step() + } + + pb.close() + }.join() + } +} diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinRunner.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinRunner.kt new file mode 100644 index 00000000..cca5b6cf --- /dev/null +++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinRunner.kt @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.experiments.capelin + +import org.opendc.compute.api.Server +import org.opendc.compute.workload.ComputeServiceHelper +import org.opendc.compute.workload.ComputeWorkloadLoader +import org.opendc.compute.workload.createComputeScheduler +import org.opendc.compute.workload.export.parquet.ParquetComputeMonitor +import org.opendc.compute.workload.grid5000 +import org.opendc.compute.workload.telemetry.ComputeMetricReader +import org.opendc.compute.workload.topology.apply +import org.opendc.experiments.capelin.model.Scenario +import org.opendc.experiments.capelin.topology.clusterTopology +import org.opendc.simulator.core.runBlockingSimulation +import java.io.File +import java.time.Duration +import java.util.* +import kotlin.math.roundToLong + +/** + * Helper class for running the Capelin experiments. + * + * @param envPath The path to the directory containing the environments. + * @param tracePath The path to the directory containing the traces. + * @param outputPath The path to the directory where the output should be written (or `null` if no output should be generated). + */ +public class CapelinRunner( + private val envPath: File, + tracePath: File, + private val outputPath: File? +) { + /** + * The [ComputeWorkloadLoader] to use for loading the traces. + */ + private val workloadLoader = ComputeWorkloadLoader(tracePath) + + /** + * Run a single [scenario] with the specified seed. + */ + fun runScenario(scenario: Scenario, seed: Long) = runBlockingSimulation { + val seeder = Random(seed) + + val operationalPhenomena = scenario.operationalPhenomena + val computeScheduler = createComputeScheduler(scenario.allocationPolicy, seeder) + val failureModel = + if (operationalPhenomena.failureFrequency > 0) + grid5000(Duration.ofSeconds((operationalPhenomena.failureFrequency * 60).roundToLong())) + else + null + val (vms, interferenceModel) = scenario.workload.source.resolve(workloadLoader, seeder) + val runner = ComputeServiceHelper( + coroutineContext, + clock, + computeScheduler, + failureModel, + interferenceModel?.withSeed(seed) + ) + + val topology = clusterTopology(File(envPath, "${scenario.topology.name}.txt")) + + val servers = mutableListOf<Server>() + val partitions = scenario.partitions + ("seed" to seed.toString()) + val partition = partitions.map { (k, v) -> "$k=$v" }.joinToString("/") + val exporter = if (outputPath != null) { + ComputeMetricReader( + this, + clock, + runner.service, + servers, + ParquetComputeMonitor( + outputPath, + partition, + bufferSize = 4096 + ), + exportInterval = Duration.ofMinutes(5) + ) + } else { + null + } + + try { + // Instantiate the desired topology + runner.apply(topology, optimize = true) + + // Run the workload trace + runner.run(vms, seeder.nextLong(), servers) + + // Stop the metric collection + exporter?.close() + } finally { + runner.close() + } + } +} diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/Portfolio.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/Portfolio.kt deleted file mode 100644 index 0de8aa7b..00000000 --- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/Portfolio.kt +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.experiments.capelin - -import com.typesafe.config.ConfigFactory -import kotlinx.coroutines.* -import org.opendc.compute.api.Server -import org.opendc.compute.workload.ComputeServiceHelper -import org.opendc.compute.workload.ComputeWorkloadLoader -import org.opendc.compute.workload.createComputeScheduler -import org.opendc.compute.workload.export.parquet.ParquetComputeMonitor -import org.opendc.compute.workload.grid5000 -import org.opendc.compute.workload.telemetry.ComputeMetricReader -import org.opendc.compute.workload.topology.apply -import org.opendc.experiments.capelin.model.OperationalPhenomena -import org.opendc.experiments.capelin.model.Topology -import org.opendc.experiments.capelin.model.Workload -import org.opendc.experiments.capelin.topology.clusterTopology -import org.opendc.harness.dsl.Experiment -import org.opendc.harness.dsl.anyOf -import org.opendc.simulator.core.runBlockingSimulation -import java.io.File -import java.time.Duration -import java.util.* -import kotlin.math.roundToLong - -/** - * A portfolio represents a collection of scenarios are tested for the work. - * - * @param name The name of the portfolio. - */ -abstract class Portfolio(name: String) : Experiment(name) { - /** - * The configuration to use. - */ - private val config = ConfigFactory.load().getConfig("opendc.experiments.capelin") - - /** - * The path to the original VM placements file. - */ - private val vmPlacements by anyOf(emptyMap<String, String>()) - - /** - * The topology to test. - */ - abstract val topology: Topology - - /** - * The workload to test. - */ - abstract val workload: Workload - - /** - * The operational phenomenas to consider. - */ - abstract val operationalPhenomena: OperationalPhenomena - - /** - * The allocation policies to consider. - */ - abstract val allocationPolicy: String - - /** - * A helper class to load workload traces. - */ - private val workloadLoader = ComputeWorkloadLoader(File(config.getString("trace-path"))) - - /** - * Perform a single trial for this portfolio. - */ - override fun doRun(repeat: Int): Unit = runBlockingSimulation { - val seeder = Random(repeat.toLong()) - - val computeScheduler = createComputeScheduler(allocationPolicy, seeder, vmPlacements) - val failureModel = - if (operationalPhenomena.failureFrequency > 0) - grid5000(Duration.ofSeconds((operationalPhenomena.failureFrequency * 60).roundToLong())) - else - null - val (vms, interferenceModel) = workload.source.resolve(workloadLoader, seeder) - val runner = ComputeServiceHelper( - coroutineContext, - clock, - computeScheduler, - failureModel, - interferenceModel?.withSeed(repeat.toLong()) - ) - - val topology = clusterTopology(File(config.getString("env-path"), "${topology.name}.txt")) - val servers = mutableListOf<Server>() - val exporter = ComputeMetricReader( - this, - clock, - runner.service, - servers, - ParquetComputeMonitor( - File(config.getString("output-path")), - "portfolio_id=$name/scenario_id=$id/run_id=$repeat", - bufferSize = 4096 - ), - exportInterval = Duration.ofMinutes(5) - ) - - try { - // Instantiate the desired topology - runner.apply(topology) - - coroutineScope { - // Run the workload trace - runner.run(vms, seeder.nextLong(), servers) - - // Stop the metric collection - exporter.close() - } - } finally { - runner.close() - exporter.close() - } - } -} diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/ReplayPortfolio.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/model/Scenario.kt index 17ec48d4..2218a46b 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/ReplayPortfolio.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/model/Scenario.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 AtLarge Research + * Copyright (c) 2022 AtLarge Research * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,32 +20,21 @@ * SOFTWARE. */ -package org.opendc.experiments.capelin - -import org.opendc.compute.workload.trace -import org.opendc.experiments.capelin.model.OperationalPhenomena -import org.opendc.experiments.capelin.model.Topology -import org.opendc.experiments.capelin.model.Workload -import org.opendc.harness.dsl.anyOf +package org.opendc.experiments.capelin.model /** - * A [Portfolio] that compares the original VM placements against our policies. + * A single scenario of a portfolio. + * + * @property topology The topology to test. + * @property workload The workload to test. + * @property operationalPhenomena The [OperationalPhenomena] to model. + * @property allocationPolicy The allocation policy of the scheduler. + * @property partitions The partition of the scenario. */ -public class ReplayPortfolio : Portfolio("replay") { - override val topology: Topology by anyOf( - Topology("base") - ) - - override val workload: Workload by anyOf( - Workload("solvinity", trace("solvinity")) - ) - - override val operationalPhenomena: OperationalPhenomena by anyOf( - OperationalPhenomena(failureFrequency = 0.0, hasInterference = false) - ) - - override val allocationPolicy: String by anyOf( - "replay", - "active-servers" - ) -} +public data class Scenario( + val topology: Topology, + val workload: Workload, + val operationalPhenomena: OperationalPhenomena, + val allocationPolicy: String, + val partitions: Map<String, String> = emptyMap() +) diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CompositeWorkloadPortfolio.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolio/CompositeWorkloadPortfolio.kt index 31e8f961..68eb15b3 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CompositeWorkloadPortfolio.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolio/CompositeWorkloadPortfolio.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 AtLarge Research + * Copyright (c) 2022 AtLarge Research * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,29 +20,26 @@ * SOFTWARE. */ -package org.opendc.experiments.capelin +package org.opendc.experiments.capelin.portfolio import org.opendc.compute.workload.composite import org.opendc.compute.workload.trace import org.opendc.experiments.capelin.model.OperationalPhenomena +import org.opendc.experiments.capelin.model.Scenario import org.opendc.experiments.capelin.model.Topology import org.opendc.experiments.capelin.model.Workload -import org.opendc.harness.dsl.anyOf /** * A [Portfolio] that explores the effect of a composite workload. */ -public class CompositeWorkloadPortfolio : Portfolio("composite-workload") { - private val totalSampleLoad = 1.3301733005049648E12 - - override val topology: Topology by anyOf( +public class CompositeWorkloadPortfolio : Portfolio { + private val topologies = listOf( Topology("base"), Topology("exp-vol-hor-hom"), Topology("exp-vol-ver-hom"), Topology("exp-vel-ver-hom") ) - - override val workload: Workload by anyOf( + private val workloads = listOf( Workload( "all-azure", composite(trace("solvinity-short") to 0.0, trace("azure") to 1.0) @@ -64,12 +61,18 @@ public class CompositeWorkloadPortfolio : Portfolio("composite-workload") { composite(trace("solvinity-short") to 1.0, trace("azure") to 0.0) ) ) + private val operationalPhenomena = OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = false) + private val allocationPolicy = "active-servers" - override val operationalPhenomena: OperationalPhenomena by anyOf( - OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = false) - ) - - override val allocationPolicy: String by anyOf( - "active-servers" - ) + override val scenarios: Iterable<Scenario> = topologies.flatMap { topology -> + workloads.map { workload -> + Scenario( + topology, + workload, + operationalPhenomena, + allocationPolicy, + mapOf("topology" to topology.name, "workload" to workload.name) + ) + } + } } diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/HorVerPortfolio.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolio/HorVerPortfolio.kt index cd093e6c..0d7f3072 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/HorVerPortfolio.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolio/HorVerPortfolio.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 AtLarge Research + * Copyright (c) 2022 AtLarge Research * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,20 +20,20 @@ * SOFTWARE. */ -package org.opendc.experiments.capelin +package org.opendc.experiments.capelin.portfolio import org.opendc.compute.workload.sampleByLoad import org.opendc.compute.workload.trace import org.opendc.experiments.capelin.model.OperationalPhenomena +import org.opendc.experiments.capelin.model.Scenario import org.opendc.experiments.capelin.model.Topology import org.opendc.experiments.capelin.model.Workload -import org.opendc.harness.dsl.anyOf /** * A [Portfolio] that explores the difference between horizontal and vertical scaling. */ -public class HorVerPortfolio : Portfolio("horizontal_vs_vertical") { - override val topology: Topology by anyOf( +public class HorVerPortfolio : Portfolio { + private val topologies = listOf( Topology("base"), Topology("rep-vol-hor-hom"), Topology("rep-vol-hor-het"), @@ -45,18 +45,24 @@ public class HorVerPortfolio : Portfolio("horizontal_vs_vertical") { Topology("exp-vol-ver-het") ) - override val workload: Workload by anyOf( - Workload("solvinity", trace("solvinity").sampleByLoad(0.1)), - Workload("solvinity", trace("solvinity").sampleByLoad(0.25)), - Workload("solvinity", trace("solvinity").sampleByLoad(0.5)), - Workload("solvinity", trace("solvinity").sampleByLoad(1.0)) + private val workloads = listOf( + Workload("solvinity-10%", trace("solvinity").sampleByLoad(0.1)), + Workload("solvinity-25%", trace("solvinity").sampleByLoad(0.25)), + Workload("solvinity-50%", trace("solvinity").sampleByLoad(0.5)), + Workload("solvinity-100%", trace("solvinity").sampleByLoad(1.0)) ) + private val operationalPhenomena = OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true) + private val allocationPolicy = "active-servers" - override val operationalPhenomena: OperationalPhenomena by anyOf( - OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true) - ) - - override val allocationPolicy: String by anyOf( - "active-servers" - ) + override val scenarios: Iterable<Scenario> = topologies.flatMap { topology -> + workloads.map { workload -> + Scenario( + topology, + workload, + operationalPhenomena, + allocationPolicy, + mapOf("topology" to topology.name, "workload" to workload.name) + ) + } + } } diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/MoreHpcPortfolio.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolio/MoreHpcPortfolio.kt index 73e59a58..6afffc09 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/MoreHpcPortfolio.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolio/MoreHpcPortfolio.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 AtLarge Research + * Copyright (c) 2022 AtLarge Research * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,42 +20,48 @@ * SOFTWARE. */ -package org.opendc.experiments.capelin +package org.opendc.experiments.capelin.portfolio import org.opendc.compute.workload.sampleByHpc import org.opendc.compute.workload.sampleByHpcLoad import org.opendc.compute.workload.trace import org.opendc.experiments.capelin.model.OperationalPhenomena +import org.opendc.experiments.capelin.model.Scenario import org.opendc.experiments.capelin.model.Topology import org.opendc.experiments.capelin.model.Workload -import org.opendc.harness.dsl.anyOf /** * A [Portfolio] to explore the effect of HPC workloads. */ -public class MoreHpcPortfolio : Portfolio("more_hpc") { - override val topology: Topology by anyOf( +public class MoreHpcPortfolio : Portfolio { + private val topologies = listOf( Topology("base"), Topology("exp-vol-hor-hom"), Topology("exp-vol-ver-hom"), Topology("exp-vel-ver-hom") ) - - override val workload: Workload by anyOf( - Workload("solvinity", trace("solvinity").sampleByHpc(0.0)), - Workload("solvinity", trace("solvinity").sampleByHpc(0.25)), - Workload("solvinity", trace("solvinity").sampleByHpc(0.5)), - Workload("solvinity", trace("solvinity").sampleByHpc(1.0)), - Workload("solvinity", trace("solvinity").sampleByHpcLoad(0.25)), - Workload("solvinity", trace("solvinity").sampleByHpcLoad(0.5)), - Workload("solvinity", trace("solvinity").sampleByHpcLoad(1.0)) + private val workloads = listOf( + Workload("hpc-0%", trace("solvinity").sampleByHpc(0.0)), + Workload("hpc-25%", trace("solvinity").sampleByHpc(0.25)), + Workload("hpc-50%", trace("solvinity").sampleByHpc(0.5)), + Workload("hpc-100%", trace("solvinity").sampleByHpc(1.0)), + Workload("hpc-load-25%", trace("solvinity").sampleByHpcLoad(0.25)), + Workload("hpc-load-50%", trace("solvinity").sampleByHpcLoad(0.5)), + Workload("hpc-load-100%", trace("solvinity").sampleByHpcLoad(1.0)) ) - override val operationalPhenomena: OperationalPhenomena by anyOf( - OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true) - ) + private val operationalPhenomena = OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true) + private val allocationPolicy: String = "active-servers" - override val allocationPolicy: String by anyOf( - "active-servers" - ) + override val scenarios: Iterable<Scenario> = topologies.flatMap { topology -> + workloads.map { workload -> + Scenario( + topology, + workload, + operationalPhenomena, + allocationPolicy, + mapOf("topology" to topology.name, "workload" to workload.name) + ) + } + } } diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/MoreVelocityPortfolio.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolio/MoreVelocityPortfolio.kt index 9d5717bb..92bf80b3 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/MoreVelocityPortfolio.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolio/MoreVelocityPortfolio.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 AtLarge Research + * Copyright (c) 2022 AtLarge Research * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,20 +20,20 @@ * SOFTWARE. */ -package org.opendc.experiments.capelin +package org.opendc.experiments.capelin.portfolio import org.opendc.compute.workload.sampleByLoad import org.opendc.compute.workload.trace import org.opendc.experiments.capelin.model.OperationalPhenomena +import org.opendc.experiments.capelin.model.Scenario import org.opendc.experiments.capelin.model.Topology import org.opendc.experiments.capelin.model.Workload -import org.opendc.harness.dsl.anyOf /** * A [Portfolio] that explores the effect of adding more velocity to a cluster (e.g., faster machines). */ -public class MoreVelocityPortfolio : Portfolio("more_velocity") { - override val topology: Topology by anyOf( +public class MoreVelocityPortfolio : Portfolio { + private val topologies = listOf( Topology("base"), Topology("rep-vel-ver-hom"), Topology("rep-vel-ver-het"), @@ -41,18 +41,25 @@ public class MoreVelocityPortfolio : Portfolio("more_velocity") { Topology("exp-vel-ver-het") ) - override val workload: Workload by anyOf( - Workload("solvinity", trace("solvinity").sampleByLoad(0.1)), - Workload("solvinity", trace("solvinity").sampleByLoad(0.25)), - Workload("solvinity", trace("solvinity").sampleByLoad(0.5)), - Workload("solvinity", trace("solvinity").sampleByLoad(1.0)) + private val workloads = listOf( + Workload("solvinity-10%", trace("solvinity").sampleByLoad(0.1)), + Workload("solvinity-25%", trace("solvinity").sampleByLoad(0.25)), + Workload("solvinity-50%", trace("solvinity").sampleByLoad(0.5)), + Workload("solvinity-100%", trace("solvinity").sampleByLoad(1.0)) ) - override val operationalPhenomena: OperationalPhenomena by anyOf( - OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true) - ) + private val operationalPhenomena = OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true) + private val allocationPolicy = "active-servers" - override val allocationPolicy: String by anyOf( - "active-servers" - ) + override val scenarios: Iterable<Scenario> = topologies.flatMap { topology -> + workloads.map { workload -> + Scenario( + topology, + workload, + operationalPhenomena, + allocationPolicy, + mapOf("topology" to topology.name, "workload" to workload.name) + ) + } + } } diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/OperationalPhenomenaPortfolio.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolio/OperationalPhenomenaPortfolio.kt index 7ab586b3..f9a9d681 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/OperationalPhenomenaPortfolio.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolio/OperationalPhenomenaPortfolio.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 AtLarge Research + * Copyright (c) 2022 AtLarge Research * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,38 +20,35 @@ * SOFTWARE. */ -package org.opendc.experiments.capelin +package org.opendc.experiments.capelin.portfolio import org.opendc.compute.workload.sampleByLoad import org.opendc.compute.workload.trace import org.opendc.experiments.capelin.model.OperationalPhenomena +import org.opendc.experiments.capelin.model.Scenario import org.opendc.experiments.capelin.model.Topology import org.opendc.experiments.capelin.model.Workload -import org.opendc.harness.dsl.anyOf /** * A [Portfolio] that explores the effect of operational phenomena on metrics. */ -public class OperationalPhenomenaPortfolio : Portfolio("operational_phenomena") { - override val topology: Topology by anyOf( - Topology("base") +public class OperationalPhenomenaPortfolio : Portfolio { + private val topology = Topology("base") + private val workloads = listOf( + Workload("solvinity-10%", trace("solvinity").sampleByLoad(0.1)), + Workload("solvinity-25%", trace("solvinity").sampleByLoad(0.25)), + Workload("solvinity-50%", trace("solvinity").sampleByLoad(0.5)), + Workload("solvinity-100%", trace("solvinity").sampleByLoad(1.0)) ) - override val workload: Workload by anyOf( - Workload("solvinity", trace("solvinity").sampleByLoad(0.1)), - Workload("solvinity", trace("solvinity").sampleByLoad(0.25)), - Workload("solvinity", trace("solvinity").sampleByLoad(0.5)), - Workload("solvinity", trace("solvinity").sampleByLoad(1.0)) - ) - - override val operationalPhenomena: OperationalPhenomena by anyOf( + private val phenomenas = listOf( OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true), OperationalPhenomena(failureFrequency = 0.0, hasInterference = true), OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = false), OperationalPhenomena(failureFrequency = 0.0, hasInterference = false) ) - override val allocationPolicy: String by anyOf( + private val allocationPolicies = listOf( "mem", "mem-inv", "core-mem", @@ -60,4 +57,19 @@ public class OperationalPhenomenaPortfolio : Portfolio("operational_phenomena") "active-servers-inv", "random" ) + + override val scenarios: Iterable<Scenario> = + workloads.flatMap { workload -> + phenomenas.flatMapIndexed { index, operationalPhenomena -> + allocationPolicies.map { allocationPolicy -> + Scenario( + topology, + workload, + operationalPhenomena, + allocationPolicy, + mapOf("workload" to workload.name, "scheduler" to allocationPolicy, "phenomena" to index.toString()) + ) + } + } + } } diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/util/VmPlacementReader.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolio/Portfolio.kt index 67de2777..abf37a5f 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/util/VmPlacementReader.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolio/Portfolio.kt @@ -20,28 +20,16 @@ * SOFTWARE. */ -package org.opendc.experiments.capelin.util +package org.opendc.experiments.capelin.portfolio -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue -import java.io.InputStream +import org.opendc.experiments.capelin.model.Scenario /** - * A parser for the JSON VM placement data files used for the TPDS article on Capelin. + * A portfolio represents a collection of scenarios are tested for the work. */ -class VmPlacementReader { +public interface Portfolio { /** - * The [ObjectMapper] to parse the placement. + * The scenarios that belong to this portfolio. */ - private val mapper = jacksonObjectMapper() - - /** - * Read the VM placements from the input. - */ - fun read(input: InputStream): Map<String, String> { - return mapper.readValue<Map<String, String>>(input) - .mapKeys { "vm__workload__${it.key}.txt" } - .mapValues { it.value.split("/")[1] } // Clusters have format XX0 / X00 - } + val scenarios: Iterable<Scenario> } diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/TestPortfolio.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolio/TestPortfolio.kt index 98eb989d..944e9f43 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/TestPortfolio.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolio/TestPortfolio.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 AtLarge Research + * Copyright (c) 2022 AtLarge Research * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,29 +20,24 @@ * SOFTWARE. */ -package org.opendc.experiments.capelin +package org.opendc.experiments.capelin.portfolio import org.opendc.compute.workload.trace import org.opendc.experiments.capelin.model.OperationalPhenomena +import org.opendc.experiments.capelin.model.Scenario import org.opendc.experiments.capelin.model.Topology import org.opendc.experiments.capelin.model.Workload -import org.opendc.harness.dsl.anyOf /** * A [Portfolio] to perform a simple test run. */ -public class TestPortfolio : Portfolio("test") { - override val topology: Topology by anyOf( - Topology("base") +public class TestPortfolio : Portfolio { + override val scenarios: Iterable<Scenario> = listOf( + Scenario( + Topology("base"), + Workload("solvinity", trace("solvinity")), + OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true), + "active-servers" + ) ) - - override val workload: Workload by anyOf( - Workload("solvinity", trace("solvinity")) - ) - - override val operationalPhenomena: OperationalPhenomena by anyOf( - OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true) - ) - - override val allocationPolicy: String by anyOf("active-servers") } diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/resources/application.conf b/opendc-experiments/opendc-experiments-capelin/src/main/resources/application.conf deleted file mode 100644 index f0e0f2d3..00000000 --- a/opendc-experiments/opendc-experiments-capelin/src/main/resources/application.conf +++ /dev/null @@ -1,6 +0,0 @@ -# Default configuration for the Capelin experiments -opendc.experiments.capelin { - env-path = input/environments/ - trace-path = input/traces/ - output-path = output -} diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/resources/log4j2.xml b/opendc-experiments/opendc-experiments-capelin/src/main/resources/log4j2.xml index d46b50c3..e479f2ca 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/main/resources/log4j2.xml +++ b/opendc-experiments/opendc-experiments-capelin/src/main/resources/log4j2.xml @@ -30,13 +30,7 @@ </Console> </Appenders> <Loggers> - <Logger name="org.opendc" level="debug" additivity="false"> - <AppenderRef ref="Console"/> - </Logger> - <Logger name="org.opendc.experiments.capelin" level="info" additivity="false"> - <AppenderRef ref="Console"/> - </Logger> - <Logger name="org.opendc.experiments.vm.trace" level="debug" additivity="false"> + <Logger name="org.opendc" level="warn" additivity="false"> <AppenderRef ref="Console"/> </Logger> <Logger name="org.apache.hadoop" level="warn" additivity="false"> |
