diff options
Diffstat (limited to 'opendc-experiments/opendc-experiments-base/src/main')
5 files changed, 145 insertions, 27 deletions
diff --git a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/runner/ScenarioRunner.kt b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/runner/ScenarioRunner.kt index bf5188a2..d6ee5d72 100644 --- a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/runner/ScenarioRunner.kt +++ b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/runner/ScenarioRunner.kt @@ -58,9 +58,10 @@ public fun runScenarios( val ansiReset = "\u001B[0m" val ansiGreen = "\u001B[32m" val ansiBlue = "\u001B[34m" - clearOutputFolder(scenarios[0].outputFolder) - for (scenario in scenarios) { + setupOutputFolderStructure(scenarios[0].outputFolder) + + for ((i, scenario) in scenarios.withIndex()) { val pool = ForkJoinPool(parallelism) println( "\n\n$ansiGreen================================================================================$ansiReset", @@ -70,6 +71,7 @@ public fun runScenarios( runScenario( scenario, pool, + i, ) } } @@ -84,6 +86,7 @@ public fun runScenarios( public fun runScenario( scenario: Scenario, pool: ForkJoinPool, + index: Int = -1, ) { val pb = ProgressBarBuilder().setInitialMax(scenario.runs.toLong()).setStyle(ProgressBarStyle.ASCII) @@ -91,7 +94,7 @@ public fun runScenario( pool.submit { LongStream.range(0, scenario.runs.toLong()).parallel().forEach { - runScenario(scenario, scenario.initialSeed + it) + runScenario(scenario, scenario.initialSeed + it, index) pb.step() } pb.close() @@ -107,6 +110,7 @@ public fun runScenario( public fun runScenario( scenario: Scenario, seed: Long, + index: Int = 0, ): Unit = runSimulation { val serviceDomain = "compute.opendc.org" @@ -126,7 +130,7 @@ public fun runScenario( val carbonTrace = getCarbonTrace(scenario.carbonTracePath) val startTime = Duration.ofMillis(vms.minOf { it.startTime }.toEpochMilli()) - saveInOutputFolder(provisioner, serviceDomain, scenario, seed, startTime, carbonTrace) + addExportModel(provisioner, serviceDomain, scenario, seed, startTime, carbonTrace, index) val service = provisioner.registry.resolve(serviceDomain, ComputeService::class.java)!! service.replay(timeSource, vms, failureModelSpec = scenario.failureModel, seed = seed) @@ -143,19 +147,20 @@ public fun runScenario( * @param startTime The start time of the simulation given by the workload trace. * @param carbonTrace The carbon trace used to determine carbon emissions. */ -public fun saveInOutputFolder( +public fun addExportModel( provisioner: Provisioner, serviceDomain: String, scenario: Scenario, seed: Long, startTime: Duration, carbonTrace: CarbonTrace, + index: Int, ) { provisioner.runStep( registerComputeMonitor( serviceDomain, ParquetComputeMonitor( - File("${scenario.outputFolder}/${scenario.name}"), + File("${scenario.outputFolder}/raw-output/$index"), "seed=$seed", bufferSize = 4096, ), @@ -173,3 +178,20 @@ public fun saveInOutputFolder( public fun clearOutputFolder(outputFolderPath: String) { if (File(outputFolderPath).exists()) File(outputFolderPath).deleteRecursively() } + +/** + * Utility function to create the output folder structure for the simulation results. + * @param folderPath The path to the output folder + */ +private fun setupOutputFolderStructure(folderPath: String) { + val trackrPath = folderPath + "/trackr.json" + val simulationAnalysisPath = folderPath + "/simulation-analysis/" + val energyAnalysisPath = simulationAnalysisPath + "/power_draw/" + val emissionsAnalysisPath = simulationAnalysisPath + "/carbon_emission/" + + File(folderPath).mkdir() + File(trackrPath).createNewFile() + File(simulationAnalysisPath).mkdir() + File(energyAnalysisPath).mkdir() + File(emissionsAnalysisPath).mkdir() +} diff --git a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/scenario/Scenario.kt b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/scenario/Scenario.kt index 4f3fcd4f..7f0308fc 100644 --- a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/scenario/Scenario.kt +++ b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/scenario/Scenario.kt @@ -41,7 +41,9 @@ import WorkloadSpec * @property runs The Int representing the number of runs of the scenario. It defaults to 1. * @property initialSeed The Int representing the initial seed of the scenario. It defaults to 0. */ + public data class Scenario( + var id: Int = -1, val topology: ScenarioTopologySpec, val workload: WorkloadSpec, val allocationPolicy: AllocationPolicySpec, diff --git a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/scenario/ScenarioFactories.kt b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/scenario/ScenarioFactories.kt index 010c8845..19f8ebf0 100644 --- a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/scenario/ScenarioFactories.kt +++ b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/scenario/ScenarioFactories.kt @@ -22,13 +22,12 @@ package org.opendc.experiments.base.scenario -import AllocationPolicySpec import ScenarioTopologySpec -import WorkloadSpec import org.opendc.experiments.base.scenario.specs.ScenarioSpec import java.io.File private val scenarioReader = ScenarioReader() +private val scenarioWriter = ScenarioWriter() /** * Returns a list of Scenarios from a given file path (input). @@ -58,7 +57,14 @@ public fun getScenarios(file: File): List<Scenario> { * @return A list of Scenarios. */ public fun getScenarios(scenarioSpec: ScenarioSpec): List<Scenario> { + val outputFolder = scenarioSpec.outputFolder + "/" + scenarioSpec.name + File(outputFolder).mkdirs() + + val trackrPath = outputFolder + "/trackr.json" + File(trackrPath).createNewFile() + val scenarios = mutableListOf<Scenario>() + var scenarioID = 0 for (scenarioTopologySpec in scenarioSpec.topologies) { for (workloadSpec in scenarioSpec.workloads) { @@ -68,18 +74,21 @@ public fun getScenarios(scenarioSpec: ScenarioSpec): List<Scenario> { for (exportModelSpec in scenarioSpec.exportModels) { val scenario = Scenario( + id = scenarioID, topology = scenarioTopologySpec, workload = workloadSpec, allocationPolicy = allocationPolicySpec, failureModel = failureModelSpec, carbonTracePath = carbonTracePath, exportModel = exportModelSpec, - outputFolder = scenarioSpec.outputFolder, - name = getOutputFolderName(scenarioSpec, scenarioTopologySpec, workloadSpec, allocationPolicySpec), + outputFolder = outputFolder, + name = scenarioID.toString(), runs = scenarioSpec.runs, initialSeed = scenarioSpec.initialSeed, ) + trackScenario(scenarioSpec, outputFolder, scenario, scenarioTopologySpec) scenarios.add(scenario) + scenarioID++ } } } @@ -91,22 +100,38 @@ public fun getScenarios(scenarioSpec: ScenarioSpec): List<Scenario> { } /** - * Returns a string representing the output folder name for a given ScenarioSpec, CpuPowerModel, AllocationPolicySpec, and topology path. + * Writes a ScenarioSpec to a file. * * @param scenarioSpec The ScenarioSpec. - * @param topology The specification of the topology used - * @param workload The specification of the workload - * @param allocationPolicy The allocation policy used - * @return A string representing the output folder name. + * @param outputFolder The output folder path. + * @param scenario The Scenario. + * @param topologySpec The TopologySpec. + */ -public fun getOutputFolderName( +public fun trackScenario( scenarioSpec: ScenarioSpec, - topology: ScenarioTopologySpec, - workload: WorkloadSpec, - allocationPolicy: AllocationPolicySpec, -): String { - return "scenario=${scenarioSpec.name}" + - "-topology=${topology.name}" + - "-workload=${workload.name}" + - "-allocation=${allocationPolicy.name}" + outputFolder: String, + scenario: Scenario, + topologySpec: ScenarioTopologySpec, +) { + val trackrPath = outputFolder + "/trackr.json" + scenarioWriter.write( + ScenarioSpec( + id = scenario.id, + name = scenarioSpec.name, + topologies = listOf(topologySpec), + workloads = listOf(scenario.workload), + allocationPolicies = listOf(scenario.allocationPolicy), + // when implemented, add failure models here + carbonTracePaths = listOf(scenario.carbonTracePath), + exportModels = listOf(scenario.exportModel), + outputFolder = scenario.outputFolder, + initialSeed = scenario.initialSeed, + runs = scenario.runs, + ), + File(trackrPath), + ) + + // remove the last comma + File(trackrPath).writeText(File(trackrPath).readText().dropLast(3) + "]") } diff --git a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/scenario/ScenarioWriter.kt b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/scenario/ScenarioWriter.kt new file mode 100644 index 00000000..8d99647a --- /dev/null +++ b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/scenario/ScenarioWriter.kt @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2024 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.base.scenario + +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import org.opendc.experiments.base.scenario.specs.ScenarioSpec +import java.io.File + +/** + * A writer for writing scenarios to a file. + * @param jsonText The JSON text to write to the file, which is constantly updated during the writing process. + * @param json The JSON object used to encode the scenario specification. + */ +public class ScenarioWriter { + private var jsonText = "[" + private val json = Json { prettyPrint = true } + + /** + * Write the given [scenarioSpec] to the given [file]. + */ + public fun write( + scenarioSpec: ScenarioSpec, + file: File, + ) { + openArray(file) + val jsonString = json.encodeToString(scenarioSpec) + "," + jsonText += jsonString + "\n" + file.writeText(jsonText) + closeArray(file) + } + + /** + * Delete the last character of the file. + */ + private fun openArray(file: File) { + val text = file.readText() + file.writeText(text.dropLast(0)) + } + + /** + * Add the closing bracket to the file. + */ + private fun closeArray(file: File) { + file.appendText("]") + } +} diff --git a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/scenario/specs/ScenarioSpec.kt b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/scenario/specs/ScenarioSpec.kt index cfbb913c..876a62cf 100644 --- a/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/scenario/specs/ScenarioSpec.kt +++ b/opendc-experiments/opendc-experiments-base/src/main/kotlin/org/opendc/experiments/base/scenario/specs/ScenarioSpec.kt @@ -28,6 +28,7 @@ import FailureModelSpec import ScenarioTopologySpec import WorkloadSpec import kotlinx.serialization.Serializable +import java.util.UUID /** * specification describing a scenario @@ -43,6 +44,8 @@ import kotlinx.serialization.Serializable */ @Serializable public data class ScenarioSpec( + var id: Int = -1, + var name: String = "", val topologies: List<ScenarioTopologySpec>, val workloads: List<WorkloadSpec>, val allocationPolicies: List<AllocationPolicySpec>, @@ -52,7 +55,6 @@ public data class ScenarioSpec( val outputFolder: String = "output", val initialSeed: Int = 0, val runs: Int = 1, - var name: String = "", ) { init { require(runs > 0) { "The number of runs should always be positive" } @@ -60,8 +62,8 @@ public data class ScenarioSpec( // generate name if not provided // TODO: improve this if (name == "") { - name = - "workload=${workloads[0].name}_topology=${topologies[0].name}_allocationPolicy=${allocationPolicies[0].name}" + name = "unnamed-simulation-${UUID.randomUUID().toString().substring(0, 4)}" +// "workload=${workloads[0].name}_topology=${topologies[0].name}_allocationPolicy=${allocationPolicies[0].name}" } } } |
