summaryrefslogtreecommitdiff
path: root/opendc-experiments/opendc-experiments-greenifier
diff options
context:
space:
mode:
Diffstat (limited to 'opendc-experiments/opendc-experiments-greenifier')
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/build.gradle.kts90
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/jmh/kotlin/org/opendc/experiments/capelin/GreenifierBenchmarks.kt93
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/jmh/resources/log4j2.xml37
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/jmh/resources/topology.txt5
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/Python_scripts/.ipynb_checkpoints/OpenDCdemo-checkpoint.ipynb957
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/Python_scripts/OpenDCdemo.ipynb834
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierCli.kt154
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierRunner.kt102
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/model/OperationalPhenomena.kt31
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/model/Scenario.kt40
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/model/Topology.kt28
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/model/Workload.kt33
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/portfolio/GreenifierPortfolio.kt58
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/portfolio/Portfolio.kt35
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/topology/ClusterSpec.kt46
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/topology/ClusterSpecReader.kt121
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/topology/TopologyFactories.kt99
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/resources/bitbrains-small/interference-model.json21
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/resources/bitbrains-small/trace/meta.parquetbin0 -> 2723 bytes
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/resources/bitbrains-small/trace/trace.parquetbin0 -> 2163354 bytes
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/multi.txt5
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/single.txt3
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/main/resources/log4j2.xml43
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/capelin/GreenifierIntegrationTest.kt287
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/capelin/GreenifierRunnerTest.kt86
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/test/resources/env/single.txt3
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/test/resources/env/topology.txt5
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/test/resources/trace/bitbrains-small/interference-model.json21
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/test/resources/trace/bitbrains-small/meta.parquetbin0 -> 2723 bytes
-rw-r--r--opendc-experiments/opendc-experiments-greenifier/src/test/resources/trace/bitbrains-small/trace.parquetbin0 -> 2163354 bytes
30 files changed, 3237 insertions, 0 deletions
diff --git a/opendc-experiments/opendc-experiments-greenifier/build.gradle.kts b/opendc-experiments/opendc-experiments-greenifier/build.gradle.kts
new file mode 100644
index 00000000..d75abe49
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/build.gradle.kts
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+description = "Experiments for the Greenifier work"
+
+/* Build configuration */
+plugins {
+ `kotlin-conventions`
+ `testing-conventions`
+ `jacoco-conventions`
+ `benchmark-conventions`
+ distribution
+}
+
+dependencies {
+ api(projects.opendcExperiments.opendcExperimentsCompute)
+
+ implementation(projects.opendcSimulator.opendcSimulatorCore)
+ implementation(projects.opendcSimulator.opendcSimulatorCompute)
+ implementation(projects.opendcCompute.opendcComputeSimulator)
+
+ implementation(libs.clikt)
+ implementation(libs.progressbar)
+ implementation(libs.kotlin.logging)
+ implementation(libs.jackson.dataformat.csv)
+
+ runtimeOnly(projects.opendcTrace.opendcTraceOpendc)
+ runtimeOnly(libs.log4j.core)
+ runtimeOnly(libs.log4j.slf4j)
+}
+
+val createGreenifierApp by tasks.creating(CreateStartScripts::class) {
+ dependsOn(tasks.jar)
+
+ applicationName = "greenifier"
+ mainClass.set("org.opendc.experiments.greenifier.GreenifierCli")
+ classpath = tasks.jar.get().outputs.files + configurations["runtimeClasspath"]
+ outputDir = project.buildDir.resolve("scripts")
+}
+
+/* Create custom Greenifier distribution */
+distributions {
+ main {
+ distributionBaseName.set("greenifier")
+
+ contents {
+ from("README.md")
+ from("LICENSE.txt")
+ from("../../LICENSE.txt") {
+ rename { "LICENSE-OpenDC.txt" }
+ }
+
+ into("bin") {
+ from(createGreenifierApp)
+ }
+
+ into("lib") {
+ from(tasks.jar)
+ from(configurations["runtimeClasspath"])
+ }
+
+ into("resources") {
+ from("src/main/resources")
+ }
+
+ into("Python_scripts") {
+ from("src/main/Python_scripts")
+ }
+ }
+ }
+}
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/jmh/kotlin/org/opendc/experiments/capelin/GreenifierBenchmarks.kt b/opendc-experiments/opendc-experiments-greenifier/src/jmh/kotlin/org/opendc/experiments/capelin/GreenifierBenchmarks.kt
new file mode 100644
index 00000000..7f06f41a
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/jmh/kotlin/org/opendc/experiments/capelin/GreenifierBenchmarks.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.greenifier
+
+import org.opendc.compute.service.ComputeService
+import org.opendc.compute.service.scheduler.FilterScheduler
+import org.opendc.compute.service.scheduler.filters.ComputeFilter
+import org.opendc.compute.service.scheduler.filters.RamFilter
+import org.opendc.compute.service.scheduler.filters.VCpuFilter
+import org.opendc.compute.service.scheduler.weights.CoreRamWeigher
+import org.opendc.experiments.greenifier.topology.clusterTopology
+import org.opendc.experiments.compute.ComputeWorkloadLoader
+import org.opendc.experiments.compute.VirtualMachine
+import org.opendc.experiments.compute.replay
+import org.opendc.experiments.compute.setupComputeService
+import org.opendc.experiments.compute.setupHosts
+import org.opendc.experiments.compute.topology.HostSpec
+import org.opendc.experiments.compute.trace
+import org.opendc.experiments.provisioner.Provisioner
+import org.opendc.simulator.kotlin.runSimulation
+import org.openjdk.jmh.annotations.Benchmark
+import org.openjdk.jmh.annotations.Fork
+import org.openjdk.jmh.annotations.Measurement
+import org.openjdk.jmh.annotations.Param
+import org.openjdk.jmh.annotations.Scope
+import org.openjdk.jmh.annotations.Setup
+import org.openjdk.jmh.annotations.State
+import org.openjdk.jmh.annotations.Warmup
+import java.io.File
+import java.util.Random
+import java.util.concurrent.TimeUnit
+
+/**
+ * Benchmark suite for the Greenifier experiments.
+ */
+@State(Scope.Thread)
+@Fork(1)
+@Warmup(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
+class GreenifierBenchmarks {
+ private lateinit var vms: List<VirtualMachine>
+ private lateinit var topology: List<HostSpec>
+
+ @Param("true", "false")
+ private var isOptimized: Boolean = false
+
+ @Setup
+ fun setUp() {
+ val loader = ComputeWorkloadLoader(File("src/test/resources/trace"))
+ vms = trace("bitbrains-small").resolve(loader, Random(1L))
+ topology = checkNotNull(object {}.javaClass.getResourceAsStream("/topology.txt")).use { clusterTopology(it) }
+ }
+
+ @Benchmark
+ fun benchmarkGreenifier() = runSimulation {
+ val serviceDomain = "compute.opendc.org"
+
+ Provisioner(dispatcher, seed = 0).use { provisioner ->
+ val computeScheduler = FilterScheduler(
+ filters = listOf(ComputeFilter(), VCpuFilter(16.0), RamFilter(1.0)),
+ weighers = listOf(CoreRamWeigher(multiplier = 1.0))
+ )
+
+ provisioner.runSteps(
+ setupComputeService(serviceDomain, { computeScheduler }),
+ setupHosts(serviceDomain, topology, optimize = isOptimized)
+ )
+
+ val service = provisioner.registry.resolve(serviceDomain, ComputeService::class.java)!!
+ service.replay(timeSource, vms, 0L, interference = true)
+ }
+ }
+}
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/jmh/resources/log4j2.xml b/opendc-experiments/opendc-experiments-greenifier/src/jmh/resources/log4j2.xml
new file mode 100644
index 00000000..c496dd75
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/jmh/resources/log4j2.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ MIT License
+ ~
+ ~ Copyright (c) 2020 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.
+ -->
+
+<Configuration status="WARN">
+ <Appenders>
+ <Console name="Console" target="SYSTEM_OUT">
+ <PatternLayout pattern="%d{HH:mm:ss.SSS} [%highlight{%-5level}] %logger{36} - %msg%n" disableAnsi="false"/>
+ </Console>
+ </Appenders>
+ <Loggers>
+ <Root level="warn">
+ <AppenderRef ref="Console"/>
+ </Root>
+ </Loggers>
+</Configuration>
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/jmh/resources/topology.txt b/opendc-experiments/opendc-experiments-greenifier/src/jmh/resources/topology.txt
new file mode 100644
index 00000000..6b347bff
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/jmh/resources/topology.txt
@@ -0,0 +1,5 @@
+ClusterID;ClusterName;Cores;Speed;Memory;numberOfHosts;memoryCapacityPerHost;coreCountPerHost
+A01;A01;32;3.2;2048;1;256;32
+B01;B01;48;2.93;1256;6;64;8
+C01;C01;32;3.2;2048;2;128;16
+
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/Python_scripts/.ipynb_checkpoints/OpenDCdemo-checkpoint.ipynb b/opendc-experiments/opendc-experiments-greenifier/src/main/Python_scripts/.ipynb_checkpoints/OpenDCdemo-checkpoint.ipynb
new file mode 100644
index 00000000..15fc32f6
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/main/Python_scripts/.ipynb_checkpoints/OpenDCdemo-checkpoint.ipynb
@@ -0,0 +1,957 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "18170001",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "['build.gradle.kts',\n",
+ " '.github',\n",
+ " 'opendc-web',\n",
+ " 'gradle.properties',\n",
+ " 'gradlew.bat',\n",
+ " 'docker-compose.yml',\n",
+ " '.gitignore',\n",
+ " 'resources',\n",
+ " '.dockerignore',\n",
+ " 'opendc-simulator',\n",
+ " '.gitattributes',\n",
+ " 'traces',\n",
+ " 'codecov.yml',\n",
+ " 'opendc-experiments',\n",
+ " '.editorconfig',\n",
+ " 'gradlew',\n",
+ " '.gradle',\n",
+ " 'site',\n",
+ " 'opendc-compute',\n",
+ " 'opendc-workflow',\n",
+ " 'output',\n",
+ " 'CONTRIBUTING.md',\n",
+ " 'opendc-trace',\n",
+ " 'LICENSE.txt',\n",
+ " 'docker-compose.prod.yml',\n",
+ " 'CITATION.cff',\n",
+ " '.git',\n",
+ " 'buildSrc',\n",
+ " 'build',\n",
+ " 'README.md',\n",
+ " 'opendc-common',\n",
+ " 'opendc-faas',\n",
+ " 'docker-compose.override.yml',\n",
+ " '.idea',\n",
+ " 'gradle',\n",
+ " 'settings.gradle.kts']"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "import matplotlib.pyplot as plt\n",
+ "\n",
+ "from IPython.display import display, HTML\n",
+ "\n",
+ "base_folder = \"../../../..\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "422f4d05",
+ "metadata": {},
+ "source": [
+ "## Topologies"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "a2d05361",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Topology name: multi\n"
+ ]
+ },
+ {
+ "ename": "FileNotFoundError",
+ "evalue": "[Errno 2] No such file or directory: '../resources/env/multi.txt'",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mFileNotFoundError\u001b[0m Traceback (most recent call last)",
+ "Cell \u001b[0;32mIn[2], line 6\u001b[0m\n\u001b[1;32m 3\u001b[0m df \u001b[38;5;241m=\u001b[39m pd\u001b[38;5;241m.\u001b[39mread_csv(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m../resources/env/\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mtopology_name\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m.txt\u001b[39m\u001b[38;5;124m\"\u001b[39m, delimiter\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m;\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 4\u001b[0m display(HTML(df\u001b[38;5;241m.\u001b[39mto_html()))\n\u001b[0;32m----> 6\u001b[0m \u001b[43mread_topology\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmulti\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 7\u001b[0m read_topology(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124msingle\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
+ "Cell \u001b[0;32mIn[2], line 3\u001b[0m, in \u001b[0;36mread_topology\u001b[0;34m(topology_name)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mread_topology\u001b[39m(topology_name):\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mTopology name: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mtopology_name\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m----> 3\u001b[0m df \u001b[38;5;241m=\u001b[39m \u001b[43mpd\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread_csv\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43mf\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m../resources/env/\u001b[39;49m\u001b[38;5;132;43;01m{\u001b[39;49;00m\u001b[43mtopology_name\u001b[49m\u001b[38;5;132;43;01m}\u001b[39;49;00m\u001b[38;5;124;43m.txt\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdelimiter\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m;\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 4\u001b[0m display(HTML(df\u001b[38;5;241m.\u001b[39mto_html()))\n",
+ "File \u001b[0;32m~/.local/lib/python3.10/site-packages/pandas/util/_decorators.py:211\u001b[0m, in \u001b[0;36mdeprecate_kwarg.<locals>._deprecate_kwarg.<locals>.wrapper\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 209\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 210\u001b[0m kwargs[new_arg_name] \u001b[38;5;241m=\u001b[39m new_arg_value\n\u001b[0;32m--> 211\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n",
+ "File \u001b[0;32m~/.local/lib/python3.10/site-packages/pandas/util/_decorators.py:317\u001b[0m, in \u001b[0;36mdeprecate_nonkeyword_arguments.<locals>.decorate.<locals>.wrapper\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 311\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(args) \u001b[38;5;241m>\u001b[39m num_allow_args:\n\u001b[1;32m 312\u001b[0m warnings\u001b[38;5;241m.\u001b[39mwarn(\n\u001b[1;32m 313\u001b[0m msg\u001b[38;5;241m.\u001b[39mformat(arguments\u001b[38;5;241m=\u001b[39marguments),\n\u001b[1;32m 314\u001b[0m \u001b[38;5;167;01mFutureWarning\u001b[39;00m,\n\u001b[1;32m 315\u001b[0m stacklevel\u001b[38;5;241m=\u001b[39mfind_stack_level(inspect\u001b[38;5;241m.\u001b[39mcurrentframe()),\n\u001b[1;32m 316\u001b[0m )\n\u001b[0;32m--> 317\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n",
+ "File \u001b[0;32m~/.local/lib/python3.10/site-packages/pandas/io/parsers/readers.py:950\u001b[0m, in \u001b[0;36mread_csv\u001b[0;34m(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, squeeze, prefix, mangle_dupe_cols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, verbose, skip_blank_lines, parse_dates, infer_datetime_format, keep_date_col, date_parser, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, encoding_errors, dialect, error_bad_lines, warn_bad_lines, on_bad_lines, delim_whitespace, low_memory, memory_map, float_precision, storage_options)\u001b[0m\n\u001b[1;32m 935\u001b[0m kwds_defaults \u001b[38;5;241m=\u001b[39m _refine_defaults_read(\n\u001b[1;32m 936\u001b[0m dialect,\n\u001b[1;32m 937\u001b[0m delimiter,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 946\u001b[0m defaults\u001b[38;5;241m=\u001b[39m{\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mdelimiter\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m,\u001b[39m\u001b[38;5;124m\"\u001b[39m},\n\u001b[1;32m 947\u001b[0m )\n\u001b[1;32m 948\u001b[0m kwds\u001b[38;5;241m.\u001b[39mupdate(kwds_defaults)\n\u001b[0;32m--> 950\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m_read\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilepath_or_buffer\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwds\u001b[49m\u001b[43m)\u001b[49m\n",
+ "File \u001b[0;32m~/.local/lib/python3.10/site-packages/pandas/io/parsers/readers.py:605\u001b[0m, in \u001b[0;36m_read\u001b[0;34m(filepath_or_buffer, kwds)\u001b[0m\n\u001b[1;32m 602\u001b[0m _validate_names(kwds\u001b[38;5;241m.\u001b[39mget(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mnames\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;28;01mNone\u001b[39;00m))\n\u001b[1;32m 604\u001b[0m \u001b[38;5;66;03m# Create the parser.\u001b[39;00m\n\u001b[0;32m--> 605\u001b[0m parser \u001b[38;5;241m=\u001b[39m \u001b[43mTextFileReader\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilepath_or_buffer\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwds\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 607\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m chunksize \u001b[38;5;129;01mor\u001b[39;00m iterator:\n\u001b[1;32m 608\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m parser\n",
+ "File \u001b[0;32m~/.local/lib/python3.10/site-packages/pandas/io/parsers/readers.py:1442\u001b[0m, in \u001b[0;36mTextFileReader.__init__\u001b[0;34m(self, f, engine, **kwds)\u001b[0m\n\u001b[1;32m 1439\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39moptions[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mhas_index_names\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m kwds[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mhas_index_names\u001b[39m\u001b[38;5;124m\"\u001b[39m]\n\u001b[1;32m 1441\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mhandles: IOHandles \u001b[38;5;241m|\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[0;32m-> 1442\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_engine \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_make_engine\u001b[49m\u001b[43m(\u001b[49m\u001b[43mf\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mengine\u001b[49m\u001b[43m)\u001b[49m\n",
+ "File \u001b[0;32m~/.local/lib/python3.10/site-packages/pandas/io/parsers/readers.py:1729\u001b[0m, in \u001b[0;36mTextFileReader._make_engine\u001b[0;34m(self, f, engine)\u001b[0m\n\u001b[1;32m 1727\u001b[0m is_text \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mFalse\u001b[39;00m\n\u001b[1;32m 1728\u001b[0m mode \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrb\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m-> 1729\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mhandles \u001b[38;5;241m=\u001b[39m \u001b[43mget_handle\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1730\u001b[0m \u001b[43m \u001b[49m\u001b[43mf\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1731\u001b[0m \u001b[43m \u001b[49m\u001b[43mmode\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1732\u001b[0m \u001b[43m \u001b[49m\u001b[43mencoding\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43moptions\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mencoding\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1733\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompression\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43moptions\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mcompression\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1734\u001b[0m \u001b[43m \u001b[49m\u001b[43mmemory_map\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43moptions\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmemory_map\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1735\u001b[0m \u001b[43m \u001b[49m\u001b[43mis_text\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mis_text\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1736\u001b[0m \u001b[43m \u001b[49m\u001b[43merrors\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43moptions\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mencoding_errors\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mstrict\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1737\u001b[0m \u001b[43m \u001b[49m\u001b[43mstorage_options\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43moptions\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mstorage_options\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1738\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1739\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mhandles \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 1740\u001b[0m f \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mhandles\u001b[38;5;241m.\u001b[39mhandle\n",
+ "File \u001b[0;32m~/.local/lib/python3.10/site-packages/pandas/io/common.py:857\u001b[0m, in \u001b[0;36mget_handle\u001b[0;34m(path_or_buf, mode, encoding, compression, memory_map, is_text, errors, storage_options)\u001b[0m\n\u001b[1;32m 852\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(handle, \u001b[38;5;28mstr\u001b[39m):\n\u001b[1;32m 853\u001b[0m \u001b[38;5;66;03m# Check whether the filename is to be opened in binary mode.\u001b[39;00m\n\u001b[1;32m 854\u001b[0m \u001b[38;5;66;03m# Binary mode does not support 'encoding' and 'newline'.\u001b[39;00m\n\u001b[1;32m 855\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m ioargs\u001b[38;5;241m.\u001b[39mencoding \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mb\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m ioargs\u001b[38;5;241m.\u001b[39mmode:\n\u001b[1;32m 856\u001b[0m \u001b[38;5;66;03m# Encoding\u001b[39;00m\n\u001b[0;32m--> 857\u001b[0m handle \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mopen\u001b[39;49m\u001b[43m(\u001b[49m\n\u001b[1;32m 858\u001b[0m \u001b[43m \u001b[49m\u001b[43mhandle\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 859\u001b[0m \u001b[43m \u001b[49m\u001b[43mioargs\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmode\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 860\u001b[0m \u001b[43m \u001b[49m\u001b[43mencoding\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mioargs\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mencoding\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 861\u001b[0m \u001b[43m \u001b[49m\u001b[43merrors\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43merrors\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 862\u001b[0m \u001b[43m \u001b[49m\u001b[43mnewline\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 863\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 864\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 865\u001b[0m \u001b[38;5;66;03m# Binary mode\u001b[39;00m\n\u001b[1;32m 866\u001b[0m handle \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mopen\u001b[39m(handle, ioargs\u001b[38;5;241m.\u001b[39mmode)\n",
+ "\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: '../resources/env/multi.txt'"
+ ]
+ }
+ ],
+ "source": [
+ "def read_topology(topology_name):\n",
+ " print(f\"Topology name: {topology_name}\")\n",
+ " df = pd.read_csv(f\"../resources/env/{topology_name}.txt\", delimiter=\";\")\n",
+ " display(HTML(df.to_html()))\n",
+ " \n",
+ "read_topology(\"multi\")\n",
+ "read_topology(\"single\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "8f4fe54d",
+ "metadata": {},
+ "source": [
+ "## Traces"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "fd17d88a",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "<div>\n",
+ "<style scoped>\n",
+ " .dataframe tbody tr th:only-of-type {\n",
+ " vertical-align: middle;\n",
+ " }\n",
+ "\n",
+ " .dataframe tbody tr th {\n",
+ " vertical-align: top;\n",
+ " }\n",
+ "\n",
+ " .dataframe thead th {\n",
+ " text-align: right;\n",
+ " }\n",
+ "</style>\n",
+ "<table border=\"1\" class=\"dataframe\">\n",
+ " <thead>\n",
+ " <tr style=\"text-align: right;\">\n",
+ " <th></th>\n",
+ " <th>id</th>\n",
+ " <th>timestamp</th>\n",
+ " <th>duration</th>\n",
+ " <th>cpu_count</th>\n",
+ " <th>cpu_usage</th>\n",
+ " </tr>\n",
+ " </thead>\n",
+ " <tbody>\n",
+ " <tr>\n",
+ " <th>0</th>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>1</td>\n",
+ " <td>0.000000</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>1</th>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 13:45:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>1</td>\n",
+ " <td>11.703998</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>2</th>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 13:55:46+00:00</td>\n",
+ " <td>600000</td>\n",
+ " <td>1</td>\n",
+ " <td>0.000000</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>3</th>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 14:00:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>1</td>\n",
+ " <td>11.703998</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>4</th>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 14:15:46+00:00</td>\n",
+ " <td>900000</td>\n",
+ " <td>1</td>\n",
+ " <td>0.000000</td>\n",
+ " </tr>\n",
+ " </tbody>\n",
+ "</table>\n",
+ "</div>"
+ ],
+ "text/plain": [
+ " id timestamp duration cpu_count cpu_usage\n",
+ "0 1019 2013-08-12 13:40:46+00:00 300000 1 0.000000\n",
+ "1 1019 2013-08-12 13:45:46+00:00 300000 1 11.703998\n",
+ "2 1019 2013-08-12 13:55:46+00:00 600000 1 0.000000\n",
+ "3 1019 2013-08-12 14:00:46+00:00 300000 1 11.703998\n",
+ "4 1019 2013-08-12 14:15:46+00:00 900000 1 0.000000"
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "trace = pd.read_parquet(f\"resources/bitbrains-small/trace/trace.parquet\")\n",
+ "trace.head(5)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "346f097f",
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "<div>\n",
+ "<style scoped>\n",
+ " .dataframe tbody tr th:only-of-type {\n",
+ " vertical-align: middle;\n",
+ " }\n",
+ "\n",
+ " .dataframe tbody tr th {\n",
+ " vertical-align: top;\n",
+ " }\n",
+ "\n",
+ " .dataframe thead th {\n",
+ " text-align: right;\n",
+ " }\n",
+ "</style>\n",
+ "<table border=\"1\" class=\"dataframe\">\n",
+ " <thead>\n",
+ " <tr style=\"text-align: right;\">\n",
+ " <th></th>\n",
+ " <th>id</th>\n",
+ " <th>start_time</th>\n",
+ " <th>stop_time</th>\n",
+ " <th>cpu_count</th>\n",
+ " <th>cpu_capacity</th>\n",
+ " <th>mem_capacity</th>\n",
+ " </tr>\n",
+ " </thead>\n",
+ " <tbody>\n",
+ " <tr>\n",
+ " <th>0</th>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 13:35:46+00:00</td>\n",
+ " <td>2013-09-11 13:39:58+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>2926.000135</td>\n",
+ " <td>181352</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>1</th>\n",
+ " <td>1023</td>\n",
+ " <td>2013-08-12 13:35:46+00:00</td>\n",
+ " <td>2013-09-11 13:39:58+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>2925.999560</td>\n",
+ " <td>260096</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>2</th>\n",
+ " <td>1026</td>\n",
+ " <td>2013-08-12 13:35:46+00:00</td>\n",
+ " <td>2013-09-11 13:39:58+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>2925.999717</td>\n",
+ " <td>249972</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>3</th>\n",
+ " <td>1052</td>\n",
+ " <td>2013-08-29 14:38:12+00:00</td>\n",
+ " <td>2013-09-05 07:09:07+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>2926.000107</td>\n",
+ " <td>131245</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>4</th>\n",
+ " <td>1073</td>\n",
+ " <td>2013-08-21 11:07:12+00:00</td>\n",
+ " <td>2013-09-11 13:39:58+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>2599.999649</td>\n",
+ " <td>179306</td>\n",
+ " </tr>\n",
+ " </tbody>\n",
+ "</table>\n",
+ "</div>"
+ ],
+ "text/plain": [
+ " id start_time stop_time cpu_count \\\n",
+ "0 1019 2013-08-12 13:35:46+00:00 2013-09-11 13:39:58+00:00 1 \n",
+ "1 1023 2013-08-12 13:35:46+00:00 2013-09-11 13:39:58+00:00 1 \n",
+ "2 1026 2013-08-12 13:35:46+00:00 2013-09-11 13:39:58+00:00 1 \n",
+ "3 1052 2013-08-29 14:38:12+00:00 2013-09-05 07:09:07+00:00 1 \n",
+ "4 1073 2013-08-21 11:07:12+00:00 2013-09-11 13:39:58+00:00 1 \n",
+ "\n",
+ " cpu_capacity mem_capacity \n",
+ "0 2926.000135 181352 \n",
+ "1 2925.999560 260096 \n",
+ "2 2925.999717 249972 \n",
+ "3 2926.000107 131245 \n",
+ "4 2599.999649 179306 "
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "meta = pd.read_parquet(f\"resources/bitbrains-small/trace/meta.parquet\")\n",
+ "meta.head(5)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "13bf9fdb",
+ "metadata": {},
+ "source": [
+ "# Lets run this in OpenDC!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c9766446",
+ "metadata": {},
+ "source": [
+ "## Resulting Files"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "id": "0d400ffd",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "output_folder = \"output\"\n",
+ "workload = \"workload=bitbrains-small\"\n",
+ "seed = \"seed=0\"\n",
+ "\n",
+ "df_host_multi = pd.read_parquet(f\"{output_folder}/host/topology=multi/{workload}/{seed}/data.parquet\")\n",
+ "df_host_single = pd.read_parquet(f\"{output_folder}/host/topology=single/{workload}/{seed}/data.parquet\")\n",
+ "\n",
+ "df_server_multi = pd.read_parquet(f\"{output_folder}/server/topology=multi/{workload}/{seed}/data.parquet\")\n",
+ "df_server_single = pd.read_parquet(f\"{output_folder}/server/topology=single/{workload}/{seed}/data.parquet\")\n",
+ "\n",
+ "df_service_multi = pd.read_parquet(f\"{output_folder}/service/topology=multi/{workload}/{seed}/data.parquet\")\n",
+ "df_service_single = pd.read_parquet(f\"{output_folder}/service/topology=single/{workload}/{seed}/data.parquet\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "6d494d6e",
+ "metadata": {},
+ "source": [
+ "### Host"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "id": "48a1e1a6",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "['timestamp',\n",
+ " 'host_id',\n",
+ " 'cpu_count',\n",
+ " 'mem_capacity',\n",
+ " 'guests_terminated',\n",
+ " 'guests_running',\n",
+ " 'guests_error',\n",
+ " 'guests_invalid',\n",
+ " 'cpu_limit',\n",
+ " 'cpu_usage',\n",
+ " 'cpu_demand',\n",
+ " 'cpu_utilization',\n",
+ " 'cpu_time_active',\n",
+ " 'cpu_time_idle',\n",
+ " 'cpu_time_steal',\n",
+ " 'cpu_time_lost',\n",
+ " 'power_total',\n",
+ " 'uptime',\n",
+ " 'downtime',\n",
+ " 'boot_time']"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "number of measurements: 77778\n"
+ ]
+ }
+ ],
+ "source": [
+ "display(list(df_host_multi.columns))\n",
+ "print(f\"number of measurements: {len(df_host_multi)}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9eb9be2c",
+ "metadata": {},
+ "source": [
+ "### Server"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "id": "57a2b148",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "['timestamp',\n",
+ " 'server_id',\n",
+ " 'host_id',\n",
+ " 'uptime',\n",
+ " 'downtime',\n",
+ " 'provision_time',\n",
+ " 'boot_time',\n",
+ " 'cpu_count',\n",
+ " 'cpu_limit',\n",
+ " 'cpu_time_active',\n",
+ " 'cpu_time_idle',\n",
+ " 'cpu_time_steal',\n",
+ " 'cpu_time_lost',\n",
+ " 'mem_limit']"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "number of measurements: 408040\n"
+ ]
+ }
+ ],
+ "source": [
+ "display(list(df_server_multi.columns))\n",
+ "print(f\"number of measurements: {len(df_server_multi)}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "fbe5f439",
+ "metadata": {},
+ "source": [
+ "### Service"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "id": "9ef468ed",
+ "metadata": {
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "['timestamp',\n",
+ " 'hosts_up',\n",
+ " 'hosts_down',\n",
+ " 'servers_pending',\n",
+ " 'servers_active',\n",
+ " 'attempts_success',\n",
+ " 'attempts_failure',\n",
+ " 'attempts_error']"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "number of measurements: 8642\n"
+ ]
+ }
+ ],
+ "source": [
+ "display(list(df_service_single.columns))\n",
+ "print(f\"number of measurements: {len(df_host_single)}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "09d31c91",
+ "metadata": {},
+ "source": [
+ "## Power Usage"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "id": "82f0a24a",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "big topology: 3400876.9739568997\n",
+ "small topology: 60000.000858306885\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(f\"big topology: {df_host_multi.power_total.sum()}\")\n",
+ "print(f\"small topology: {df_host_single.power_total.sum()}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7ab3357d",
+ "metadata": {},
+ "source": [
+ "## CPU usage"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "id": "e94db3a6",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "big topology: 2.352397194967523e-05\n",
+ "small topology: 0.00011571395510298542\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(f\"big topology: {df_host_multi.cpu_utilization.mean()}\")\n",
+ "print(f\"small topology: {df_host_single.cpu_utilization.mean()}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "e000a260",
+ "metadata": {},
+ "source": [
+ "## CPU utilization"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 29,
+ "id": "8d7daa45",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "big topology: 2.352397194967523e-05\n",
+ "small topology: 0.00011571395510298542\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(f\"big topology: {df_host_multi.cpu_utilization.mean()}\")\n",
+ "print(f\"small topology: {df_host_single.cpu_utilization.mean()}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ad97741c",
+ "metadata": {},
+ "source": [
+ "## Plotting Results"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "id": "5df8f9aa",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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OnjwoL3N39/fHmwkKTw83H4KDIBrWLkBIElq27atLBaLDh06VKn+Tz/9tEaOHKl69eopNDRUFovF/llgYKC+++67cmN++eUXeXt7V3jaS5K8vC79/9Zvg9ZvT+G4U1ZWlu6991498sgjGj16tL29oKBAsbGxio2N1TvvvKPGjRsrIyNDsbGx1XLazcfHx+G9xWJxOmgCcMTKDQBJl+70iY2NVVJSkgoKCsp9/vvbnoODg9WmTRuFhYU5BBtJat++vb755hsVFhY6tO/fv18tW7Ys94V+WePGjSXJ4YLa3z+bxmq1qrS0tLKHVaGLFy9q0KBB6tChgxYtWuTw2aFDh/Tjjz9q7ty56tu3rzp06FBuJcVqtUrSVevo2LGjSkpKtHv3bnvbjz/+qMOHD6tTp05Vqh/A1RFuANglJSWptLRUPXv21IcffqijR4/q4MGDWrJkiaKjoyu9nWHDhslisSguLk779u3TsWPHtGLFCi1evFhPPvnkFce1adNGNptNzz33nI4ePaqNGzdq4cKFDn0iIiJ0/vx5JScn69y5c7pw4YLTx/noo4/q9OnTWrJkiXJycpSVlaWsrCwVFRWpefPmslqtevXVV3XixAlt2LCh3LNrWrRoIYvFoo8//lg5OTk6f/58uX20bdtWgwYN0pgxY7Rjxw599dVXeuihh9S0aVMNGjTI6ZoBVB7hBoBdq1attH//fvXr109PPvmkOnfurP79+ys5OVmvv/56pbdTv359ff755youLta9996ryMhILVmyRIsWLdKjjz56xXE+Pj567733dOjQId10002aN2+eXnjhBYc+vXr10mOPPaYhQ4aocePGmj9/vtPH+b//+7/KzMxUp06dFB4ebn/t2rVLjRs31qpVq7R27Vp16tRJc+fO1UsvveQwvmnTppo1a5amTJmi0NBQTZgwocL9rFy5Ut26ddO//du/KTo6WoZhaNOmTVdcuQLgHhaDk7sAAMBEWLkBAACmQrgBAACmQrgBAACmQrgBAACmQrgBAACmQrgBAACmQrgBAACmQrgBAACmQrgBAACmQrgBAACmQrgBAACm8v8BjCGIOvW4gUwAAAAASUVORK5CYII=",
+ "text/plain": [
+ "<Figure size 640x480 with 1 Axes>"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "data = df_host_multi.cpu_utilization\n",
+ "plt.hist(data, weights=np.ones_like(data) / len(data),\n",
+ " alpha=0.7, label=\"big\", bins=30)\n",
+ "\n",
+ "\n",
+ "data = df_host_single.cpu_utilization\n",
+ "plt.hist(data, weights=np.ones_like(data) / len(data),\n",
+ " alpha=0.7, label=\"small\", bins=30)\n",
+ "\n",
+ "plt.xlabel(\"CPU utilization\")\n",
+ "plt.ylabel(\"Frequency\")\n",
+ "plt.legend()\n",
+ "plt.show()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "id": "520e42a4",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "0.0 8641\n",
+ "1.0 1\n",
+ "Name: cpu_utilization, dtype: int64"
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_host_single.cpu_utilization.value_counts()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "id": "a8c35267",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "array([b'\\xf8\\x8b\\xb8\\xa8rL\\x81\\xec\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02',\n",
+ " b'\\x1b9\\x89jQ\\xa8t\\x9b\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03',\n",
+ " b'\\xc5\\x84\\x13:\\xc9\\x16\\xab<\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00',\n",
+ " b'S\\xcb\\x9f\\x0ct~\\xa2\\xea\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x04',\n",
+ " b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00',\n",
+ " b'\\x06\\xc4]\\x18\\x80\\tEO\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01',\n",
+ " b',\\x82\\x9a\\xbe\\x1fE2\\xe1\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05',\n",
+ " b'>\\xe5x\\x90A\\xc9\\x8a\\xc3\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01',\n",
+ " b'nx\\x9ej\\xa1\\xb9e\\xf4\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'],\n",
+ " dtype=object)"
+ ]
+ },
+ "execution_count": 22,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_host_multi.host_id.unique()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "id": "68546b09",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "<div>\n",
+ "<style scoped>\n",
+ " .dataframe tbody tr th:only-of-type {\n",
+ " vertical-align: middle;\n",
+ " }\n",
+ "\n",
+ " .dataframe tbody tr th {\n",
+ " vertical-align: top;\n",
+ " }\n",
+ "\n",
+ " .dataframe thead th {\n",
+ " text-align: right;\n",
+ " }\n",
+ "</style>\n",
+ "<table border=\"1\" class=\"dataframe\">\n",
+ " <thead>\n",
+ " <tr style=\"text-align: right;\">\n",
+ " <th></th>\n",
+ " <th>timestamp</th>\n",
+ " <th>hosts_up</th>\n",
+ " <th>hosts_down</th>\n",
+ " <th>servers_pending</th>\n",
+ " <th>servers_active</th>\n",
+ " <th>attempts_success</th>\n",
+ " <th>attempts_failure</th>\n",
+ " <th>attempts_error</th>\n",
+ " </tr>\n",
+ " </thead>\n",
+ " <tbody>\n",
+ " <tr>\n",
+ " <th>0</th>\n",
+ " <td>1970-01-01 00:05:00+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>0</td>\n",
+ " <td>44</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>1</th>\n",
+ " <td>1970-01-01 00:10:00+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>0</td>\n",
+ " <td>29</td>\n",
+ " <td>15</td>\n",
+ " <td>15</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>2</th>\n",
+ " <td>1970-01-01 00:15:00+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>0</td>\n",
+ " <td>29</td>\n",
+ " <td>15</td>\n",
+ " <td>15</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>3</th>\n",
+ " <td>1970-01-01 00:20:00+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>0</td>\n",
+ " <td>29</td>\n",
+ " <td>15</td>\n",
+ " <td>15</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>4</th>\n",
+ " <td>1970-01-01 00:25:00+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>0</td>\n",
+ " <td>29</td>\n",
+ " <td>15</td>\n",
+ " <td>15</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>...</th>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " <td>...</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>8637</th>\n",
+ " <td>1970-01-30 23:50:00+00:00</td>\n",
+ " <td>0</td>\n",
+ " <td>1</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " <td>49</td>\n",
+ " <td>1</td>\n",
+ " <td>0</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>8638</th>\n",
+ " <td>1970-01-30 23:55:00+00:00</td>\n",
+ " <td>0</td>\n",
+ " <td>1</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " <td>49</td>\n",
+ " <td>1</td>\n",
+ " <td>0</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>8639</th>\n",
+ " <td>1970-01-31 00:00:00+00:00</td>\n",
+ " <td>0</td>\n",
+ " <td>1</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " <td>49</td>\n",
+ " <td>1</td>\n",
+ " <td>0</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>8640</th>\n",
+ " <td>1970-01-31 00:05:00+00:00</td>\n",
+ " <td>0</td>\n",
+ " <td>1</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " <td>49</td>\n",
+ " <td>1</td>\n",
+ " <td>0</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>8641</th>\n",
+ " <td>1970-01-31 00:10:00+00:00</td>\n",
+ " <td>0</td>\n",
+ " <td>1</td>\n",
+ " <td>0</td>\n",
+ " <td>0</td>\n",
+ " <td>49</td>\n",
+ " <td>1</td>\n",
+ " <td>0</td>\n",
+ " </tr>\n",
+ " </tbody>\n",
+ "</table>\n",
+ "<p>8642 rows × 8 columns</p>\n",
+ "</div>"
+ ],
+ "text/plain": [
+ " timestamp hosts_up hosts_down servers_pending \\\n",
+ "0 1970-01-01 00:05:00+00:00 1 0 44 \n",
+ "1 1970-01-01 00:10:00+00:00 1 0 29 \n",
+ "2 1970-01-01 00:15:00+00:00 1 0 29 \n",
+ "3 1970-01-01 00:20:00+00:00 1 0 29 \n",
+ "4 1970-01-01 00:25:00+00:00 1 0 29 \n",
+ "... ... ... ... ... \n",
+ "8637 1970-01-30 23:50:00+00:00 0 1 0 \n",
+ "8638 1970-01-30 23:55:00+00:00 0 1 0 \n",
+ "8639 1970-01-31 00:00:00+00:00 0 1 0 \n",
+ "8640 1970-01-31 00:05:00+00:00 0 1 0 \n",
+ "8641 1970-01-31 00:10:00+00:00 0 1 0 \n",
+ "\n",
+ " servers_active attempts_success attempts_failure attempts_error \n",
+ "0 0 0 0 0 \n",
+ "1 15 15 0 0 \n",
+ "2 15 15 0 0 \n",
+ "3 15 15 0 0 \n",
+ "4 15 15 0 0 \n",
+ "... ... ... ... ... \n",
+ "8637 0 49 1 0 \n",
+ "8638 0 49 1 0 \n",
+ "8639 0 49 1 0 \n",
+ "8640 0 49 1 0 \n",
+ "8641 0 49 1 0 \n",
+ "\n",
+ "[8642 rows x 8 columns]"
+ ]
+ },
+ "execution_count": 30,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_service_single"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "id": "5b9ccf81",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "0 6189\n",
+ "1 1828\n",
+ "2 612\n",
+ "44 13\n",
+ "Name: servers_active, dtype: int64"
+ ]
+ },
+ "execution_count": 33,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_service_multi.servers_active.value_counts()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "9e9bd9b9",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.12"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/Python_scripts/OpenDCdemo.ipynb b/opendc-experiments/opendc-experiments-greenifier/src/main/Python_scripts/OpenDCdemo.ipynb
new file mode 100644
index 00000000..3a88aca7
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/main/Python_scripts/OpenDCdemo.ipynb
@@ -0,0 +1,834 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "18170001",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "import matplotlib.pyplot as plt\n",
+ "\n",
+ "from IPython.display import display, HTML\n",
+ "\n",
+ "base_folder = \"../../../..\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "422f4d05",
+ "metadata": {},
+ "source": [
+ "## Topologies"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "a2d05361",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Topology name: multi\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "<table border=\"1\" class=\"dataframe\">\n",
+ " <thead>\n",
+ " <tr style=\"text-align: right;\">\n",
+ " <th></th>\n",
+ " <th>ClusterID</th>\n",
+ " <th>ClusterName</th>\n",
+ " <th>Cores</th>\n",
+ " <th>Speed</th>\n",
+ " <th>Memory</th>\n",
+ " <th>numberOfHosts</th>\n",
+ " <th>memoryCapacityPerHost</th>\n",
+ " <th>coreCountPerHost</th>\n",
+ " </tr>\n",
+ " </thead>\n",
+ " <tbody>\n",
+ " <tr>\n",
+ " <th>0</th>\n",
+ " <td>A01</td>\n",
+ " <td>A01</td>\n",
+ " <td>32</td>\n",
+ " <td>3.20</td>\n",
+ " <td>2048</td>\n",
+ " <td>1</td>\n",
+ " <td>256</td>\n",
+ " <td>32</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>1</th>\n",
+ " <td>B01</td>\n",
+ " <td>B01</td>\n",
+ " <td>48</td>\n",
+ " <td>2.93</td>\n",
+ " <td>1256</td>\n",
+ " <td>6</td>\n",
+ " <td>64</td>\n",
+ " <td>8</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>2</th>\n",
+ " <td>C01</td>\n",
+ " <td>C01</td>\n",
+ " <td>32</td>\n",
+ " <td>3.20</td>\n",
+ " <td>2048</td>\n",
+ " <td>2</td>\n",
+ " <td>128</td>\n",
+ " <td>16</td>\n",
+ " </tr>\n",
+ " </tbody>\n",
+ "</table>"
+ ],
+ "text/plain": [
+ "<IPython.core.display.HTML object>"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Topology name: single\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "<table border=\"1\" class=\"dataframe\">\n",
+ " <thead>\n",
+ " <tr style=\"text-align: right;\">\n",
+ " <th></th>\n",
+ " <th>ClusterID</th>\n",
+ " <th>ClusterName</th>\n",
+ " <th>Cores</th>\n",
+ " <th>Speed</th>\n",
+ " <th>Memory</th>\n",
+ " <th>numberOfHosts</th>\n",
+ " <th>memoryCapacityPerHost</th>\n",
+ " <th>coreCountPerHost</th>\n",
+ " </tr>\n",
+ " </thead>\n",
+ " <tbody>\n",
+ " <tr>\n",
+ " <th>0</th>\n",
+ " <td>A01</td>\n",
+ " <td>A01</td>\n",
+ " <td>8</td>\n",
+ " <td>3.2</td>\n",
+ " <td>128</td>\n",
+ " <td>1</td>\n",
+ " <td>128</td>\n",
+ " <td>8</td>\n",
+ " </tr>\n",
+ " </tbody>\n",
+ "</table>"
+ ],
+ "text/plain": [
+ "<IPython.core.display.HTML object>"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "def read_topology(topology_name):\n",
+ " print(f\"Topology name: {topology_name}\")\n",
+ " df = pd.read_csv(f\"{base_folder}/resources/env/{topology_name}.txt\", delimiter=\";\")\n",
+ " display(HTML(df.to_html()))\n",
+ " \n",
+ "read_topology(\"multi\")\n",
+ "read_topology(\"single\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "8f4fe54d",
+ "metadata": {},
+ "source": [
+ "## Traces"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "fd17d88a",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "<div>\n",
+ "<style scoped>\n",
+ " .dataframe tbody tr th:only-of-type {\n",
+ " vertical-align: middle;\n",
+ " }\n",
+ "\n",
+ " .dataframe tbody tr th {\n",
+ " vertical-align: top;\n",
+ " }\n",
+ "\n",
+ " .dataframe thead th {\n",
+ " text-align: right;\n",
+ " }\n",
+ "</style>\n",
+ "<table border=\"1\" class=\"dataframe\">\n",
+ " <thead>\n",
+ " <tr style=\"text-align: right;\">\n",
+ " <th></th>\n",
+ " <th>id</th>\n",
+ " <th>timestamp</th>\n",
+ " <th>duration</th>\n",
+ " <th>cpu_count</th>\n",
+ " <th>cpu_usage</th>\n",
+ " </tr>\n",
+ " </thead>\n",
+ " <tbody>\n",
+ " <tr>\n",
+ " <th>0</th>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 13:40:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>1</td>\n",
+ " <td>0.000000</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>1</th>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 13:45:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>1</td>\n",
+ " <td>11.703998</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>2</th>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 13:55:46+00:00</td>\n",
+ " <td>600000</td>\n",
+ " <td>1</td>\n",
+ " <td>0.000000</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>3</th>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 14:00:46+00:00</td>\n",
+ " <td>300000</td>\n",
+ " <td>1</td>\n",
+ " <td>11.703998</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>4</th>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 14:15:46+00:00</td>\n",
+ " <td>900000</td>\n",
+ " <td>1</td>\n",
+ " <td>0.000000</td>\n",
+ " </tr>\n",
+ " </tbody>\n",
+ "</table>\n",
+ "</div>"
+ ],
+ "text/plain": [
+ " id timestamp duration cpu_count cpu_usage\n",
+ "0 1019 2013-08-12 13:40:46+00:00 300000 1 0.000000\n",
+ "1 1019 2013-08-12 13:45:46+00:00 300000 1 11.703998\n",
+ "2 1019 2013-08-12 13:55:46+00:00 600000 1 0.000000\n",
+ "3 1019 2013-08-12 14:00:46+00:00 300000 1 11.703998\n",
+ "4 1019 2013-08-12 14:15:46+00:00 900000 1 0.000000"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_trace = pd.read_parquet(f\"{base_folder}/resources/bitbrains-small/trace/trace.parquet\")\n",
+ "df_trace.head()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "346f097f",
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "<div>\n",
+ "<style scoped>\n",
+ " .dataframe tbody tr th:only-of-type {\n",
+ " vertical-align: middle;\n",
+ " }\n",
+ "\n",
+ " .dataframe tbody tr th {\n",
+ " vertical-align: top;\n",
+ " }\n",
+ "\n",
+ " .dataframe thead th {\n",
+ " text-align: right;\n",
+ " }\n",
+ "</style>\n",
+ "<table border=\"1\" class=\"dataframe\">\n",
+ " <thead>\n",
+ " <tr style=\"text-align: right;\">\n",
+ " <th></th>\n",
+ " <th>id</th>\n",
+ " <th>start_time</th>\n",
+ " <th>stop_time</th>\n",
+ " <th>cpu_count</th>\n",
+ " <th>cpu_capacity</th>\n",
+ " <th>mem_capacity</th>\n",
+ " </tr>\n",
+ " </thead>\n",
+ " <tbody>\n",
+ " <tr>\n",
+ " <th>0</th>\n",
+ " <td>1019</td>\n",
+ " <td>2013-08-12 13:35:46+00:00</td>\n",
+ " <td>2013-09-11 13:39:58+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>2926.000135</td>\n",
+ " <td>181352</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>1</th>\n",
+ " <td>1023</td>\n",
+ " <td>2013-08-12 13:35:46+00:00</td>\n",
+ " <td>2013-09-11 13:39:58+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>2925.999560</td>\n",
+ " <td>260096</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>2</th>\n",
+ " <td>1026</td>\n",
+ " <td>2013-08-12 13:35:46+00:00</td>\n",
+ " <td>2013-09-11 13:39:58+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>2925.999717</td>\n",
+ " <td>249972</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>3</th>\n",
+ " <td>1052</td>\n",
+ " <td>2013-08-29 14:38:12+00:00</td>\n",
+ " <td>2013-09-05 07:09:07+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>2926.000107</td>\n",
+ " <td>131245</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>4</th>\n",
+ " <td>1073</td>\n",
+ " <td>2013-08-21 11:07:12+00:00</td>\n",
+ " <td>2013-09-11 13:39:58+00:00</td>\n",
+ " <td>1</td>\n",
+ " <td>2599.999649</td>\n",
+ " <td>179306</td>\n",
+ " </tr>\n",
+ " </tbody>\n",
+ "</table>\n",
+ "</div>"
+ ],
+ "text/plain": [
+ " id start_time stop_time cpu_count \\\n",
+ "0 1019 2013-08-12 13:35:46+00:00 2013-09-11 13:39:58+00:00 1 \n",
+ "1 1023 2013-08-12 13:35:46+00:00 2013-09-11 13:39:58+00:00 1 \n",
+ "2 1026 2013-08-12 13:35:46+00:00 2013-09-11 13:39:58+00:00 1 \n",
+ "3 1052 2013-08-29 14:38:12+00:00 2013-09-05 07:09:07+00:00 1 \n",
+ "4 1073 2013-08-21 11:07:12+00:00 2013-09-11 13:39:58+00:00 1 \n",
+ "\n",
+ " cpu_capacity mem_capacity \n",
+ "0 2926.000135 181352 \n",
+ "1 2925.999560 260096 \n",
+ "2 2925.999717 249972 \n",
+ "3 2926.000107 131245 \n",
+ "4 2599.999649 179306 "
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_meta = pd.read_parquet(f\"{base_folder}/resources/bitbrains-small/trace/meta.parquet\")\n",
+ "df_meta.head()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "13bf9fdb",
+ "metadata": {},
+ "source": [
+ "# Lets run this in OpenDC!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c9766446",
+ "metadata": {},
+ "source": [
+ "## Resulting Files"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 43,
+ "id": "0d400ffd",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "output_folder = f\"{base_folder}/output\"\n",
+ "workload = \"workload=bitbrains-small\"\n",
+ "seed = \"seed=0\"\n",
+ "\n",
+ "df_host_single = pd.read_parquet(f\"{output_folder}/host/topology=single/{workload}/{seed}/data.parquet\")\n",
+ "df_host_multi = pd.read_parquet(f\"{output_folder}/host/topology=multi/{workload}/{seed}/data.parquet\")\n",
+ "\n",
+ "df_server_single = pd.read_parquet(f\"{output_folder}/server/topology=single/{workload}/{seed}/data.parquet\")\n",
+ "df_server_multi = pd.read_parquet(f\"{output_folder}/server/topology=multi/{workload}/{seed}/data.parquet\")\n",
+ "\n",
+ "df_service_single = pd.read_parquet(f\"{output_folder}/service/topology=single/{workload}/{seed}/data.parquet\")\n",
+ "df_service_multi = pd.read_parquet(f\"{output_folder}/service/topology=multi/{workload}/{seed}/data.parquet\")\n",
+ "\n",
+ "def add_absolute_timestamp(df, start_dt):\n",
+ " df[\"absolute_timestamp\"] = start_dt + (df[\"timestamp\"] - df[\"timestamp\"].min())\n",
+ "\n",
+ "add_absolute_timestamp(df_host_single, df_meta[\"start_time\"].min())\n",
+ "add_absolute_timestamp(df_host_single, df_meta[\"start_time\"].min())\n",
+ "\n",
+ "add_absolute_timestamp(df_server_single, df_meta[\"start_time\"].min())\n",
+ "add_absolute_timestamp(df_server_multi, df_meta[\"start_time\"].min())\n",
+ "\n",
+ "add_absolute_timestamp(df_service_single, df_meta[\"start_time\"].min())\n",
+ "add_absolute_timestamp(df_service_multi, df_meta[\"start_time\"].min())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "6d494d6e",
+ "metadata": {},
+ "source": [
+ "### Host"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 44,
+ "id": "48a1e1a6",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "['timestamp',\n",
+ " 'host_id',\n",
+ " 'cpu_count',\n",
+ " 'mem_capacity',\n",
+ " 'guests_terminated',\n",
+ " 'guests_running',\n",
+ " 'guests_error',\n",
+ " 'guests_invalid',\n",
+ " 'cpu_limit',\n",
+ " 'cpu_usage',\n",
+ " 'cpu_demand',\n",
+ " 'cpu_utilization',\n",
+ " 'cpu_time_active',\n",
+ " 'cpu_time_idle',\n",
+ " 'cpu_time_steal',\n",
+ " 'cpu_time_lost',\n",
+ " 'power_total',\n",
+ " 'uptime',\n",
+ " 'downtime',\n",
+ " 'boot_time']"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "number of measurements: 77859\n"
+ ]
+ }
+ ],
+ "source": [
+ "display(list(df_host_multi.columns))\n",
+ "print(f\"number of measurements: {len(df_host_multi)}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9eb9be2c",
+ "metadata": {},
+ "source": [
+ "### Server"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 45,
+ "id": "57a2b148",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "['timestamp',\n",
+ " 'server_id',\n",
+ " 'host_id',\n",
+ " 'mem_capacity',\n",
+ " 'cpu_count',\n",
+ " 'cpu_limit',\n",
+ " 'cpu_time_active',\n",
+ " 'cpu_time_idle',\n",
+ " 'cpu_time_steal',\n",
+ " 'cpu_time_lost',\n",
+ " 'uptime',\n",
+ " 'downtime',\n",
+ " 'provision_time',\n",
+ " 'boot_time',\n",
+ " 'absolute_timestamp']"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "number of measurements: 408490\n"
+ ]
+ }
+ ],
+ "source": [
+ "display(list(df_server_multi.columns))\n",
+ "print(f\"number of measurements: {len(df_server_multi)}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "fbe5f439",
+ "metadata": {},
+ "source": [
+ "### Service"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 46,
+ "id": "9ef468ed",
+ "metadata": {
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "['timestamp',\n",
+ " 'hosts_up',\n",
+ " 'hosts_down',\n",
+ " 'servers_pending',\n",
+ " 'servers_active',\n",
+ " 'attempts_success',\n",
+ " 'attempts_failure',\n",
+ " 'attempts_error',\n",
+ " 'absolute_timestamp']"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "number of measurements: 8651\n"
+ ]
+ }
+ ],
+ "source": [
+ "display(list(df_service_single.columns))\n",
+ "print(f\"number of measurements: {len(df_host_single)}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "09d31c91",
+ "metadata": {},
+ "source": [
+ "## Power Usage"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 47,
+ "id": "82f0a24a",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "single topology: 822692246.2425151\n",
+ "multi topology: 5870271518.168591\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(f\"single topology: {df_host_single.power_total.sum()}\")\n",
+ "print(f\"multi topology: {df_host_multi.power_total.sum()}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7ab3357d",
+ "metadata": {},
+ "source": [
+ "## CPU usage"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 48,
+ "id": "e94db3a6",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "single topology: 0.7799672554077309\n",
+ "multi topology: 0.3421434368579651\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(f\"single topology: {df_host_single.cpu_utilization.mean()}\")\n",
+ "print(f\"multi topology: {df_host_multi.cpu_utilization.mean()}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "e000a260",
+ "metadata": {},
+ "source": [
+ "## CPU utilization"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 49,
+ "id": "8d7daa45",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "multi topology: 0.3421434368579651\n",
+ "single topology: 0.7799672554077309\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(f\"multi topology: {df_host_multi.cpu_utilization.mean()}\")\n",
+ "print(f\"single topology: {df_host_single.cpu_utilization.mean()}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ad97741c",
+ "metadata": {},
+ "source": [
+ "## Plotting Results"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "id": "5df8f9aa",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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CuEGAACYCuEGAACYCuEGAACYCuEGAACYCuEGAACYSpcIN/n5+YqJiZG/v78SExO1bdu2c/ZdtWqVLBZLs83f39+D1QIAgK7M6+FmzZo1stvtys7O1o4dOzRq1CilpKTo2LFj59wnKChIFRUVzu3w4cMerBgAAHRlXg83ubm5yszMVEZGhkaMGKGCggIFBASosLDwnPtYLBZFREQ4t/DwcA9WDAAAujKvhpv6+npt375dycnJzjYfHx8lJyertLT0nPudOnVKl1xyiaKjozVp0iR98MEH5+xbV1enmpqaZhsAADAvr4ab48ePq7GxscXMS3h4uBwOR6v7DBs2TIWFhfrLX/6iP/7xj2pqatKYMWP0ySeftNo/JydHwcHBzi06Otrt1wEAALoOr9+Waq+kpCSlpaUpNjZW11xzjdatW6d+/frpt7/9bav9Z82aperqaud25MgRD1cMAAA8qYc3Tx4aGiqr1arKyspm7ZWVlYqIiGjTMXx9ffWtb31L+/fvb/V9Pz8/+fn5dbhWAADQPXh15sZmsykuLk7FxcXOtqamJhUXFyspKalNx2hsbNTu3bsVGRnZWWUCAIBuxKszN5Jkt9uVnp6u+Ph4JSQkKC8vT7W1tcrIyJAkpaWlKSoqSjk5OZKkefPm6dvf/rYGDx6sEydOaPHixTp8+LDuuusub14GAADoIrweblJTU1VVVaW5c+fK4XAoNjZWRUVFzkXG5eXl8vH5coLps88+U2ZmphwOh3r37q24uDht3bpVI0aM8NYlAACALsRiGIbh7SI8qaamRsHBwaqurlZQUJC3ywEAoGt6IdX1faescV8d/689n9/d7ttSAAAA50O4AQAApkK4AQAApkK4AQAApkK4AQAApkK4AQAApkK4AQAApkK4AQAApkK4AQAApkK4AQAApkK4AQAApkK4AQAApkK4AQAApkK4AQAApkK4AQAApkK4AQAApkK4AQAApkK4AQAAptLD2wWYzbRV77i874qpo91YCQAAFyZmbgAAgKkQbgAAgKkQbgAAgKkQbgAAgKm4FG4OHjzo7joAAADcwqVwM3jwYF133XX64x//qM8//9zdNQEAALjMpXCzY8cOXXnllbLb7YqIiNA999yjbdu2ubs2AACAdnMp3MTGxurJJ5/U0aNHVVhYqIqKCl199dUaOXKkcnNzVVVV5e46AQAA2qRD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",
+ "text/plain": [
+ "<Figure size 640x480 with 1 Axes>"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "data = df_host_multi.cpu_utilization\n",
+ "plt.hist(data, weights=np.ones_like(data) / len(data),\n",
+ " alpha=0.7, label=\"big\", bins=30)\n",
+ "\n",
+ "\n",
+ "data = df_host_single.cpu_utilization\n",
+ "plt.hist(data, weights=np.ones_like(data) / len(data),\n",
+ " alpha=0.7, label=\"small\", bins=30)\n",
+ "\n",
+ "plt.xlabel(\"CPU utilization\")\n",
+ "plt.ylabel(\"Frequency\")\n",
+ "plt.legend()\n",
+ "plt.show()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "id": "520e42a4",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "1.000000 6146\n",
+ "0.000009 9\n",
+ "0.002294 2\n",
+ "0.027410 2\n",
+ "0.021973 2\n",
+ " ... \n",
+ "0.028164 1\n",
+ "0.029120 1\n",
+ "0.028367 1\n",
+ "0.030243 1\n",
+ "0.030289 1\n",
+ "Name: cpu_utilization, Length: 2488, dtype: int64"
+ ]
+ },
+ "execution_count": 34,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_host_single.cpu_utilization.value_counts()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 35,
+ "id": "a8c35267",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "array([b'\\xf8\\x8b\\xb8\\xa8rL\\x81\\xec\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02',\n",
+ " b'\\x1b9\\x89jQ\\xa8t\\x9b\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03',\n",
+ " b'\\xc5\\x84\\x13:\\xc9\\x16\\xab<\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00',\n",
+ " b'S\\xcb\\x9f\\x0ct~\\xa2\\xea\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x04',\n",
+ " b'\\xe2 \\xa89{\\x1d\\xcd\\xaf\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00',\n",
+ " b'\\x06\\xc4]\\x18\\x80\\tEO\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01',\n",
+ " b',\\x82\\x9a\\xbe\\x1fE2\\xe1\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05',\n",
+ " b'>\\xe5x\\x90A\\xc9\\x8a\\xc3\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01',\n",
+ " b'nx\\x9ej\\xa1\\xb9e\\xf4\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'],\n",
+ " dtype=object)"
+ ]
+ },
+ "execution_count": 35,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_host_multi.host_id.unique()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 54,
+ "id": "68546b09",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "1 141065\n",
+ "4 118263\n",
+ "8 77859\n",
+ "2 62652\n",
+ "32 8651\n",
+ "Name: cpu_count, dtype: int64"
+ ]
+ },
+ "execution_count": 54,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_server_single.cpu_count.value_counts()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 56,
+ "id": "326abd0c",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "8 8651\n",
+ "Name: cpu_count, dtype: int64"
+ ]
+ },
+ "execution_count": 56,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_host_single.cpu_count.value_counts()"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.12"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierCli.kt b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierCli.kt
new file mode 100644
index 00000000..1f5a40f1
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierCli.kt
@@ -0,0 +1,154 @@
+/*
+ * 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("GreenifierCli")
+
+package org.opendc.experiments.greenifier
+
+import com.github.ajalt.clikt.core.CliktCommand
+import com.github.ajalt.clikt.parameters.arguments.argument
+import com.github.ajalt.clikt.parameters.arguments.default
+import com.github.ajalt.clikt.parameters.options.associate
+import com.github.ajalt.clikt.parameters.options.default
+import com.github.ajalt.clikt.parameters.options.defaultLazy
+import com.github.ajalt.clikt.parameters.options.flag
+import com.github.ajalt.clikt.parameters.options.option
+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.greenifier.model.Scenario
+import org.opendc.experiments.greenifier.portfolio.GreenifierPortfolio
+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 = GreenifierCommand().main(args)
+
+/**
+ * Represents the command for the Greenifier experiments.
+ */
+internal class GreenifierCommand : CliktCommand(name = "greenifier") {
+ /**
+ * 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(
+ "greenifier" to { GreenifierPortfolio() },
+ )
+ .default({GreenifierPortfolio()})
+
+ /**
+ * 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 = GreenifierRunner(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: GreenifierRunner, 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-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierRunner.kt b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierRunner.kt
new file mode 100644
index 00000000..4811cb61
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/GreenifierRunner.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.greenifier
+
+import org.opendc.compute.service.ComputeService
+import org.opendc.experiments.greenifier.model.Scenario
+import org.opendc.experiments.greenifier.topology.clusterTopology
+import org.opendc.experiments.compute.ComputeWorkloadLoader
+import org.opendc.experiments.compute.createComputeScheduler
+import org.opendc.experiments.compute.export.parquet.ParquetComputeMonitor
+import org.opendc.experiments.compute.grid5000
+import org.opendc.experiments.compute.registerComputeMonitor
+import org.opendc.experiments.compute.replay
+import org.opendc.experiments.compute.setupComputeService
+import org.opendc.experiments.compute.setupHosts
+import org.opendc.experiments.provisioner.Provisioner
+import org.opendc.simulator.kotlin.runSimulation
+import java.io.File
+import java.time.Duration
+import java.util.Random
+import kotlin.math.roundToLong
+
+/**
+ * Helper class for running the Greenifier 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 GreenifierRunner(
+ 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) = runSimulation {
+ val serviceDomain = "compute.opendc.org"
+ val topology = clusterTopology(File(envPath, "${scenario.topology.name}.txt"))
+
+ Provisioner(dispatcher, seed).use { provisioner ->
+ provisioner.runSteps(
+ setupComputeService(serviceDomain, { createComputeScheduler(scenario.allocationPolicy, Random(it.seeder.nextLong())) }),
+ setupHosts(serviceDomain, topology, optimize = true)
+ )
+
+ if (outputPath != null) {
+ val partitions = scenario.partitions + ("seed" to seed.toString())
+ val partition = partitions.map { (k, v) -> "$k=$v" }.joinToString("/")
+
+ provisioner.runStep(
+ registerComputeMonitor(
+ serviceDomain,
+ ParquetComputeMonitor(
+ outputPath,
+ partition,
+ bufferSize = 4096
+ )
+ )
+ )
+ }
+
+ val service = provisioner.registry.resolve(serviceDomain, ComputeService::class.java)!!
+ val vms = scenario.workload.source.resolve(workloadLoader, Random(seed))
+ val operationalPhenomena = scenario.operationalPhenomena
+ val failureModel =
+ if (operationalPhenomena.failureFrequency > 0) {
+ grid5000(Duration.ofSeconds((operationalPhenomena.failureFrequency * 60).roundToLong()))
+ } else {
+ null
+ }
+
+ service.replay(timeSource, vms, seed, failureModel = failureModel, interference = operationalPhenomena.hasInterference)
+ }
+ }
+}
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/model/OperationalPhenomena.kt b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/model/OperationalPhenomena.kt
new file mode 100644
index 00000000..c5a8acc7
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/model/OperationalPhenomena.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.greenifier.model
+
+/**
+ * Operation phenomena during experiments.
+ *
+ * @param failureFrequency The average time between failures in hours.
+ * @param hasInterference A flag to enable performance interference between VMs.
+ */
+public data class OperationalPhenomena(val failureFrequency: Double, val hasInterference: Boolean)
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/model/Scenario.kt b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/model/Scenario.kt
new file mode 100644
index 00000000..4f31aeb8
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/model/Scenario.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.greenifier.model
+
+/**
+ * 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-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/model/Topology.kt b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/model/Topology.kt
new file mode 100644
index 00000000..aa2e5190
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/model/Topology.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.greenifier.model
+
+/**
+ * The topology on which we simulate the workload.
+ */
+public data class Topology(val name: String)
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/model/Workload.kt b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/model/Workload.kt
new file mode 100644
index 00000000..c222a28d
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/model/Workload.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.greenifier.model
+
+import org.opendc.experiments.compute.ComputeWorkload
+
+/**
+ * A single workload originating from a trace.
+ *
+ * @param name the name of the workload.
+ * @param source The source of the workload data.
+ */
+data class Workload(val name: String, val source: ComputeWorkload)
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/portfolio/GreenifierPortfolio.kt b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/portfolio/GreenifierPortfolio.kt
new file mode 100644
index 00000000..44fd359a
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/portfolio/GreenifierPortfolio.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.greenifier.portfolio
+
+import org.opendc.experiments.greenifier.model.OperationalPhenomena
+import org.opendc.experiments.greenifier.model.Scenario
+import org.opendc.experiments.greenifier.model.Topology
+import org.opendc.experiments.greenifier.model.Workload
+import org.opendc.experiments.compute.sampleByLoad
+import org.opendc.experiments.compute.trace
+
+/**
+ * A [Portfolio] that explores the difference between horizontal and vertical scaling.
+ */
+public class GreenifierPortfolio : Portfolio {
+ private val topologies = listOf(
+ Topology("single"),
+ Topology("multi"),
+ )
+
+ private val workloads = listOf(
+ Workload("bitbrains-small", trace("trace").sampleByLoad(1.0)),
+ )
+ private val operationalPhenomena = OperationalPhenomena(0.0, false)
+ private val allocationPolicy = "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-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/portfolio/Portfolio.kt b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/portfolio/Portfolio.kt
new file mode 100644
index 00000000..e2875c9c
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/portfolio/Portfolio.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.greenifier.portfolio
+
+import org.opendc.experiments.greenifier.model.Scenario
+
+/**
+ * A portfolio represents a collection of scenarios are tested for the work.
+ */
+public interface Portfolio {
+ /**
+ * The scenarios that belong to this portfolio.
+ */
+ val scenarios: Iterable<Scenario>
+}
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/topology/ClusterSpec.kt b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/topology/ClusterSpec.kt
new file mode 100644
index 00000000..905a9ac9
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/topology/ClusterSpec.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.greenifier.topology
+
+/**
+ * Definition of a compute cluster modeled in the simulation.
+ *
+ * @param id A unique identifier representing the compute cluster.
+ * @param name The name of the cluster.
+ * @param cpuCount The total number of CPUs in the cluster.
+ * @param cpuSpeed The speed of a CPU in the cluster in MHz.
+ * @param memCapacity The total memory capacity of the cluster (in MiB).
+ * @param hostCount The number of hosts in the cluster.
+ * @param memCapacityPerHost The memory capacity per host in the cluster (MiB).
+ * @param cpuCountPerHost The number of CPUs per host in the cluster.
+ */
+public data class ClusterSpec(
+ val id: String,
+ val name: String,
+ val cpuCount: Int,
+ val cpuSpeed: Double,
+ val memCapacity: Double,
+ val hostCount: Int,
+ val memCapacityPerHost: Double,
+ val cpuCountPerHost: Int
+)
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/topology/ClusterSpecReader.kt b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/topology/ClusterSpecReader.kt
new file mode 100644
index 00000000..2488a539
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/topology/ClusterSpecReader.kt
@@ -0,0 +1,121 @@
+/*
+ * 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.greenifier.topology
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.fasterxml.jackson.databind.MappingIterator
+import com.fasterxml.jackson.databind.ObjectReader
+import com.fasterxml.jackson.dataformat.csv.CsvMapper
+import com.fasterxml.jackson.dataformat.csv.CsvSchema
+import java.io.File
+import java.io.InputStream
+
+/**
+ * A helper class for reading a cluster specification file.
+ */
+class ClusterSpecReader {
+ /**
+ * The [CsvMapper] to map the environment file to an object.
+ */
+ private val mapper = CsvMapper()
+
+ /**
+ * The [ObjectReader] to convert the lines into objects.
+ */
+ private val reader: ObjectReader = mapper.readerFor(Entry::class.java).with(schema)
+
+ /**
+ * Read the specified [file].
+ */
+ fun read(file: File): List<ClusterSpec> {
+ return reader.readValues<Entry>(file).use { read(it) }
+ }
+
+ /**
+ * Read the specified [input].
+ */
+ fun read(input: InputStream): List<ClusterSpec> {
+ return reader.readValues<Entry>(input).use { read(it) }
+ }
+
+ /**
+ * Convert the specified [MappingIterator] into a list of [ClusterSpec]s.
+ */
+ private fun read(it: MappingIterator<Entry>): List<ClusterSpec> {
+ val result = mutableListOf<ClusterSpec>()
+
+ for (entry in it) {
+ val def = ClusterSpec(
+ entry.id,
+ entry.name,
+ entry.cpuCount,
+ entry.cpuSpeed * 1000, // Convert to MHz
+ entry.memCapacity * 1000, // Convert to MiB
+ entry.hostCount,
+ entry.memCapacityPerHost * 1000,
+ entry.cpuCountPerHost
+ )
+ result.add(def)
+ }
+
+ return result
+ }
+
+ private open class Entry(
+ @JsonProperty("ClusterID")
+ val id: String,
+ @JsonProperty("ClusterName")
+ val name: String,
+ @JsonProperty("Cores")
+ val cpuCount: Int,
+ @JsonProperty("Speed")
+ val cpuSpeed: Double,
+ @JsonProperty("Memory")
+ val memCapacity: Double,
+ @JsonProperty("numberOfHosts")
+ val hostCount: Int,
+ @JsonProperty("memoryCapacityPerHost")
+ val memCapacityPerHost: Double,
+ @JsonProperty("coreCountPerHost")
+ val cpuCountPerHost: Int
+ )
+
+ companion object {
+ /**
+ * The [CsvSchema] that is used to parse the trace.
+ */
+ private val schema = CsvSchema.builder()
+ .addColumn("ClusterID", CsvSchema.ColumnType.STRING)
+ .addColumn("ClusterName", CsvSchema.ColumnType.STRING)
+ .addColumn("Cores", CsvSchema.ColumnType.NUMBER)
+ .addColumn("Speed", CsvSchema.ColumnType.NUMBER)
+ .addColumn("Memory", CsvSchema.ColumnType.NUMBER)
+ .addColumn("numberOfHosts", CsvSchema.ColumnType.NUMBER)
+ .addColumn("memoryCapacityPerHost", CsvSchema.ColumnType.NUMBER)
+ .addColumn("coreCountPerHost", CsvSchema.ColumnType.NUMBER)
+ .setAllowComments(true)
+ .setColumnSeparator(';')
+ .setUseHeader(true)
+ .build()
+ }
+}
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/topology/TopologyFactories.kt b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/topology/TopologyFactories.kt
new file mode 100644
index 00000000..a23ae1a0
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/main/kotlin/org/opendc/experiments/greenifier/topology/TopologyFactories.kt
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+@file:JvmName("TopologyFactories")
+
+package org.opendc.experiments.greenifier.topology
+
+import org.opendc.experiments.compute.topology.HostSpec
+import org.opendc.simulator.compute.SimPsuFactories
+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.compute.power.CpuPowerModel
+import org.opendc.simulator.compute.power.CpuPowerModels
+import java.io.File
+import java.io.InputStream
+import java.util.SplittableRandom
+import java.util.UUID
+import java.util.random.RandomGenerator
+import kotlin.math.roundToLong
+
+/**
+ * A [ClusterSpecReader] that is used to read the cluster definition file.
+ */
+private val reader = ClusterSpecReader()
+
+/**
+ * Construct a topology from the specified [file].
+ */
+fun clusterTopology(
+ file: File,
+ powerModel: CpuPowerModel = CpuPowerModels.linear(350.0, 200.0),
+ random: RandomGenerator = SplittableRandom(0)
+): List<HostSpec> {
+ return clusterTopology(reader.read(file), powerModel, random)
+}
+
+/**
+ * Construct a topology from the specified [input].
+ */
+fun clusterTopology(
+ input: InputStream,
+ powerModel: CpuPowerModel = CpuPowerModels.linear(350.0, 200.0),
+ random: RandomGenerator = SplittableRandom(0)
+): List<HostSpec> {
+ return clusterTopology(reader.read(input), powerModel, random)
+}
+
+/**
+ * Construct a topology from the given list of [clusters].
+ */
+fun clusterTopology(clusters: List<ClusterSpec>, powerModel: CpuPowerModel, random: RandomGenerator = SplittableRandom(0)): List<HostSpec> {
+ return clusters.flatMap { it.toHostSpecs(random, powerModel) }
+}
+
+/**
+ * Helper method to convert a [ClusterSpec] into a list of [HostSpec]s.
+ */
+private fun ClusterSpec.toHostSpecs(random: RandomGenerator, powerModel: CpuPowerModel): List<HostSpec> {
+ val cpuSpeed = cpuSpeed
+ val memoryPerHost = memCapacityPerHost.roundToLong()
+
+ val unknownProcessingNode = ProcessingNode("unknown", "unknown", "unknown", cpuCountPerHost)
+ val unknownMemoryUnit = MemoryUnit("unknown", "unknown", -1.0, memoryPerHost)
+ val machineModel = MachineModel(
+ List(cpuCountPerHost) { coreId -> ProcessingUnit(unknownProcessingNode, coreId, cpuSpeed) },
+ listOf(unknownMemoryUnit)
+ )
+
+ return List(hostCount) {
+ HostSpec(
+ UUID(random.nextLong(), it.toLong()),
+ "node-$name-$it",
+ mapOf("cluster" to id),
+ machineModel,
+ SimPsuFactories.simple(powerModel)
+ )
+ }
+}
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/bitbrains-small/interference-model.json b/opendc-experiments/opendc-experiments-greenifier/src/main/resources/bitbrains-small/interference-model.json
new file mode 100644
index 00000000..51fc6366
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/main/resources/bitbrains-small/interference-model.json
@@ -0,0 +1,21 @@
+[
+ {
+ "vms": [
+ "141",
+ "379",
+ "851",
+ "116"
+ ],
+ "minServerLoad": 0.0,
+ "performanceScore": 0.8830158730158756
+ },
+ {
+ "vms": [
+ "205",
+ "116",
+ "463"
+ ],
+ "minServerLoad": 0.0,
+ "performanceScore": 0.7133055555552751
+ }
+]
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/bitbrains-small/trace/meta.parquet b/opendc-experiments/opendc-experiments-greenifier/src/main/resources/bitbrains-small/trace/meta.parquet
new file mode 100644
index 00000000..9cded35f
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/main/resources/bitbrains-small/trace/meta.parquet
Binary files differ
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/bitbrains-small/trace/trace.parquet b/opendc-experiments/opendc-experiments-greenifier/src/main/resources/bitbrains-small/trace/trace.parquet
new file mode 100644
index 00000000..9d953956
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/main/resources/bitbrains-small/trace/trace.parquet
Binary files differ
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/multi.txt b/opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/multi.txt
new file mode 100644
index 00000000..6b347bff
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/multi.txt
@@ -0,0 +1,5 @@
+ClusterID;ClusterName;Cores;Speed;Memory;numberOfHosts;memoryCapacityPerHost;coreCountPerHost
+A01;A01;32;3.2;2048;1;256;32
+B01;B01;48;2.93;1256;6;64;8
+C01;C01;32;3.2;2048;2;128;16
+
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/single.txt b/opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/single.txt
new file mode 100644
index 00000000..5642003d
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/main/resources/env/single.txt
@@ -0,0 +1,3 @@
+ClusterID;ClusterName;Cores;Speed;Memory;numberOfHosts;memoryCapacityPerHost;coreCountPerHost
+A01;A01;8;3.2;128;1;128;8
+
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/main/resources/log4j2.xml b/opendc-experiments/opendc-experiments-greenifier/src/main/resources/log4j2.xml
new file mode 100644
index 00000000..e479f2ca
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/main/resources/log4j2.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ MIT License
+ ~
+ ~ Copyright (c) 2020 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.
+ -->
+
+<Configuration status="WARN">
+ <Appenders>
+ <Console name="Console" target="SYSTEM_OUT">
+ <PatternLayout pattern="%d{HH:mm:ss.SSS} [%highlight{%-5level}] %logger{36} - %msg%n" disableAnsi="false"/>
+ </Console>
+ </Appenders>
+ <Loggers>
+ <Logger name="org.opendc" level="warn" additivity="false">
+ <AppenderRef ref="Console"/>
+ </Logger>
+ <Logger name="org.apache.hadoop" level="warn" additivity="false">
+ <AppenderRef ref="Console"/>
+ </Logger>
+ <Root level="error">
+ <AppenderRef ref="Console"/>
+ </Root>
+ </Loggers>
+</Configuration>
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/capelin/GreenifierIntegrationTest.kt b/opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/capelin/GreenifierIntegrationTest.kt
new file mode 100644
index 00000000..5431a061
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/capelin/GreenifierIntegrationTest.kt
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2020 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.greenifier
+
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.assertAll
+import org.opendc.compute.service.ComputeService
+import org.opendc.compute.service.scheduler.FilterScheduler
+import org.opendc.compute.service.scheduler.filters.ComputeFilter
+import org.opendc.compute.service.scheduler.filters.RamFilter
+import org.opendc.compute.service.scheduler.filters.VCpuFilter
+import org.opendc.compute.service.scheduler.weights.CoreRamWeigher
+import org.opendc.experiments.greenifier.topology.clusterTopology
+import org.opendc.experiments.compute.ComputeWorkloadLoader
+import org.opendc.experiments.compute.VirtualMachine
+import org.opendc.experiments.compute.grid5000
+import org.opendc.experiments.compute.registerComputeMonitor
+import org.opendc.experiments.compute.replay
+import org.opendc.experiments.compute.sampleByLoad
+import org.opendc.experiments.compute.setupComputeService
+import org.opendc.experiments.compute.setupHosts
+import org.opendc.experiments.compute.telemetry.ComputeMonitor
+import org.opendc.experiments.compute.telemetry.table.HostTableReader
+import org.opendc.experiments.compute.telemetry.table.ServiceTableReader
+import org.opendc.experiments.compute.topology.HostSpec
+import org.opendc.experiments.compute.trace
+import org.opendc.experiments.provisioner.Provisioner
+import org.opendc.simulator.kotlin.runSimulation
+import java.io.File
+import java.time.Duration
+import java.util.Random
+
+/**
+ * An integration test suite for the Greenifier experiments.
+ */
+class GreenifierIntegrationTest {
+ /**
+ * The monitor used to keep track of the metrics.
+ */
+ private lateinit var monitor: TestComputeMonitor
+
+ /**
+ * The [FilterScheduler] to use for all experiments.
+ */
+ private lateinit var computeScheduler: FilterScheduler
+
+ /**
+ * The [ComputeWorkloadLoader] responsible for loading the traces.
+ */
+ private lateinit var workloadLoader: ComputeWorkloadLoader
+
+ /**
+ * Set up the experimental environment.
+ */
+ @BeforeEach
+ fun setUp() {
+ monitor = TestComputeMonitor()
+ computeScheduler = FilterScheduler(
+ filters = listOf(ComputeFilter(), VCpuFilter(16.0), RamFilter(1.0)),
+ weighers = listOf(CoreRamWeigher(multiplier = 1.0))
+ )
+ workloadLoader = ComputeWorkloadLoader(File("src/test/resources/trace"))
+ }
+
+ /**
+ * Test a large simulation setup.
+ */
+ @Test
+ fun testLarge() = runSimulation {
+ val seed = 0L
+ val workload = createTestWorkload(1.0, seed)
+ val topology = createTopology()
+ val monitor = monitor
+
+ Provisioner(dispatcher, seed).use { provisioner ->
+ provisioner.runSteps(
+ setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }),
+ registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor),
+ setupHosts(serviceDomain = "compute.opendc.org", topology)
+ )
+
+ val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!!
+ service.replay(timeSource, workload, seed)
+ }
+
+ println(
+ "Scheduler " +
+ "Success=${monitor.attemptsSuccess} " +
+ "Failure=${monitor.attemptsFailure} " +
+ "Error=${monitor.attemptsError} " +
+ "Pending=${monitor.serversPending} " +
+ "Active=${monitor.serversActive}"
+ )
+
+ // Note that these values have been verified beforehand
+ assertAll(
+ { assertEquals(50, monitor.attemptsSuccess, "The scheduler should schedule 50 VMs") },
+ { assertEquals(0, monitor.serversActive, "All VMs should finish after a run") },
+ { assertEquals(0, monitor.attemptsFailure, "No VM should be unscheduled") },
+ { assertEquals(0, monitor.serversPending, "No VM should not be in the queue") },
+ { assertEquals(223394101, monitor.idleTime) { "Incorrect idle time" } },
+ { assertEquals(66977086, monitor.activeTime) { "Incorrect active time" } },
+ { assertEquals(3160276, monitor.stealTime) { "Incorrect steal time" } },
+ { assertEquals(0, monitor.lostTime) { "Incorrect lost time" } },
+ { assertEquals(5.84093E9, monitor.energyUsage, 1E4) { "Incorrect power draw" } }
+ )
+ }
+
+ /**
+ * Test a small simulation setup.
+ */
+ @Test
+ fun testSmall() = runSimulation {
+ val seed = 1L
+ val workload = createTestWorkload(0.25, seed)
+ val topology = createTopology("single")
+ val monitor = monitor
+
+ Provisioner(dispatcher, seed).use { provisioner ->
+ provisioner.runSteps(
+ setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }),
+ registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor),
+ setupHosts(serviceDomain = "compute.opendc.org", topology)
+ )
+
+ val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!!
+ service.replay(timeSource, workload, seed)
+ }
+
+ println(
+ "Scheduler " +
+ "Success=${monitor.attemptsSuccess} " +
+ "Failure=${monitor.attemptsFailure} " +
+ "Error=${monitor.attemptsError} " +
+ "Pending=${monitor.serversPending} " +
+ "Active=${monitor.serversActive}"
+ )
+
+ // Note that these values have been verified beforehand
+ assertAll(
+ { assertEquals(10999514, monitor.idleTime) { "Idle time incorrect" } },
+ { assertEquals(9741285, monitor.activeTime) { "Active time incorrect" } },
+ { assertEquals(0, monitor.stealTime) { "Steal time incorrect" } },
+ { assertEquals(0, monitor.lostTime) { "Lost time incorrect" } },
+ { assertEquals(7.0116E8, monitor.energyUsage, 1E4) { "Incorrect power draw" } }
+ )
+ }
+
+ /**
+ * Test a small simulation setup with interference.
+ */
+ @Test
+ fun testInterference() = runSimulation {
+ val seed = 0L
+ val workload = createTestWorkload(1.0, seed)
+ val topology = createTopology("single")
+
+ Provisioner(dispatcher, seed).use { provisioner ->
+ provisioner.runSteps(
+ setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }),
+ registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor),
+ setupHosts(serviceDomain = "compute.opendc.org", topology)
+ )
+
+ val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!!
+ service.replay(timeSource, workload, seed, interference = true)
+ }
+
+ println(
+ "Scheduler " +
+ "Success=${monitor.attemptsSuccess} " +
+ "Failure=${monitor.attemptsFailure} " +
+ "Error=${monitor.attemptsError} " +
+ "Pending=${monitor.serversPending} " +
+ "Active=${monitor.serversActive}"
+ )
+
+ // Note that these values have been verified beforehand
+ assertAll(
+ { assertEquals(6028018, monitor.idleTime) { "Idle time incorrect" } },
+ { assertEquals(14712781, monitor.activeTime) { "Active time incorrect" } },
+ { assertEquals(12532934, monitor.stealTime) { "Steal time incorrect" } },
+ { assertEquals(424267, monitor.lostTime) { "Lost time incorrect" } }
+ )
+ }
+
+ /**
+ * Test a small simulation setup with failures.
+ */
+ @Test
+ fun testFailures() = runSimulation {
+ val seed = 0L
+ val topology = createTopology("single")
+ val workload = createTestWorkload(0.25, seed)
+ val monitor = monitor
+
+ Provisioner(dispatcher, seed).use { provisioner ->
+ provisioner.runSteps(
+ setupComputeService(serviceDomain = "compute.opendc.org", { computeScheduler }),
+ registerComputeMonitor(serviceDomain = "compute.opendc.org", monitor),
+ setupHosts(serviceDomain = "compute.opendc.org", topology)
+ )
+
+ val service = provisioner.registry.resolve("compute.opendc.org", ComputeService::class.java)!!
+ service.replay(timeSource, workload, seed, failureModel = grid5000(Duration.ofDays(7)))
+ }
+
+ // Note that these values have been verified beforehand
+ assertAll(
+ { assertEquals(10085111, monitor.idleTime) { "Idle time incorrect" } },
+ { assertEquals(8539204, monitor.activeTime) { "Active time incorrect" } },
+ { assertEquals(0, monitor.stealTime) { "Steal time incorrect" } },
+ { assertEquals(0, monitor.lostTime) { "Lost time incorrect" } },
+ { assertEquals(2328039558, monitor.uptime) { "Uptime incorrect" } }
+ )
+ }
+
+ /**
+ * Obtain the trace reader for the test.
+ */
+ private fun createTestWorkload(fraction: Double, seed: Long): List<VirtualMachine> {
+ val source = trace("bitbrains-small").sampleByLoad(fraction)
+ return source.resolve(workloadLoader, Random(seed))
+ }
+
+ /**
+ * Obtain the topology factory for the test.
+ */
+ private fun createTopology(name: String = "topology"): List<HostSpec> {
+ val stream = checkNotNull(object {}.javaClass.getResourceAsStream("/env/$name.txt"))
+ return stream.use { clusterTopology(stream) }
+ }
+
+ class TestComputeMonitor : ComputeMonitor {
+ var attemptsSuccess = 0
+ var attemptsFailure = 0
+ var attemptsError = 0
+ var serversPending = 0
+ var serversActive = 0
+
+ override fun record(reader: ServiceTableReader) {
+ attemptsSuccess = reader.attemptsSuccess
+ attemptsFailure = reader.attemptsFailure
+ attemptsError = reader.attemptsError
+ serversPending = reader.serversPending
+ serversActive = reader.serversActive
+ }
+
+ var idleTime = 0L
+ var activeTime = 0L
+ var stealTime = 0L
+ var lostTime = 0L
+ var energyUsage = 0.0
+ var uptime = 0L
+
+ override fun record(reader: HostTableReader) {
+ idleTime += reader.cpuIdleTime
+ activeTime += reader.cpuActiveTime
+ stealTime += reader.cpuStealTime
+ lostTime += reader.cpuLostTime
+ energyUsage += reader.powerTotal
+ uptime += reader.uptime
+ }
+ }
+}
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/capelin/GreenifierRunnerTest.kt b/opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/capelin/GreenifierRunnerTest.kt
new file mode 100644
index 00000000..b6e361d7
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/test/kotlin/org/opendc/experiments/capelin/GreenifierRunnerTest.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.greenifier
+
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.assertDoesNotThrow
+import org.opendc.experiments.greenifier.model.OperationalPhenomena
+import org.opendc.experiments.greenifier.model.Scenario
+import org.opendc.experiments.greenifier.model.Topology
+import org.opendc.experiments.greenifier.model.Workload
+import org.opendc.experiments.compute.trace
+import java.io.File
+import java.nio.file.Files
+
+/**
+ * Test suite for [GreenifierRunner].
+ */
+class GreenifierRunnerTest {
+ /**
+ * The path to the environments.
+ */
+ private val envPath = File("src/test/resources/env")
+
+ /**
+ * The path to the traces.
+ */
+ private val tracePath = File("src/test/resources/trace")
+
+ /**
+ * Smoke test with output.
+ */
+ @Test
+ fun testSmoke() {
+ val outputPath = Files.createTempDirectory("output").toFile()
+
+ try {
+ val runner = GreenifierRunner(envPath, tracePath, outputPath)
+ val scenario = Scenario(
+ Topology("topology"),
+ Workload("bitbrains-small", trace("bitbrains-small")),
+ OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true),
+ "active-servers"
+ )
+
+ assertDoesNotThrow { runner.runScenario(scenario, seed = 0L) }
+ } finally {
+ outputPath.delete()
+ }
+ }
+
+ /**
+ * Smoke test without output.
+ */
+ @Test
+ fun testSmokeNoOutput() {
+ val runner = GreenifierRunner(envPath, tracePath, null)
+ val scenario = Scenario(
+ Topology("topology"),
+ Workload("bitbrains-small", trace("bitbrains-small")),
+ OperationalPhenomena(failureFrequency = 24.0 * 7, hasInterference = true),
+ "active-servers"
+ )
+
+ assertDoesNotThrow { runner.runScenario(scenario, seed = 0L) }
+ }
+}
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/test/resources/env/single.txt b/opendc-experiments/opendc-experiments-greenifier/src/test/resources/env/single.txt
new file mode 100644
index 00000000..5642003d
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/test/resources/env/single.txt
@@ -0,0 +1,3 @@
+ClusterID;ClusterName;Cores;Speed;Memory;numberOfHosts;memoryCapacityPerHost;coreCountPerHost
+A01;A01;8;3.2;128;1;128;8
+
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/test/resources/env/topology.txt b/opendc-experiments/opendc-experiments-greenifier/src/test/resources/env/topology.txt
new file mode 100644
index 00000000..6b347bff
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/test/resources/env/topology.txt
@@ -0,0 +1,5 @@
+ClusterID;ClusterName;Cores;Speed;Memory;numberOfHosts;memoryCapacityPerHost;coreCountPerHost
+A01;A01;32;3.2;2048;1;256;32
+B01;B01;48;2.93;1256;6;64;8
+C01;C01;32;3.2;2048;2;128;16
+
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/test/resources/trace/bitbrains-small/interference-model.json b/opendc-experiments/opendc-experiments-greenifier/src/test/resources/trace/bitbrains-small/interference-model.json
new file mode 100644
index 00000000..51fc6366
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/test/resources/trace/bitbrains-small/interference-model.json
@@ -0,0 +1,21 @@
+[
+ {
+ "vms": [
+ "141",
+ "379",
+ "851",
+ "116"
+ ],
+ "minServerLoad": 0.0,
+ "performanceScore": 0.8830158730158756
+ },
+ {
+ "vms": [
+ "205",
+ "116",
+ "463"
+ ],
+ "minServerLoad": 0.0,
+ "performanceScore": 0.7133055555552751
+ }
+]
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/test/resources/trace/bitbrains-small/meta.parquet b/opendc-experiments/opendc-experiments-greenifier/src/test/resources/trace/bitbrains-small/meta.parquet
new file mode 100644
index 00000000..9cded35f
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/test/resources/trace/bitbrains-small/meta.parquet
Binary files differ
diff --git a/opendc-experiments/opendc-experiments-greenifier/src/test/resources/trace/bitbrains-small/trace.parquet b/opendc-experiments/opendc-experiments-greenifier/src/test/resources/trace/bitbrains-small/trace.parquet
new file mode 100644
index 00000000..9d953956
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-greenifier/src/test/resources/trace/bitbrains-small/trace.parquet
Binary files differ