diff options
Diffstat (limited to 'opendc-experiments')
29 files changed, 554 insertions, 733 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/ReplayPortfolio.kt deleted file mode 100644 index 17ec48d4..00000000 --- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/ReplayPortfolio.kt +++ /dev/null @@ -1,51 +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 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 - -/** - * A [Portfolio] that compares the original VM placements against our policies. - */ -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" - ) -} diff --git a/opendc-experiments/opendc-experiments-serverless20/build.gradle.kts b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/model/Scenario.kt index a6391986..2218a46b 100644 --- a/opendc-experiments/opendc-experiments-serverless20/build.gradle.kts +++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/model/Scenario.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 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,19 +20,21 @@ * SOFTWARE. */ -description = "Experiments for OpenDC Serverless" +package org.opendc.experiments.capelin.model -/* Build configuration */ -plugins { - `experiment-conventions` - `testing-conventions` -} - -dependencies { - api(projects.opendcHarness.opendcHarnessApi) - implementation(projects.opendcSimulator.opendcSimulatorCore) - implementation(projects.opendcFaas.opendcFaasService) - implementation(projects.opendcFaas.opendcFaasSimulator) - implementation(libs.kotlin.logging) - implementation(libs.config) -} +/** + * 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 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-serverless20/src/main/kotlin/org/opendc/experiments/serverless/trace/FunctionTrace.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolio/Portfolio.kt index 4fea6b96..abf37a5f 100644 --- a/opendc-experiments/opendc-experiments-serverless20/src/main/kotlin/org/opendc/experiments/serverless/trace/FunctionTrace.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/portfolio/Portfolio.kt @@ -20,9 +20,16 @@ * SOFTWARE. */ -package org.opendc.experiments.serverless.trace +package org.opendc.experiments.capelin.portfolio + +import org.opendc.experiments.capelin.model.Scenario /** - * A trace for a single function + * A portfolio represents a collection of scenarios are tested for the work. */ -public data class FunctionTrace(val id: String, val maxMemory: Int, val samples: List<FunctionSample>) +public interface Portfolio { + /** + * The scenarios that belong to this portfolio. + */ + 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/kotlin/org/opendc/experiments/capelin/util/VmPlacementReader.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/util/VmPlacementReader.kt deleted file mode 100644 index 67de2777..00000000 --- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/util/VmPlacementReader.kt +++ /dev/null @@ -1,47 +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.util - -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue -import java.io.InputStream - -/** - * A parser for the JSON VM placement data files used for the TPDS article on Capelin. - */ -class VmPlacementReader { - /** - * The [ObjectMapper] to parse the placement. - */ - 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 - } -} 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"> diff --git a/opendc-experiments/opendc-experiments-serverless20/README.md b/opendc-experiments/opendc-experiments-serverless20/README.md deleted file mode 100644 index 40855ad0..00000000 --- a/opendc-experiments/opendc-experiments-serverless20/README.md +++ /dev/null @@ -1,7 +0,0 @@ -OpenDC Serverless -================= - -This module contains a reproduction of the experiments of Soufiane Jounaid's BSc Computer Science thesis: -OpenDC Serverless: Design, Implementation and Evaluation of a FaaS Platform Simulator [1] - -[1] https://drive.google.com/file/d/12hox3PwagpD0jNFA57tO4r2HqvOonkY3/view?usp=sharing diff --git a/opendc-experiments/opendc-experiments-serverless20/src/main/kotlin/org/opendc/experiments/serverless/ServerlessExperiment.kt b/opendc-experiments/opendc-experiments-serverless20/src/main/kotlin/org/opendc/experiments/serverless/ServerlessExperiment.kt deleted file mode 100644 index 1c357f67..00000000 --- a/opendc-experiments/opendc-experiments-serverless20/src/main/kotlin/org/opendc/experiments/serverless/ServerlessExperiment.kt +++ /dev/null @@ -1,132 +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.serverless - -import com.typesafe.config.ConfigFactory -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import mu.KotlinLogging -import org.opendc.experiments.serverless.trace.FunctionTraceWorkload -import org.opendc.experiments.serverless.trace.ServerlessTraceReader -import org.opendc.faas.service.FaaSService -import org.opendc.faas.service.autoscaler.FunctionTerminationPolicyFixed -import org.opendc.faas.service.router.RandomRoutingPolicy -import org.opendc.faas.simulator.SimFunctionDeployer -import org.opendc.faas.simulator.delay.ColdStartModel -import org.opendc.faas.simulator.delay.StochasticDelayInjector -import org.opendc.harness.dsl.Experiment -import org.opendc.harness.dsl.anyOf -import org.opendc.simulator.compute.model.MachineModel -import org.opendc.simulator.compute.model.MemoryUnit -import org.opendc.simulator.compute.model.ProcessingNode -import org.opendc.simulator.compute.model.ProcessingUnit -import org.opendc.simulator.core.runBlockingSimulation -import java.io.File -import java.time.Duration -import java.util.* -import kotlin.math.max - -/** - * A reproduction of the experiments of Soufiane Jounaid's BSc Computer Science thesis: - * OpenDC Serverless: Design, Implementation and Evaluation of a FaaS Platform Simulator. - */ -public class ServerlessExperiment : Experiment("Serverless") { - /** - * The logger for this portfolio instance. - */ - private val logger = KotlinLogging.logger {} - - /** - * The configuration to use. - */ - private val config = ConfigFactory.load().getConfig("opendc.experiments.serverless20") - - /** - * The routing policy to test. - */ - private val routingPolicy by anyOf(RandomRoutingPolicy()) - - /** - * The cold start models to test. - */ - private val coldStartModel by anyOf(ColdStartModel.LAMBDA, ColdStartModel.AZURE, ColdStartModel.GOOGLE) - - override fun doRun(repeat: Int): Unit = runBlockingSimulation { - val trace = ServerlessTraceReader().parse(File(config.getString("trace-path"))) - val traceById = trace.associateBy { it.id } - val delayInjector = StochasticDelayInjector(coldStartModel, Random()) - val deployer = SimFunctionDeployer(clock, this, createMachineModel(), delayInjector) { FunctionTraceWorkload(traceById.getValue(it.name)) } - val service = - FaaSService( - coroutineContext, - clock, - deployer, - routingPolicy, - FunctionTerminationPolicyFixed(coroutineContext, clock, timeout = Duration.ofMinutes(10)) - ) - val client = service.newClient() - - coroutineScope { - for (entry in trace) { - launch { - val function = client.newFunction(entry.id, entry.maxMemory.toLong()) - var offset = Long.MIN_VALUE - - for (sample in entry.samples) { - if (sample.invocations == 0) { - continue - } - - if (offset < 0) { - offset = sample.timestamp - clock.millis() - } - - delay(max(0, (sample.timestamp - offset) - clock.millis())) - - logger.info { "Invoking function ${entry.id} ${sample.invocations} times [${sample.timestamp}]" } - - repeat(sample.invocations) { - function.invoke() - } - } - } - } - } - - client.close() - service.close() - } - - /** - * Construct the machine model to test with. - */ - private fun createMachineModel(): MachineModel { - val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) - - return MachineModel( - cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, - memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } - ) - } -} diff --git a/opendc-experiments/opendc-experiments-serverless20/src/main/kotlin/org/opendc/experiments/serverless/trace/FunctionSample.kt b/opendc-experiments/opendc-experiments-serverless20/src/main/kotlin/org/opendc/experiments/serverless/trace/FunctionSample.kt deleted file mode 100644 index 492f44b9..00000000 --- a/opendc-experiments/opendc-experiments-serverless20/src/main/kotlin/org/opendc/experiments/serverless/trace/FunctionSample.kt +++ /dev/null @@ -1,44 +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.serverless.trace - -/** - * A sample of a single function. - * - * @param timestamp The timestamp of the function. - * @param duration The average execution time of the function. - * @param invocations The number of invocations. - * @param provisionedCpu The provisioned CPU for this function in MHz. - * @param provisionedMem The amount of memory provisioned for this function in MB. - * @param cpuUsage The actual CPU usage in MHz. - * @param memUsage The actual memory usage in MB. - */ -public data class FunctionSample( - val timestamp: Long, - val duration: Long, - val invocations: Int, - val provisionedCpu: Int, - val provisionedMem: Int, - val cpuUsage: Double, - val memUsage: Double -) diff --git a/opendc-experiments/opendc-experiments-serverless20/src/main/kotlin/org/opendc/experiments/serverless/trace/FunctionTraceWorkload.kt b/opendc-experiments/opendc-experiments-serverless20/src/main/kotlin/org/opendc/experiments/serverless/trace/FunctionTraceWorkload.kt deleted file mode 100644 index bbe130e3..00000000 --- a/opendc-experiments/opendc-experiments-serverless20/src/main/kotlin/org/opendc/experiments/serverless/trace/FunctionTraceWorkload.kt +++ /dev/null @@ -1,37 +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.serverless.trace - -import org.opendc.faas.simulator.workload.SimFaaSWorkload -import org.opendc.simulator.compute.workload.SimTrace -import org.opendc.simulator.compute.workload.SimTraceFragment -import org.opendc.simulator.compute.workload.SimTraceWorkload -import org.opendc.simulator.compute.workload.SimWorkload - -/** - * A [SimFaaSWorkload] for a [FunctionTrace]. - */ -class FunctionTraceWorkload(trace: FunctionTrace) : - SimFaaSWorkload, SimWorkload by SimTraceWorkload(SimTrace.ofFragments(trace.samples.map { SimTraceFragment(it.timestamp, it.duration, it.cpuUsage, 1) })) { - override suspend fun invoke() {} -} diff --git a/opendc-experiments/opendc-experiments-serverless20/src/main/kotlin/org/opendc/experiments/serverless/trace/ServerlessTraceReader.kt b/opendc-experiments/opendc-experiments-serverless20/src/main/kotlin/org/opendc/experiments/serverless/trace/ServerlessTraceReader.kt deleted file mode 100644 index 6dc35c59..00000000 --- a/opendc-experiments/opendc-experiments-serverless20/src/main/kotlin/org/opendc/experiments/serverless/trace/ServerlessTraceReader.kt +++ /dev/null @@ -1,107 +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.serverless.trace - -import mu.KotlinLogging -import java.io.File -import kotlin.math.max - -/** - * A trace reader for the serverless workload trace used in the OpenDC Serverless thesis. - */ -public class ServerlessTraceReader { - /** - * The logger for this portfolio instance. - */ - private val logger = KotlinLogging.logger {} - - /** - * Parse the traces at the specified [path]. - */ - public fun parse(path: File): List<FunctionTrace> { - return if (path.isFile) { - listOf(parseSingle(path)) - } else { - path.walk() - .filterNot { it.isDirectory } - .map { file -> - logger.info { "Parsing $file" } - parseSingle(file) - } - .toList() - } - } - - /** - * Parse a single trace. - */ - private fun parseSingle(path: File): FunctionTrace { - val samples = mutableListOf<FunctionSample>() - val id = path.nameWithoutExtension - var idx = 0 - - var timestampCol = 0 - var invocationsCol = 0 - var execTimeCol = 0 - var provCpuCol = 0 - var provMemCol = 0 - var cpuUsageCol = 0 - var memoryUsageCol = 0 - var maxMemory = 0 - - path.forEachLine { line -> - if (line.startsWith("#") && line.isNotBlank()) { - return@forEachLine - } - - val values = line.split(",") - - /* Header parsing */ - if (idx++ == 0) { - val header = values.mapIndexed { col, name -> Pair(name.trim(), col) }.toMap() - timestampCol = header["Timestamp [ms]"]!! - invocationsCol = header["Invocations"]!! - execTimeCol = header["Avg Exec time per Invocation"]!! - provCpuCol = header["Provisioned CPU [Mhz]"]!! - provMemCol = header["Provisioned Memory [mb]"]!! - cpuUsageCol = header["Avg cpu usage per Invocation [Mhz]"]!! - memoryUsageCol = header["Avg mem usage per Invocation [mb]"]!! - return@forEachLine - } - - val timestamp = values[timestampCol].trim().toLong() - val invocations = values[invocationsCol].trim().toInt() - val execTime = values[execTimeCol].trim().toLong() - val provisionedCpu = values[provCpuCol].trim().toInt() - val provisionedMemory = values[provMemCol].trim().toInt() - val cpuUsage = values[cpuUsageCol].trim().toDouble() - val memoryUsage = values[memoryUsageCol].trim().toDouble() - - maxMemory = max(maxMemory, provisionedMemory) - - samples.add(FunctionSample(timestamp, execTime, invocations, provisionedCpu, provisionedMemory, cpuUsage, memoryUsage)) - } - - return FunctionTrace(id, maxMemory, samples) - } -} diff --git a/opendc-experiments/opendc-experiments-serverless20/src/main/resources/application.conf b/opendc-experiments/opendc-experiments-serverless20/src/main/resources/application.conf deleted file mode 100644 index a065c5d6..00000000 --- a/opendc-experiments/opendc-experiments-serverless20/src/main/resources/application.conf +++ /dev/null @@ -1,8 +0,0 @@ -# Default configuration for the serverless experiments -opendc.experiments.serverless20 { - # Path to the directory containing the serverless trace - trace-path = input/traces/serverless - - # Path to the output directory to write the results to - output-path = output -} diff --git a/opendc-experiments/opendc-experiments-tf20/build.gradle.kts b/opendc-experiments/opendc-experiments-tf20/build.gradle.kts index f61c8fef..7b3b084f 100644 --- a/opendc-experiments/opendc-experiments-tf20/build.gradle.kts +++ b/opendc-experiments/opendc-experiments-tf20/build.gradle.kts @@ -20,16 +20,15 @@ * SOFTWARE. */ -description = "Experiments with the OpenDC TensorFlow model" +description = "TensorFlow application model in OpenDC" /* Build configuration */ plugins { - `experiment-conventions` + `kotlin-conventions` `testing-conventions` } dependencies { - api(projects.opendcHarness.opendcHarnessApi) implementation(projects.opendcSimulator.opendcSimulatorCore) implementation(projects.opendcSimulator.opendcSimulatorCompute) implementation(projects.opendcCommon) diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/Models.kt b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/Models.kt index 9ef5b621..be166bd5 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/Models.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/Models.kt @@ -20,8 +20,10 @@ * SOFTWARE. */ -package org.opendc.experiments.tf20.keras +package org.opendc.experiments.tf20 +import org.opendc.experiments.tf20.keras.Sequential +import org.opendc.experiments.tf20.keras.TrainableModel import org.opendc.experiments.tf20.keras.activations.Activation import org.opendc.experiments.tf20.keras.layer.conv.Conv2D import org.opendc.experiments.tf20.keras.layer.conv.ConvPadding diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/core/SimTFDevice.kt b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/core/SimTFDevice.kt index d2105196..90350142 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/core/SimTFDevice.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/core/SimTFDevice.kt @@ -39,6 +39,7 @@ import java.util.* import kotlin.coroutines.Continuation import kotlin.coroutines.CoroutineContext import kotlin.coroutines.resume +import kotlin.math.ceil import kotlin.math.roundToLong /** @@ -137,7 +138,7 @@ public class SimTFDevice( if (activeWork.consume(consumedWork)) { this.activeWork = null } else { - val duration = (activeWork.flops / conn.capacity * 1000).roundToLong() + val duration = ceil(activeWork.flops / conn.capacity * 1000).toLong() conn.push(conn.capacity) return duration } diff --git a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/TensorFlowExperiment.kt b/opendc-experiments/opendc-experiments-tf20/src/test/kotlin/org/opendc/experiments/tf20/TensorFlowTest.kt index 19236029..7d72b48d 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/main/kotlin/org/opendc/experiments/tf20/TensorFlowExperiment.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/test/kotlin/org/opendc/experiments/tf20/TensorFlowTest.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 @@ -22,43 +22,76 @@ package org.opendc.experiments.tf20 +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertAll import org.opendc.experiments.tf20.core.SimTFDevice -import org.opendc.experiments.tf20.distribute.* -import org.opendc.experiments.tf20.keras.AlexNet +import org.opendc.experiments.tf20.distribute.OneDeviceStrategy import org.opendc.experiments.tf20.util.MLEnvironmentReader -import org.opendc.harness.dsl.Experiment -import org.opendc.harness.dsl.anyOf import org.opendc.simulator.compute.power.LinearPowerModel import org.opendc.simulator.core.runBlockingSimulation /** - * Experiments with the TensorFlow simulation model. + * Integration test suite for the TensorFlow application model in OpenDC. */ -public class TensorFlowExperiment : Experiment(name = "tf20") { +class TensorFlowTest { /** - * The environment file to use. + * Smoke test that tests the capabilities of the TensorFlow application model in OpenDC. */ - private val environmentFile by anyOf("/kth.json") + @Test + fun testSmokeAlexNet() = runBlockingSimulation { + val envInput = checkNotNull(TensorFlowTest::class.java.getResourceAsStream("/kth.json")) + val def = MLEnvironmentReader().readEnvironment(envInput).first() + + val device = SimTFDevice( + def.uid, def.meta["gpu"] as Boolean, coroutineContext, clock, def.model.cpus[0], def.model.memory[0], + LinearPowerModel(250.0, 60.0) + ) + val strategy = OneDeviceStrategy(device) + val batchSize = 32 + val model = AlexNet(batchSize.toLong()) + model.use { + it.compile(strategy) + + it.fit(epochs = 9088 / batchSize, batchSize = batchSize) + } + + device.close() + + val stats = device.getDeviceStats() + assertAll( + { assertEquals(3309694252, clock.millis()) }, + { assertEquals(8.2520933087E8, stats.energyUsage) } + ) + } /** - * The batch size used. + * Smoke test that tests the capabilities of the TensorFlow application model in OpenDC. */ - private val batchSize by anyOf(16, 32, 64, 128) - - override fun doRun(repeat: Int): Unit = runBlockingSimulation { - val envInput = checkNotNull(TensorFlowExperiment::class.java.getResourceAsStream(environmentFile)) + @Test + fun testSmokeVGG() = runBlockingSimulation { + val envInput = checkNotNull(TensorFlowTest::class.java.getResourceAsStream("/kth.json")) val def = MLEnvironmentReader().readEnvironment(envInput).first() + val device = SimTFDevice( def.uid, def.meta["gpu"] as Boolean, coroutineContext, clock, def.model.cpus[0], def.model.memory[0], LinearPowerModel(250.0, 60.0) ) val strategy = OneDeviceStrategy(device) - - val model = AlexNet(batchSize.toLong()) + val batchSize = 128 + val model = VGG16(batchSize.toLong()) model.use { it.compile(strategy) it.fit(epochs = 9088 / batchSize, batchSize = batchSize) } + + device.close() + + val stats = device.getDeviceStats() + assertAll( + { assertEquals(176230322904, clock.millis()) }, + { assertEquals(4.296544914744E10, stats.energyUsage) } + ) } } diff --git a/opendc-experiments/opendc-experiments-tf20/src/test/kotlin/org/opendc/experiments/tf20/core/SimTFDeviceTest.kt b/opendc-experiments/opendc-experiments-tf20/src/test/kotlin/org/opendc/experiments/tf20/core/SimTFDeviceTest.kt index fd18a3a7..21d30250 100644 --- a/opendc-experiments/opendc-experiments-tf20/src/test/kotlin/org/opendc/experiments/tf20/core/SimTFDeviceTest.kt +++ b/opendc-experiments/opendc-experiments-tf20/src/test/kotlin/org/opendc/experiments/tf20/core/SimTFDeviceTest.kt @@ -63,6 +63,8 @@ internal class SimTFDeviceTest { launch { device.compute(2e6) } } + device.close() + val stats = device.getDeviceStats() assertAll( |
