From 0df3d9ced743ac3385dd710c7133a6cf369b051c Mon Sep 17 00:00:00 2001 From: Radu Nicolae Date: Mon, 16 Jun 2025 18:01:07 +0200 Subject: integrated M3SA, updated with tests and CpuPowerModels --- .../org/opendc/experiments/m3sa/M3SAAnalyzer.kt | 75 ++++++++++++++++++++ .../org/opendc/experiments/m3sa/M3saAnalyzer.kt | 57 --------------- .../org/opendc/experiments/m3sa/runner/M3SACli.kt | 80 ++++++++++++++-------- .../opendc/experiments/m3sa/runner/M3SARunner.kt | 33 ++++++++- 4 files changed, 159 insertions(+), 86 deletions(-) create mode 100644 opendc-experiments/opendc-experiments-m3sa/src/main/kotlin/org/opendc/experiments/m3sa/M3SAAnalyzer.kt delete mode 100644 opendc-experiments/opendc-experiments-m3sa/src/main/kotlin/org/opendc/experiments/m3sa/M3saAnalyzer.kt (limited to 'opendc-experiments/opendc-experiments-m3sa/src/main/kotlin') diff --git a/opendc-experiments/opendc-experiments-m3sa/src/main/kotlin/org/opendc/experiments/m3sa/M3SAAnalyzer.kt b/opendc-experiments/opendc-experiments-m3sa/src/main/kotlin/org/opendc/experiments/m3sa/M3SAAnalyzer.kt new file mode 100644 index 00000000..5cc7cb78 --- /dev/null +++ b/opendc-experiments/opendc-experiments-m3sa/src/main/kotlin/org/opendc/experiments/m3sa/M3SAAnalyzer.kt @@ -0,0 +1,75 @@ +/* + * 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. + */ + +import java.nio.file.Files +import java.nio.file.Paths + +/** + * This constant variable should be changed depending on the root folder that is being run. + * PATH_TO_PYTHON_MAIN should point to the main python file, ran when the analysis starts. + */ + +public fun m3saAnalyze( + outputFolderPath: String, + m3saSetupPath: String, + m3saExecPath: String, +) { + // script to run + val scriptPath = + Paths.get(m3saExecPath, "main.py") + .toAbsolutePath() + .normalize() + .toString() + + // look for venv python; if missing, use system python3 + val venvPython = + Paths.get(m3saExecPath, "venv", "bin", "python3") + .toAbsolutePath() + .normalize() + val pythonBin = + if (Files.isRegularFile(venvPython) && Files.isExecutable(venvPython)) { + venvPython.toString() + } else { + "python3" // fallback + } + + val process = + ProcessBuilder( + pythonBin, + scriptPath, + m3saSetupPath, + "$outputFolderPath/raw-output", + "-o", + outputFolderPath, + ) + .redirectErrorStream(true) + .start() + + val exitCode = process.waitFor() + val output = process.inputStream.bufferedReader().readText() + if (exitCode == 0) { + println("[M3SA says] Success:\n$output") + } else { + println("[M3SA says] Exit code $exitCode; Output:\n$output") + throw RuntimeException("M3SA analysis failed with exit code $exitCode") + } +} diff --git a/opendc-experiments/opendc-experiments-m3sa/src/main/kotlin/org/opendc/experiments/m3sa/M3saAnalyzer.kt b/opendc-experiments/opendc-experiments-m3sa/src/main/kotlin/org/opendc/experiments/m3sa/M3saAnalyzer.kt deleted file mode 100644 index 545ed656..00000000 --- a/opendc-experiments/opendc-experiments-m3sa/src/main/kotlin/org/opendc/experiments/m3sa/M3saAnalyzer.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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.m3sa - -import kotlin.io.path.Path - -/** - * This constant variable should be changed depending on the root folder that is being run. - * PATH_TO_PYTHON_MAIN should point to the main python file, ran when the analysis starts. - */ - -public val ANALYSIS_SCRIPTS_DIRECTORY: String = "./opendc-experiments/opendc-experiments-m3sa/src/main/python" -public val ABSOLUTE_SCRIPT_PATH: String = - Path("$ANALYSIS_SCRIPTS_DIRECTORY/main.py").toAbsolutePath().normalize().toString() -public val SCRIPT_LANGUAGE: String = Path("$ANALYSIS_SCRIPTS_DIRECTORY/venv/bin/python3").toAbsolutePath().normalize().toString() - -public fun m3saAnalyze( - outputFolderPath: String, - m3saSetupPath: String, -) { - val process = - ProcessBuilder( - SCRIPT_LANGUAGE, - ABSOLUTE_SCRIPT_PATH, - outputFolderPath, - m3saSetupPath, - ).directory(Path(ANALYSIS_SCRIPTS_DIRECTORY).toFile()) - .start() - - val exitCode = process.waitFor() - if (exitCode == 0) { - println("[M3SA says] M3SA operation(s) completed successfully.") - } else { - val errors = process.errorStream.bufferedReader().readText() - println("[M3SA says] Exit code $exitCode; Error(s): $errors") - } -} diff --git a/opendc-experiments/opendc-experiments-m3sa/src/main/kotlin/org/opendc/experiments/m3sa/runner/M3SACli.kt b/opendc-experiments/opendc-experiments-m3sa/src/main/kotlin/org/opendc/experiments/m3sa/runner/M3SACli.kt index 4fe58d88..51919722 100644 --- a/opendc-experiments/opendc-experiments-m3sa/src/main/kotlin/org/opendc/experiments/m3sa/runner/M3SACli.kt +++ b/opendc-experiments/opendc-experiments-m3sa/src/main/kotlin/org/opendc/experiments/m3sa/runner/M3SACli.kt @@ -30,9 +30,8 @@ import com.github.ajalt.clikt.parameters.options.defaultLazy import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.types.file import com.github.ajalt.clikt.parameters.types.int +import m3saAnalyze import org.opendc.experiments.base.experiment.getExperiment -import org.opendc.experiments.base.runner.runExperiment -import org.opendc.experiments.m3sa.m3saAnalyze import org.opendc.experiments.m3sa.scenario.getOutputFolder import java.io.File @@ -52,35 +51,62 @@ internal class M3SACommand : CliktCommand(name = "experiment") { .file(canBeDir = false, canBeFile = true) .defaultLazy { File("resources/experiment.json") } - /** - * 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) - private val m3saPath by option("-m", "--m3sa-setup-path", help = "path to m3sa setup file") .file(canBeDir = false, canBeFile = true) - .defaultLazy { File("") } + + private val m3saExec by option("-e", "--m3sa-exec-path", help = "path to m3sa executable") + .file(canBeDir = true, canBeFile = false) + .defaultLazy { File("opendc-experiments/opendc-experiments-m3sa/src/main/python/") } + + private val inputIterations by option("-i", "--iterations", help = "number of iterations to run") + .int() + .default(1) override fun run() { - println("The provided m3saPath is $m3saPath") - - val experiment = getExperiment(scenarioPath) - runExperiment(experiment, parallelism) - - if (m3saPath.toString().isNotEmpty()) { - m3saAnalyze( - outputFolderPath = getOutputFolder(scenarioPath), - m3saSetupPath = m3saPath.toString(), - ) - } else { - println( - "\n" + - "===================================================\n" + - "|M3SA path is not provided. Skipping M3SA analysis.|\n" + - "===================================================", - ) + val file = File("analysis.txt") + if (!file.exists()) { + file.createNewFile() } + + var iterations = inputIterations + var currentIteration = 1 + + while (iterations > 0) { + val startTime = System.currentTimeMillis() + val experiment = getExperiment(scenarioPath) + org.opendc.experiments.base.runner.runExperiment(experiment) + val simulationEnd = System.currentTimeMillis() + println("Simulation time: ${(simulationEnd - startTime) / 1000} ms") + + if (m3saPath != null) { + m3saAnalyze( + outputFolderPath = getOutputFolder(scenarioPath), + m3saSetupPath = m3saPath.toString(), + m3saExecPath = m3saExec.toString(), + ) + } else { + println( + "\n" + + "===================================================\n" + + "|M3SA path is not provided. Skipping M3SA analysis.|\n" + + "===================================================", + ) + } + + val endTime = System.currentTimeMillis() + println("OpenDC time: ${(simulationEnd - startTime) / 1000.0} s") + println("M3SA time: ${(endTime - simulationEnd) / 1000.0} s") + println("Total operation time: ${(endTime - startTime) / 1000.0} s") + + file.appendText("$currentIteration. OpenDC time: ${(simulationEnd - startTime) / 1000.0} s\n") + file.appendText("$currentIteration. M3SA time: ${(endTime - simulationEnd) / 1000.0} s\n") + file.appendText("$currentIteration. Total operation time: ${(endTime - startTime) / 1000.0} s\n\n") + + iterations -= 1 + currentIteration += 1 + } + + file.appendText("===================================================\n") + println("Finished $scenarioPath") } } diff --git a/opendc-experiments/opendc-experiments-m3sa/src/main/kotlin/org/opendc/experiments/m3sa/runner/M3SARunner.kt b/opendc-experiments/opendc-experiments-m3sa/src/main/kotlin/org/opendc/experiments/m3sa/runner/M3SARunner.kt index 49bbdb96..9bc7045f 100644 --- a/opendc-experiments/opendc-experiments-m3sa/src/main/kotlin/org/opendc/experiments/m3sa/runner/M3SARunner.kt +++ b/opendc-experiments/opendc-experiments-m3sa/src/main/kotlin/org/opendc/experiments/m3sa/runner/M3SARunner.kt @@ -25,7 +25,10 @@ package org.opendc.experiments.m3sa.runner import org.opendc.experiments.base.experiment.Scenario +import org.opendc.experiments.base.runner.runScenario import org.opendc.experiments.base.runner.setupOutputFolderStructure +import java.io.File +import java.util.Optional /** * Run scenario when no pool is available for parallel execution @@ -35,9 +38,35 @@ import org.opendc.experiments.base.runner.setupOutputFolderStructure */ public fun runExperiment( experiment: List, - parallelism: Int, + extraSimDataPath: Optional, ) { + val ansiReset = "\u001B[0m" + val ansiGreen = "\u001B[32m" + val ansiBlue = "\u001B[34m" + setupOutputFolderStructure(experiment[0].outputFolder) - runExperiment(experiment, parallelism) + var latestScenarioId = experiment.map { it.id }.maxOrNull() ?: 0 + + for (scenario in experiment) { + println( + "\n\n$ansiGreen================================================================================$ansiReset", + ) + println("$ansiBlue Running scenario: ${scenario.name} $ansiReset") + println("$ansiGreen================================================================================$ansiReset") + runScenario( + scenario, + ) + } + + if (extraSimDataPath.isEmpty) return + + for (directory in File(extraSimDataPath.get()).listFiles()!!) { + if (!directory.isDirectory) continue + latestScenarioId += 1 + + val copyPath = "${experiment[0].outputFolder}/raw-output/$latestScenarioId" + File(copyPath).mkdirs() + directory.copyRecursively(File(copyPath), true) + } } -- cgit v1.2.3