summaryrefslogtreecommitdiff
path: root/opendc-simulator/opendc-simulator-compute/src
diff options
context:
space:
mode:
Diffstat (limited to 'opendc-simulator/opendc-simulator-compute/src')
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt92
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractHypervisor.kt189
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractMachine.kt196
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt136
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimFairShareHypervisor.kt56
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachine.kt11
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachineContext.kt28
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMemory.kt (renamed from opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimSpaceSharedHypervisorProvider.kt)17
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimNetworkInterface.kt51
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimProcessingUnit.kt12
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimStorageInterface.kt (renamed from opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimSpaceSharedHypervisor.kt)30
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimNetworkAdapter.kt36
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimPeripheral.kt33
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimPsu.kt114
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/interference/PerformanceInterferenceModel.kt134
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimAbstractHypervisor.kt290
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisor.kt56
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorProvider.kt (renamed from opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimFairShareHypervisorProvider.kt)18
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisor.kt (renamed from opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisor.kt)56
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisorCounters.kt48
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisorProvider.kt (renamed from opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisorProvider.kt)17
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisor.kt45
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorProvider.kt42
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimVirtualMachine.kt (renamed from opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/ScalingContext.kt)24
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernor.kt66
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernor.kt50
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernor.kt (renamed from opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/PerformanceScalingGovernor.kt)12
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernor.kt (renamed from opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/DemandScalingGovernor.kt)16
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernor.kt (renamed from opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/ScalingGovernor.kt)10
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ScalingPolicy.kt51
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.kt43
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceGroup.kt44
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.kt170
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/MachineModel.kt (renamed from opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachineModel.kt)14
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/NetworkAdapter.kt36
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/StorageDevice.kt40
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/AsymptoticPowerModel.kt22
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ConstantPowerModel.kt22
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/CubicPowerModel.kt22
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/InterpolationPowerModel.kt35
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/LinearPowerModel.kt22
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/MsePowerModel.kt22
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PStatePowerDriver.kt (renamed from opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/PStateScalingDriver.kt)46
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerDriver.kt (renamed from opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/ScalingDriver.kt)21
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerModel.kt22
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SimplePowerDriver.kt (renamed from opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/SimpleScalingDriver.kt)29
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SqrtPowerModel.kt22
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SquarePowerModel.kt22
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ZeroIdlePowerDecorator.kt22
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkload.kt13
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimRuntimeWorkload.kt15
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTrace.kt233
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceFragment.kt38
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt64
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkload.kt9
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkloadLifecycle.kt63
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimHypervisorTest.kt199
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt261
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/device/SimPsuTest.kt102
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt240
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt (renamed from opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimSpaceSharedHypervisorTest.kt)88
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernorTest.kt98
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernorTest.kt81
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernorTest.kt (renamed from opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/cpufreq/DemandScalingGovernorTest.kt)26
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernorTest.kt72
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PStatePowerDriverTest.kt (renamed from opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/cpufreq/PStateScalingDriverTest.kt)67
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PowerModelTest.kt25
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt160
68 files changed, 3307 insertions, 1159 deletions
diff --git a/opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt b/opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt
index 15714aca..cb52d24f 100644
--- a/opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt
@@ -25,18 +25,20 @@ package org.opendc.simulator.compute
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
-import org.opendc.simulator.compute.cpufreq.PerformanceScalingGovernor
-import org.opendc.simulator.compute.cpufreq.SimpleScalingDriver
+import org.opendc.simulator.compute.kernel.SimFairShareHypervisor
+import org.opendc.simulator.compute.kernel.SimSpaceSharedHypervisor
+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.ConstantPowerModel
+import org.opendc.simulator.compute.power.SimplePowerDriver
+import org.opendc.simulator.compute.workload.SimTrace
import org.opendc.simulator.compute.workload.SimTraceWorkload
-import org.opendc.simulator.core.SimulationCoroutineScope
import org.opendc.simulator.core.runBlockingSimulation
-import org.opendc.simulator.resources.SimResourceScheduler
-import org.opendc.simulator.resources.SimResourceSchedulerTrampoline
+import org.opendc.simulator.flow.FlowEngine
import org.openjdk.jmh.annotations.*
+import java.util.concurrent.ThreadLocalRandom
import java.util.concurrent.TimeUnit
@State(Scope.Thread)
@@ -45,68 +47,54 @@ import java.util.concurrent.TimeUnit
@Measurement(iterations = 5, time = 3, timeUnit = TimeUnit.SECONDS)
@OptIn(ExperimentalCoroutinesApi::class)
class SimMachineBenchmarks {
- private lateinit var scope: SimulationCoroutineScope
- private lateinit var scheduler: SimResourceScheduler
- private lateinit var machineModel: SimMachineModel
+ private lateinit var machineModel: MachineModel
+ private lateinit var trace: SimTrace
@Setup
fun setUp() {
- scope = SimulationCoroutineScope()
- scheduler = SimResourceSchedulerTrampoline(scope.coroutineContext, scope.clock)
-
val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2)
- machineModel = SimMachineModel(
+ machineModel = MachineModel(
cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) },
memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }
)
- }
- @State(Scope.Benchmark)
- class Workload {
- lateinit var trace: Sequence<SimTraceWorkload.Fragment>
-
- @Setup
- fun setUp() {
- trace = sequenceOf(
- SimTraceWorkload.Fragment(1000, 28.0, 1),
- SimTraceWorkload.Fragment(1000, 3500.0, 1),
- SimTraceWorkload.Fragment(1000, 0.0, 1),
- SimTraceWorkload.Fragment(1000, 183.0, 1),
- SimTraceWorkload.Fragment(1000, 400.0, 1),
- SimTraceWorkload.Fragment(1000, 100.0, 1),
- SimTraceWorkload.Fragment(1000, 3000.0, 1),
- SimTraceWorkload.Fragment(1000, 4500.0, 1),
- )
+ val random = ThreadLocalRandom.current()
+ val builder = SimTrace.builder()
+ repeat(10000) {
+ val timestamp = it.toLong()
+ val deadline = timestamp + 1000
+ builder.add(timestamp, deadline, random.nextDouble(0.0, 4500.0), 1)
}
+ trace = builder.build()
}
@Benchmark
- fun benchmarkBareMetal(state: Workload) {
- return scope.runBlockingSimulation {
+ fun benchmarkBareMetal() {
+ return runBlockingSimulation {
+ val engine = FlowEngine(coroutineContext, clock)
val machine = SimBareMetalMachine(
- coroutineContext, clock, machineModel, PerformanceScalingGovernor(),
- SimpleScalingDriver(ConstantPowerModel(0.0))
+ engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))
)
- return@runBlockingSimulation machine.run(SimTraceWorkload(state.trace))
+ return@runBlockingSimulation machine.run(SimTraceWorkload(trace))
}
}
@Benchmark
- fun benchmarkSpaceSharedHypervisor(state: Workload) {
- return scope.runBlockingSimulation {
+ fun benchmarkSpaceSharedHypervisor() {
+ return runBlockingSimulation {
+ val engine = FlowEngine(coroutineContext, clock)
val machine = SimBareMetalMachine(
- coroutineContext, clock, machineModel, PerformanceScalingGovernor(),
- SimpleScalingDriver(ConstantPowerModel(0.0))
+ engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))
)
- val hypervisor = SimSpaceSharedHypervisor()
+ val hypervisor = SimSpaceSharedHypervisor(engine, null, null)
launch { machine.run(hypervisor) }
val vm = hypervisor.createMachine(machineModel)
try {
- return@runBlockingSimulation vm.run(SimTraceWorkload(state.trace))
+ return@runBlockingSimulation vm.run(SimTraceWorkload(trace))
} finally {
vm.close()
machine.close()
@@ -115,20 +103,20 @@ class SimMachineBenchmarks {
}
@Benchmark
- fun benchmarkFairShareHypervisorSingle(state: Workload) {
- return scope.runBlockingSimulation {
+ fun benchmarkFairShareHypervisorSingle() {
+ return runBlockingSimulation {
+ val engine = FlowEngine(coroutineContext, clock)
val machine = SimBareMetalMachine(
- coroutineContext, clock, machineModel, PerformanceScalingGovernor(),
- SimpleScalingDriver(ConstantPowerModel(0.0))
+ engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))
)
- val hypervisor = SimFairShareHypervisor(scheduler)
+ val hypervisor = SimFairShareHypervisor(engine, null, null, null)
launch { machine.run(hypervisor) }
val vm = hypervisor.createMachine(machineModel)
try {
- return@runBlockingSimulation vm.run(SimTraceWorkload(state.trace))
+ return@runBlockingSimulation vm.run(SimTraceWorkload(trace))
} finally {
vm.close()
machine.close()
@@ -137,13 +125,13 @@ class SimMachineBenchmarks {
}
@Benchmark
- fun benchmarkFairShareHypervisorDouble(state: Workload) {
- return scope.runBlockingSimulation {
+ fun benchmarkFairShareHypervisorDouble() {
+ return runBlockingSimulation {
+ val engine = FlowEngine(coroutineContext, clock)
val machine = SimBareMetalMachine(
- coroutineContext, clock, machineModel, PerformanceScalingGovernor(),
- SimpleScalingDriver(ConstantPowerModel(0.0))
+ engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))
)
- val hypervisor = SimFairShareHypervisor(scheduler)
+ val hypervisor = SimFairShareHypervisor(engine, null, null, null)
launch { machine.run(hypervisor) }
@@ -153,7 +141,7 @@ class SimMachineBenchmarks {
launch {
try {
- vm.run(SimTraceWorkload(state.trace))
+ vm.run(SimTraceWorkload(trace))
} finally {
machine.close()
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractHypervisor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractHypervisor.kt
deleted file mode 100644
index 713376e7..00000000
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractHypervisor.kt
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (c) 2021 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.simulator.compute
-
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.launch
-import org.opendc.simulator.compute.interference.PerformanceInterferenceModel
-import org.opendc.simulator.compute.model.MemoryUnit
-import org.opendc.simulator.compute.model.ProcessingUnit
-import org.opendc.simulator.compute.workload.SimWorkload
-import org.opendc.simulator.resources.*
-import java.time.Clock
-
-/**
- * Abstract implementation of the [SimHypervisor] interface.
- */
-public abstract class SimAbstractHypervisor : SimHypervisor {
- /**
- * The machine on which the hypervisor runs.
- */
- private lateinit var context: SimMachineContext
-
- /**
- * The resource switch to use.
- */
- private lateinit var switch: SimResourceSwitch
-
- /**
- * The virtual machines running on this hypervisor.
- */
- private val _vms = mutableSetOf<VirtualMachine>()
- override val vms: Set<SimMachine>
- get() = _vms
-
- /**
- * Construct the [SimResourceSwitch] implementation that performs the actual scheduling of the CPUs.
- */
- public abstract fun createSwitch(ctx: SimMachineContext): SimResourceSwitch
-
- /**
- * Check whether the specified machine model fits on this hypervisor.
- */
- public abstract fun canFit(model: SimMachineModel, switch: SimResourceSwitch): Boolean
-
- override fun canFit(model: SimMachineModel): Boolean {
- return canFit(model, switch)
- }
-
- override fun createMachine(
- model: SimMachineModel,
- performanceInterferenceModel: PerformanceInterferenceModel?
- ): SimMachine {
- require(canFit(model)) { "Machine does not fit" }
- val vm = VirtualMachine(model, performanceInterferenceModel)
- _vms.add(vm)
- return vm
- }
-
- /**
- * A virtual machine running on the hypervisor.
- *
- * @property model The machine model of the virtual machine.
- * @property performanceInterferenceModel The performance interference model to utilize.
- */
- private inner class VirtualMachine(
- override val model: SimMachineModel,
- val performanceInterferenceModel: PerformanceInterferenceModel? = null,
- ) : SimMachine {
- /**
- * A [StateFlow] representing the CPU usage of the simulated machine.
- */
- override val usage: MutableStateFlow<Double> = MutableStateFlow(0.0)
-
- /**
- * A flag to indicate that the machine is terminated.
- */
- private var isTerminated = false
-
- /**
- * The vCPUs of the machine.
- */
- private val cpus = model.cpus.map { ProcessingUnitImpl(it, switch) }
-
- /**
- * Run the specified [SimWorkload] on this machine and suspend execution util the workload has finished.
- */
- override suspend fun run(workload: SimWorkload, meta: Map<String, Any>) {
- coroutineScope {
- require(!isTerminated) { "Machine is terminated" }
-
- val ctx = object : SimMachineContext {
- override val cpus: List<SimProcessingUnit> = this@VirtualMachine.cpus
-
- override val memory: List<MemoryUnit>
- get() = model.memory
-
- override val clock: Clock
- get() = this@SimAbstractHypervisor.context.clock
-
- override val meta: Map<String, Any> = meta
- }
-
- workload.onStart(ctx)
-
- for (cpu in cpus) {
- launch {
- cpu.consume(workload.getConsumer(ctx, cpu.model))
- }
- }
- }
- }
-
- /**
- * Terminate this VM instance.
- */
- override fun close() {
- if (!isTerminated) {
- isTerminated = true
-
- cpus.forEach(SimProcessingUnit::close)
- _vms.remove(this)
- }
- }
- }
-
- override fun onStart(ctx: SimMachineContext) {
- context = ctx
- switch = createSwitch(ctx)
- }
-
- override fun getConsumer(ctx: SimMachineContext, cpu: ProcessingUnit): SimResourceConsumer {
- val forwarder = SimResourceForwarder()
- switch.addInput(forwarder)
- return forwarder
- }
-
- /**
- * The [SimProcessingUnit] of this machine.
- */
- public inner class ProcessingUnitImpl(override val model: ProcessingUnit, switch: SimResourceSwitch) : SimProcessingUnit {
- /**
- * The actual resource supporting the processing unit.
- */
- private val source = switch.addOutput(model.frequency)
-
- override val speed: Double = 0.0 /* TODO Implement */
-
- override val state: SimResourceState
- get() = source.state
-
- override fun startConsumer(consumer: SimResourceConsumer) {
- source.startConsumer(consumer)
- }
-
- override fun interrupt() {
- source.interrupt()
- }
-
- override fun cancel() {
- source.cancel()
- }
-
- override fun close() {
- source.close()
- }
- }
-}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractMachine.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractMachine.kt
index f6324e13..60a10f20 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractMachine.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractMachine.kt
@@ -23,29 +23,53 @@
package org.opendc.simulator.compute
import kotlinx.coroutines.*
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
+import org.opendc.simulator.compute.device.SimNetworkAdapter
+import org.opendc.simulator.compute.device.SimPeripheral
+import org.opendc.simulator.compute.model.MachineModel
import org.opendc.simulator.compute.model.MemoryUnit
+import org.opendc.simulator.compute.model.NetworkAdapter
+import org.opendc.simulator.compute.model.StorageDevice
import org.opendc.simulator.compute.workload.SimWorkload
-import org.opendc.simulator.resources.consume
-import org.opendc.simulator.resources.consumer.SimSpeedConsumerAdapter
-import java.time.Clock
-import kotlin.coroutines.CoroutineContext
+import org.opendc.simulator.flow.*
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.resume
/**
* Abstract implementation of the [SimMachine] interface.
+ *
+ * @param engine The engine to manage the machine's resources.
+ * @param parent The parent simulation system.
+ * @param model The model of the machine.
*/
-public abstract class SimAbstractMachine(private val clock: Clock) : SimMachine {
- private val _usage = MutableStateFlow(0.0)
- override val usage: StateFlow<Double>
- get() = _usage
+public abstract class SimAbstractMachine(
+ protected val engine: FlowEngine,
+ private val parent: FlowConvergenceListener?,
+ final override val model: MachineModel
+) : SimMachine, FlowConvergenceListener {
+ /**
+ * The resources allocated for this machine.
+ */
+ public abstract val cpus: List<SimProcessingUnit>
+
+ /**
+ * The memory interface of the machine.
+ */
+ public val memory: SimMemory = Memory(FlowSink(engine, model.memory.sumOf { it.size }.toDouble()), model.memory)
/**
- * The speed of the CPU cores.
+ * The network interfaces available to the machine.
*/
- public val speed: DoubleArray
- get() = _speed
- private var _speed = doubleArrayOf()
+ public val net: List<SimNetworkInterface> = model.net.mapIndexed { i, adapter -> NetworkAdapterImpl(engine, adapter, i) }
+
+ /**
+ * The network interfaces available to the machine.
+ */
+ public val storage: List<SimStorageInterface> = model.storage.mapIndexed { i, device -> StorageDeviceImpl(engine, device, i) }
+
+ /**
+ * The peripherals of the machine.
+ */
+ public override val peripherals: List<SimPeripheral> = net.map { it as SimNetworkAdapter }
/**
* A flag to indicate that the machine is terminated.
@@ -53,75 +77,135 @@ public abstract class SimAbstractMachine(private val clock: Clock) : SimMachine
private var isTerminated = false
/**
- * The [CoroutineContext] to run in.
+ * The continuation to resume when the virtual machine workload has finished.
*/
- protected abstract val context: CoroutineContext
+ private var cont: Continuation<Unit>? = null
/**
- * The resources allocated for this machine.
+ * Converge the specified [SimWorkload] on this machine and suspend execution util the workload has finished.
+ */
+ override suspend fun run(workload: SimWorkload, meta: Map<String, Any>) {
+ check(!isTerminated) { "Machine is terminated" }
+ check(cont == null) { "A machine cannot run concurrently" }
+
+ val ctx = Context(meta)
+
+ return suspendCancellableCoroutine { cont ->
+ this.cont = cont
+
+ // Cancel all cpus on cancellation
+ cont.invokeOnCancellation {
+ this.cont = null
+ engine.batch {
+ for (cpu in cpus) {
+ cpu.cancel()
+ }
+ }
+ }
+
+ engine.batch { workload.onStart(ctx) }
+ }
+ }
+
+ override fun close() {
+ if (isTerminated) {
+ return
+ }
+
+ isTerminated = true
+ cancel()
+ }
+
+ override fun onConverge(now: Long, delta: Long) {
+ parent?.onConverge(now, delta)
+ }
+
+ /**
+ * Cancel the workload that is currently running on the machine.
*/
- protected abstract val cpus: List<SimProcessingUnit>
+ private fun cancel() {
+ engine.batch {
+ for (cpu in cpus) {
+ cpu.cancel()
+ }
+ }
+
+ val cont = cont
+ if (cont != null) {
+ this.cont = null
+ cont.resume(Unit)
+ }
+ }
/**
* The execution context in which the workload runs.
*/
private inner class Context(override val meta: Map<String, Any>) : SimMachineContext {
- override val clock: Clock
- get() = this@SimAbstractMachine.clock
+ override val engine: FlowEngine
+ get() = this@SimAbstractMachine.engine
override val cpus: List<SimProcessingUnit> = this@SimAbstractMachine.cpus
- override val memory: List<MemoryUnit> = model.memory
+ override val memory: SimMemory = this@SimAbstractMachine.memory
+
+ override val net: List<SimNetworkInterface> = this@SimAbstractMachine.net
+
+ override val storage: List<SimStorageInterface> = this@SimAbstractMachine.storage
+
+ override fun close() = cancel()
}
/**
- * Run the specified [SimWorkload] on this machine and suspend execution util the workload has finished.
+ * The [SimMemory] implementation for a machine.
*/
- override suspend fun run(workload: SimWorkload, meta: Map<String, Any>): Unit = withContext(context) {
- require(!isTerminated) { "Machine is terminated" }
- val ctx = Context(meta)
- val totalCapacity = model.cpus.sumByDouble { it.frequency }
+ private class Memory(source: FlowSink, override val models: List<MemoryUnit>) : SimMemory, FlowConsumer by source {
+ override fun toString(): String = "SimAbstractMachine.Memory"
+ }
- _speed = DoubleArray(model.cpus.size) { 0.0 }
- var totalSpeed = 0.0
+ /**
+ * The [SimNetworkAdapter] implementation for a machine.
+ */
+ private class NetworkAdapterImpl(
+ private val engine: FlowEngine,
+ model: NetworkAdapter,
+ index: Int
+ ) : SimNetworkAdapter(), SimNetworkInterface {
+ override val name: String = "eth$index"
- // Before the workload starts, initialize the initial power draw
- updateUsage(0.0)
+ override val bandwidth: Double = model.bandwidth
- workload.onStart(ctx)
+ override val provider: FlowConsumer
+ get() = _rx
- for (cpu in cpus) {
- val model = cpu.model
- val consumer = workload.getConsumer(ctx, model)
- val adapter = SimSpeedConsumerAdapter(consumer) { newSpeed ->
- val _speed = _speed
- val _usage = _usage
+ override fun createConsumer(): FlowSource = _tx
- val oldSpeed = _speed[model.id]
- _speed[model.id] = newSpeed
- totalSpeed = totalSpeed - oldSpeed + newSpeed
+ override val tx: FlowConsumer
+ get() = _tx
+ private val _tx = FlowForwarder(engine)
- val newUsage = totalSpeed / totalCapacity
- if (_usage.value != newUsage) {
- updateUsage(totalSpeed / totalCapacity)
- }
- }
+ override val rx: FlowSource
+ get() = _rx
+ private val _rx = FlowForwarder(engine)
- launch { cpu.consume(adapter) }
- }
+ override fun toString(): String = "SimAbstractMachine.NetworkAdapterImpl[name=$name,bandwidth=$bandwidth]"
}
/**
- * This method is invoked when the usage of the machine is updated.
+ * The [SimStorageInterface] implementation for a machine.
*/
- protected open fun updateUsage(usage: Double) {
- _usage.value = usage
- }
+ private class StorageDeviceImpl(
+ engine: FlowEngine,
+ model: StorageDevice,
+ index: Int
+ ) : SimStorageInterface {
+ override val name: String = "disk$index"
- override fun close() {
- if (!isTerminated) {
- isTerminated = true
- cpus.forEach(SimProcessingUnit::close)
- }
+ override val capacity: Double = model.capacity
+
+ override val read: FlowConsumer = FlowSink(engine, model.readBandwidth)
+
+ override val write: FlowConsumer = FlowSink(engine, model.writeBandwidth)
+
+ override fun toString(): String = "SimAbstractMachine.StorageDeviceImpl[name=$name,capacity=$capacity]"
}
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt
index 27ebba21..9140d31b 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt
@@ -22,111 +22,93 @@
package org.opendc.simulator.compute
-import kotlinx.coroutines.*
-import org.opendc.simulator.compute.cpufreq.ScalingDriver
-import org.opendc.simulator.compute.cpufreq.ScalingGovernor
+import org.opendc.simulator.compute.device.SimPsu
+import org.opendc.simulator.compute.model.MachineModel
import org.opendc.simulator.compute.model.ProcessingUnit
-import org.opendc.simulator.resources.*
-import org.opendc.utils.TimerScheduler
-import java.time.Clock
-import kotlin.coroutines.*
+import org.opendc.simulator.compute.power.PowerDriver
+import org.opendc.simulator.flow.*
+import org.opendc.simulator.flow.FlowEngine
+import kotlin.math.max
/**
* A simulated bare-metal machine that is able to run a single workload.
*
- * A [SimBareMetalMachine] is a stateful object and you should be careful when operating this object concurrently. For
- * example. the class expects only a single concurrent call to [run].
+ * A [SimBareMetalMachine] is a stateful object, and you should be careful when operating this object concurrently. For
+ * example, the class expects only a single concurrent call to [run].
*
- * @param context The [CoroutineContext] to run the simulated workload in.
- * @param clock The virtual clock to track the simulation time.
+ * @param engine The [FlowEngine] to drive the simulation.
* @param model The machine model to simulate.
+ * @param powerDriver The power driver to use.
+ * @param psu The power supply of the machine.
+ * @param parent The parent simulation system.
*/
-@OptIn(ExperimentalCoroutinesApi::class, InternalCoroutinesApi::class)
public class SimBareMetalMachine(
- context: CoroutineContext,
- private val clock: Clock,
- override val model: SimMachineModel,
- scalingGovernor: ScalingGovernor,
- scalingDriver: ScalingDriver
-) : SimAbstractMachine(clock) {
+ engine: FlowEngine,
+ model: MachineModel,
+ powerDriver: PowerDriver,
+ public val psu: SimPsu = SimPsu(500.0, mapOf(1.0 to 1.0)),
+ parent: FlowConvergenceListener? = null,
+) : SimAbstractMachine(engine, parent, model) {
/**
- * The [Job] associated with this machine.
+ * The current power usage of the machine (without PSU loss) in W.
*/
- private val scope = CoroutineScope(context + Job())
-
- override val context: CoroutineContext = scope.coroutineContext
-
- /**
- * The [TimerScheduler] to use for scheduling the interrupts.
- */
- private val scheduler = SimResourceSchedulerTrampoline(this.context, clock)
-
- override val cpus: List<SimProcessingUnit> = model.cpus.map { ProcessingUnitImpl(it) }
+ public val powerUsage: Double
+ get() = _powerUsage
+ private var _powerUsage = 0.0
/**
- * Construct the [ScalingDriver.Logic] for this machine.
+ * The total energy usage of the machine (without PSU loss) in Joules.
*/
- private val scalingDriver = scalingDriver.createLogic(this)
+ public val energyUsage: Double
+ get() = _energyUsage
+ private var _energyUsage = 0.0
/**
- * The scaling contexts associated with each CPU.
+ * The processing units of the machine.
*/
- private val scalingGovernors = cpus.map { cpu ->
- scalingGovernor.createLogic(this.scalingDriver.createContext(cpu))
- }
-
- init {
- scalingGovernors.forEach { it.onStart() }
+ override val cpus: List<SimProcessingUnit> = model.cpus.map { cpu ->
+ Cpu(FlowSink(engine, cpu.frequency, this@SimBareMetalMachine), cpu)
}
/**
- * The power draw of the machine.
+ * The logic of the power driver.
*/
- public var powerDraw: Double = 0.0
- private set
+ private val powerDriverLogic = powerDriver.createLogic(this, cpus)
- override fun updateUsage(usage: Double) {
- super.updateUsage(usage)
+ private var _lastConverge = Long.MAX_VALUE
- scalingGovernors.forEach { it.onLimit() }
- powerDraw = scalingDriver.computePower()
- }
+ override fun onConverge(now: Long, delta: Long) {
+ // Update the PSU stage
+ psu.update()
- override fun close() {
- super.close()
+ val lastConverge = _lastConverge
+ _lastConverge = now
+ val duration = max(0, now - lastConverge)
+ if (duration > 0) {
+ // Compute the power and energy usage of the machine
+ _energyUsage += _powerUsage * (duration / 1000.0)
+ _powerUsage = powerDriverLogic.computePower()
+ }
+ }
- scope.cancel()
+ init {
+ psu.connect(powerDriverLogic)
}
/**
- * The [SimProcessingUnit] of this machine.
+ * A [SimProcessingUnit] of a bare-metal machine.
*/
- public inner class ProcessingUnitImpl(override val model: ProcessingUnit) : SimProcessingUnit {
- /**
- * The actual resource supporting the processing unit.
- */
- private val source = SimResourceSource(model.frequency, scheduler)
-
- override val speed: Double
- get() = source.speed
-
- override val state: SimResourceState
- get() = source.state
-
- override fun startConsumer(consumer: SimResourceConsumer) {
- source.startConsumer(consumer)
- }
-
- override fun interrupt() {
- source.interrupt()
- }
-
- override fun cancel() {
- source.cancel()
- }
-
- override fun close() {
- source.interrupt()
- }
+ private class Cpu(
+ private val source: FlowSink,
+ override val model: ProcessingUnit
+ ) : SimProcessingUnit, FlowConsumer by source {
+ override var capacity: Double
+ get() = source.capacity
+ set(value) {
+ // Clamp the capacity of the CPU between [0.0, maxFreq]
+ source.capacity = value.coerceIn(0.0, model.frequency)
+ }
+
+ override fun toString(): String = "SimBareMetalMachine.Cpu[model=$model]"
}
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimFairShareHypervisor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimFairShareHypervisor.kt
deleted file mode 100644
index 11aec2de..00000000
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimFairShareHypervisor.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (c) 2021 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.simulator.compute
-
-import org.opendc.simulator.compute.workload.SimWorkload
-import org.opendc.simulator.resources.*
-
-/**
- * A [SimHypervisor] that distributes the computing requirements of multiple [SimWorkload] on a single
- * [SimBareMetalMachine] concurrently using weighted fair sharing.
- *
- * @param listener The hypervisor listener to use.
- */
-public class SimFairShareHypervisor(private val scheduler: SimResourceScheduler, private val listener: SimHypervisor.Listener? = null) : SimAbstractHypervisor() {
-
- override fun canFit(model: SimMachineModel, switch: SimResourceSwitch): Boolean = true
-
- override fun createSwitch(ctx: SimMachineContext): SimResourceSwitch {
- return SimResourceSwitchMaxMin(
- scheduler,
- object : SimResourceSwitchMaxMin.Listener {
- override fun onSliceFinish(
- switch: SimResourceSwitchMaxMin,
- requestedWork: Long,
- grantedWork: Long,
- overcommittedWork: Long,
- interferedWork: Long,
- cpuUsage: Double,
- cpuDemand: Double
- ) {
- listener?.onSliceFinish(this@SimFairShareHypervisor, requestedWork, grantedWork, overcommittedWork, interferedWork, cpuUsage, cpuDemand)
- }
- }
- )
- }
-}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachine.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachine.kt
index bfaa60bc..ab0b56ae 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachine.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachine.kt
@@ -22,7 +22,8 @@
package org.opendc.simulator.compute
-import kotlinx.coroutines.flow.StateFlow
+import org.opendc.simulator.compute.device.SimPeripheral
+import org.opendc.simulator.compute.model.MachineModel
import org.opendc.simulator.compute.workload.SimWorkload
/**
@@ -32,15 +33,15 @@ public interface SimMachine : AutoCloseable {
/**
* The model of the machine containing its specifications.
*/
- public val model: SimMachineModel
+ public val model: MachineModel
/**
- * A [StateFlow] representing the CPU usage of the simulated machine.
+ * The peripherals attached to the machine.
*/
- public val usage: StateFlow<Double>
+ public val peripherals: List<SimPeripheral>
/**
- * Run the specified [SimWorkload] on this machine and suspend execution util the workload has finished.
+ * Converge the specified [SimWorkload] on this machine and suspend execution util the workload has finished.
*/
public suspend fun run(workload: SimWorkload, meta: Map<String, Any> = emptyMap())
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachineContext.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachineContext.kt
index c2523a2a..1317f728 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachineContext.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachineContext.kt
@@ -22,19 +22,18 @@
package org.opendc.simulator.compute
-import org.opendc.simulator.compute.model.MemoryUnit
-import java.time.Clock
+import org.opendc.simulator.flow.FlowEngine
/**
* A simulated execution context in which a bootable image runs. This interface represents the
* firmware interface between the running image (e.g. operating system) and the physical or virtual firmware on
* which the image runs.
*/
-public interface SimMachineContext {
+public interface SimMachineContext : AutoCloseable {
/**
- * The virtual clock tracking simulation time.
+ * The [FlowEngine] that simulates the machine.
*/
- public val clock: Clock
+ public val engine: FlowEngine
/**
* The metadata associated with the context.
@@ -47,7 +46,22 @@ public interface SimMachineContext {
public val cpus: List<SimProcessingUnit>
/**
- * The memory available on the machine
+ * The memory interface of the machine.
*/
- public val memory: List<MemoryUnit>
+ public val memory: SimMemory
+
+ /**
+ * The network interfaces available to the workload.
+ */
+ public val net: List<SimNetworkInterface>
+
+ /**
+ * The storage devices available to the workload.
+ */
+ public val storage: List<SimStorageInterface>
+
+ /**
+ * Stop the workload.
+ */
+ public override fun close()
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimSpaceSharedHypervisorProvider.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMemory.kt
index 83b924d7..b1aef495 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimSpaceSharedHypervisorProvider.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMemory.kt
@@ -22,16 +22,15 @@
package org.opendc.simulator.compute
-import java.time.Clock
-import kotlin.coroutines.CoroutineContext
+import org.opendc.simulator.compute.model.MemoryUnit
+import org.opendc.simulator.flow.FlowConsumer
/**
- * A [SimHypervisorProvider] for the [SimSpaceSharedHypervisor] implementation.
+ * An interface to control the memory usage of simulated workloads.
*/
-public class SimSpaceSharedHypervisorProvider : SimHypervisorProvider {
- override val id: String = "space-shared"
-
- override fun create(context: CoroutineContext, clock: Clock, listener: SimHypervisor.Listener?): SimHypervisor {
- return SimSpaceSharedHypervisor()
- }
+public interface SimMemory : FlowConsumer {
+ /**
+ * The models representing the static information of the memory units supporting this interface.
+ */
+ public val models: List<MemoryUnit>
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimNetworkInterface.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimNetworkInterface.kt
new file mode 100644
index 00000000..660b2871
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimNetworkInterface.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.simulator.compute
+
+import org.opendc.simulator.flow.FlowConsumer
+import org.opendc.simulator.flow.FlowSource
+
+/**
+ * A firmware interface to a network adapter.
+ */
+public interface SimNetworkInterface {
+ /**
+ * The name of the network interface.
+ */
+ public val name: String
+
+ /**
+ * The unidirectional bandwidth of the network interface in Mbps.
+ */
+ public val bandwidth: Double
+
+ /**
+ * The resource provider for the transmit channel of the network interface.
+ */
+ public val tx: FlowConsumer
+
+ /**
+ * The resource consumer for the receive channel of the network interface.
+ */
+ public val rx: FlowSource
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimProcessingUnit.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimProcessingUnit.kt
index 13c7d9b2..c9f36ece 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimProcessingUnit.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimProcessingUnit.kt
@@ -23,19 +23,19 @@
package org.opendc.simulator.compute
import org.opendc.simulator.compute.model.ProcessingUnit
-import org.opendc.simulator.resources.SimResourceProvider
+import org.opendc.simulator.flow.FlowConsumer
/**
* A simulated processing unit.
*/
-public interface SimProcessingUnit : SimResourceProvider {
+public interface SimProcessingUnit : FlowConsumer {
/**
- * The model representing the static properties of the processing unit.
+ * The capacity of the processing unit, which can be adjusted by the workload if supported by the machine.
*/
- public val model: ProcessingUnit
+ public override var capacity: Double
/**
- * The current speed of the processing unit.
+ * The model representing the static properties of the processing unit.
*/
- public val speed: Double
+ public val model: ProcessingUnit
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimSpaceSharedHypervisor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimStorageInterface.kt
index fd8e546f..3d648671 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimSpaceSharedHypervisor.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimStorageInterface.kt
@@ -22,17 +22,29 @@
package org.opendc.simulator.compute
-import org.opendc.simulator.resources.*
+import org.opendc.simulator.flow.FlowConsumer
/**
- * A [SimHypervisor] that allocates its sub-resources exclusively for the virtual machine that it hosts.
+ * A firmware interface to a storage device.
*/
-public class SimSpaceSharedHypervisor : SimAbstractHypervisor() {
- override fun canFit(model: SimMachineModel, switch: SimResourceSwitch): Boolean {
- return switch.inputs.size - switch.outputs.size >= model.cpus.size
- }
+public interface SimStorageInterface {
+ /**
+ * The name of the storage device.
+ */
+ public val name: String
- override fun createSwitch(ctx: SimMachineContext): SimResourceSwitch {
- return SimResourceSwitchExclusive()
- }
+ /**
+ * The capacity of the storage device in MBs.
+ */
+ public val capacity: Double
+
+ /**
+ * The resource provider for the read operations of the storage device.
+ */
+ public val read: FlowConsumer
+
+ /**
+ * The resource consumer for the write operation of the storage device.
+ */
+ public val write: FlowConsumer
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimNetworkAdapter.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimNetworkAdapter.kt
new file mode 100644
index 00000000..dfb4ecf3
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimNetworkAdapter.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.simulator.compute.device
+
+import org.opendc.simulator.compute.SimMachine
+import org.opendc.simulator.network.SimNetworkPort
+
+/**
+ * A simulated network interface card (NIC or network adapter) that can be attached to a [SimMachine].
+ */
+public abstract class SimNetworkAdapter : SimNetworkPort(), SimPeripheral {
+ /**
+ * The unidirectional bandwidth of the network adapter in Mbps.
+ */
+ public abstract val bandwidth: Double
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimPeripheral.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimPeripheral.kt
new file mode 100644
index 00000000..268271be
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimPeripheral.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.simulator.compute.device
+
+import org.opendc.simulator.compute.SimMachine
+
+/**
+ * A component that can be attached to a [SimMachine].
+ *
+ * This interface represents the physical view of the peripheral and should be used to configure the physical properties
+ * of the peripheral.
+ */
+public interface SimPeripheral
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimPsu.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimPsu.kt
new file mode 100644
index 00000000..09defbb5
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimPsu.kt
@@ -0,0 +1,114 @@
+/*
+ * 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.simulator.compute.device
+
+import org.opendc.simulator.compute.power.PowerDriver
+import org.opendc.simulator.flow.FlowConnection
+import org.opendc.simulator.flow.FlowSource
+import org.opendc.simulator.power.SimPowerInlet
+import java.util.*
+
+/**
+ * A power supply of a [SimBareMetalMachine].
+ *
+ * @param ratedOutputPower The rated output power of the PSU.
+ * @param energyEfficiency The energy efficiency of the PSU for various power draws.
+ */
+public class SimPsu(
+ private val ratedOutputPower: Double,
+ energyEfficiency: Map<Double, Double>,
+) : SimPowerInlet() {
+ /**
+ * The power draw of the machine at this instant.
+ */
+ public val powerDraw: Double
+ get() = _powerDraw
+ private var _powerDraw = 0.0
+
+ /**
+ * The energy efficiency of the PSU at various power draws.
+ */
+ private val energyEfficiency = TreeMap(energyEfficiency)
+
+ /**
+ * The consumer context.
+ */
+ private var _ctx: FlowConnection? = null
+
+ /**
+ * The driver that is connected to the PSU.
+ */
+ private var _driver: PowerDriver.Logic? = null
+
+ init {
+ require(energyEfficiency.isNotEmpty()) { "Must specify at least one entry for energy efficiency of PSU" }
+ }
+
+ /**
+ * Update the power draw of the PSU.
+ */
+ public fun update() {
+ _ctx?.pull()
+ }
+
+ /**
+ * Connect the specified [PowerDriver.Logic] to this PSU.
+ */
+ public fun connect(driver: PowerDriver.Logic) {
+ check(_driver == null) { "PSU already connected" }
+ _driver = driver
+ update()
+ }
+
+ override fun createSource(): FlowSource = object : FlowSource {
+ override fun onStart(conn: FlowConnection, now: Long) {
+ _ctx = conn
+ conn.shouldSourceConverge = true
+ }
+
+ override fun onStop(conn: FlowConnection, now: Long, delta: Long) {
+ _ctx = null
+ }
+
+ override fun onPull(conn: FlowConnection, now: Long, delta: Long): Long {
+ val powerDraw = computePowerDraw(_driver?.computePower() ?: 0.0)
+ conn.push(powerDraw)
+ return Long.MAX_VALUE
+ }
+
+ override fun onConverge(conn: FlowConnection, now: Long, delta: Long) {
+ _powerDraw = conn.rate
+ }
+ }
+
+ /**
+ * Compute the power draw of the PSU including the power loss.
+ */
+ private fun computePowerDraw(load: Double): Double {
+ val loadPercentage = (load / ratedOutputPower).coerceIn(0.0, 1.0)
+ val efficiency = energyEfficiency.ceilingEntry(loadPercentage)?.value ?: 1.0
+ return load / efficiency
+ }
+
+ override fun toString(): String = "SimPsu[draw=$_powerDraw]"
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/interference/PerformanceInterferenceModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/interference/PerformanceInterferenceModel.kt
deleted file mode 100644
index 4c409887..00000000
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/interference/PerformanceInterferenceModel.kt
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * 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.simulator.compute.interference
-
-import java.util.*
-import kotlin.random.Random
-
-/**
- * Meta-data key for the [PerformanceInterferenceModel] of an image.
- */
-public const val IMAGE_PERF_INTERFERENCE_MODEL: String = "image:performance-interference"
-
-/**
- * Performance Interference Model describing the variability incurred by different sets of workloads if colocated.
- *
- * @param items The [PerformanceInterferenceModel.Item]s that make up this model.
- */
-public class PerformanceInterferenceModel(
- public val items: SortedSet<Item>,
- private val random: Random = Random(0)
-) {
- private var intersectingItems: List<Item> = emptyList()
- private val colocatedWorkloads = TreeMap<String, Int>()
-
- /**
- * Indicate that a VM has started.
- */
- public fun onStart(name: String) {
- colocatedWorkloads.merge(name, 1, Int::plus)
- intersectingItems = items.filter { item -> doesMatch(item) }
- }
-
- /**
- * Indicate that a VM has stopped.
- */
- public fun onStop(name: String) {
- colocatedWorkloads.computeIfPresent(name) { _, v -> (v - 1).takeUnless { it == 0 } }
- intersectingItems = items.filter { item -> doesMatch(item) }
- }
-
- /**
- * Compute the performance interference based on the current server load.
- */
- public fun apply(currentServerLoad: Double): Double {
- if (intersectingItems.isEmpty()) {
- return 1.0
- }
- val score = intersectingItems
- .firstOrNull { it.minServerLoad <= currentServerLoad }
-
- // Apply performance penalty to (on average) only one of the VMs
- return if (score != null && random.nextInt(score.workloadNames.size) == 0) {
- score.performanceScore
- } else {
- 1.0
- }
- }
-
- private fun doesMatch(item: Item): Boolean {
- var count = 0
- for (
- name in item.workloadNames.subSet(
- colocatedWorkloads.firstKey(),
- colocatedWorkloads.lastKey() + "\u0000"
- )
- ) {
- count += colocatedWorkloads.getOrDefault(name, 0)
- if (count > 1)
- return true
- }
- return false
- }
-
- /**
- * Model describing how a specific set of workloads causes performance variability for each workload.
- *
- * @param workloadNames The names of the workloads that together cause performance variability for each workload in the set.
- * @param minServerLoad The minimum total server load at which this interference is activated and noticeable.
- * @param performanceScore The performance score that should be applied to each workload's performance. 1 means no
- * influence, <1 means that performance degrades, and >1 means that performance improves.
- */
- public data class Item(
- public val workloadNames: SortedSet<String>,
- public val minServerLoad: Double,
- public val performanceScore: Double
- ) : Comparable<Item> {
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
-
- other as Item
-
- if (workloadNames != other.workloadNames) return false
-
- return true
- }
-
- override fun hashCode(): Int = workloadNames.hashCode()
-
- override fun compareTo(other: Item): Int {
- var cmp = performanceScore.compareTo(other.performanceScore)
- if (cmp != 0) {
- return cmp
- }
-
- cmp = minServerLoad.compareTo(other.minServerLoad)
- if (cmp != 0) {
- return cmp
- }
-
- return hashCode().compareTo(other.hashCode())
- }
- }
-}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimAbstractHypervisor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimAbstractHypervisor.kt
new file mode 100644
index 00000000..f6d8f628
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimAbstractHypervisor.kt
@@ -0,0 +1,290 @@
+/*
+ * 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.simulator.compute.kernel
+
+import org.opendc.simulator.compute.*
+import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernor
+import org.opendc.simulator.compute.kernel.cpufreq.ScalingPolicy
+import org.opendc.simulator.compute.kernel.interference.VmInterferenceDomain
+import org.opendc.simulator.compute.model.MachineModel
+import org.opendc.simulator.compute.model.ProcessingUnit
+import org.opendc.simulator.flow.*
+import org.opendc.simulator.flow.mux.FlowMultiplexer
+import kotlin.math.roundToLong
+
+/**
+ * Abstract implementation of the [SimHypervisor] interface.
+ *
+ * @param engine The [FlowEngine] to drive the simulation.
+ * @param scalingGovernor The scaling governor to use for scaling the CPU frequency of the underlying hardware.
+ */
+public abstract class SimAbstractHypervisor(
+ protected val engine: FlowEngine,
+ private val listener: FlowConvergenceListener?,
+ private val scalingGovernor: ScalingGovernor?,
+ protected val interferenceDomain: VmInterferenceDomain? = null
+) : SimHypervisor, FlowConvergenceListener {
+ /**
+ * The machine on which the hypervisor runs.
+ */
+ protected lateinit var context: SimMachineContext
+
+ /**
+ * The resource switch to use.
+ */
+ protected abstract val mux: FlowMultiplexer
+
+ /**
+ * The virtual machines running on this hypervisor.
+ */
+ private val _vms = mutableSetOf<VirtualMachine>()
+ override val vms: Set<SimMachine>
+ get() = _vms
+
+ /**
+ * The resource counters associated with the hypervisor.
+ */
+ public override val counters: SimHypervisorCounters
+ get() = _counters
+ private val _counters = CountersImpl(this)
+
+ /**
+ * The CPU capacity of the hypervisor in MHz.
+ */
+ override val cpuCapacity: Double
+ get() = mux.capacity
+
+ /**
+ * The CPU demand of the hypervisor in MHz.
+ */
+ override val cpuDemand: Double
+ get() = mux.demand
+
+ /**
+ * The CPU usage of the hypervisor in MHz.
+ */
+ override val cpuUsage: Double
+ get() = mux.rate
+
+ /**
+ * The scaling governors attached to the physical CPUs backing this hypervisor.
+ */
+ private val governors = mutableListOf<ScalingGovernor.Logic>()
+
+ /* SimHypervisor */
+ override fun createMachine(model: MachineModel, interferenceId: String?): SimVirtualMachine {
+ require(canFit(model)) { "Machine does not fit" }
+ val vm = VirtualMachine(model, interferenceId)
+ _vms.add(vm)
+ return vm
+ }
+
+ /* SimWorkload */
+ override fun onStart(ctx: SimMachineContext) {
+ context = ctx
+
+ _cpuCount = ctx.cpus.size
+ _cpuCapacity = ctx.cpus.sumOf { it.model.frequency }
+ _counters.d = _cpuCount / _cpuCapacity * 1000L
+
+ // Clear the existing outputs of the multiplexer
+ mux.clearOutputs()
+
+ for (cpu in ctx.cpus) {
+ val governor = scalingGovernor?.createLogic(ScalingPolicyImpl(cpu))
+ if (governor != null) {
+ governors.add(governor)
+ governor.onStart()
+ }
+
+ cpu.startConsumer(mux.newOutput())
+ }
+ }
+
+ private var _cpuCount = 0
+ private var _cpuCapacity = 0.0
+
+ /* FlowConvergenceListener */
+ override fun onConverge(now: Long, delta: Long) {
+ _counters.record()
+
+ val load = cpuDemand / cpuCapacity
+ for (governor in governors) {
+ governor.onLimit(load)
+ }
+
+ listener?.onConverge(now, delta)
+ }
+
+ /**
+ * A virtual machine running on the hypervisor.
+ *
+ * @param model The machine model of the virtual machine.
+ */
+ private inner class VirtualMachine(model: MachineModel, interferenceId: String? = null) : SimAbstractMachine(engine, parent = null, model), SimVirtualMachine {
+ /**
+ * The interference key of this virtual machine.
+ */
+ private val interferenceKey = interferenceId?.let { interferenceDomain?.join(interferenceId) }
+
+ /**
+ * The vCPUs of the machine.
+ */
+ override val cpus = model.cpus.map { VCpu(mux, mux.newInput(interferenceKey), it) }
+
+ /**
+ * The resource counters associated with the hypervisor.
+ */
+ override val counters: SimHypervisorCounters
+ get() = _counters
+ private val _counters = object : SimHypervisorCounters {
+ private val d = cpus.size / cpus.sumOf { it.model.frequency } * 1000
+
+ override val cpuActiveTime: Long
+ get() = (cpus.sumOf { it.counters.actual } * d).roundToLong()
+ override val cpuIdleTime: Long
+ get() = (cpus.sumOf { it.counters.actual + it.counters.remaining } * d).roundToLong()
+ override val cpuStealTime: Long
+ get() = (cpus.sumOf { it.counters.demand - it.counters.actual } * d).roundToLong()
+ override val cpuLostTime: Long = (cpus.sumOf { it.counters.interference } * d).roundToLong()
+ }
+
+ /**
+ * The CPU capacity of the hypervisor in MHz.
+ */
+ override val cpuCapacity: Double
+ get() = cpus.sumOf(FlowConsumer::capacity)
+
+ /**
+ * The CPU demand of the hypervisor in MHz.
+ */
+ override val cpuDemand: Double
+ get() = cpus.sumOf(FlowConsumer::demand)
+
+ /**
+ * The CPU usage of the hypervisor in MHz.
+ */
+ override val cpuUsage: Double
+ get() = cpus.sumOf(FlowConsumer::rate)
+
+ override fun close() {
+ super.close()
+
+ for (cpu in cpus) {
+ cpu.close()
+ }
+
+ _vms.remove(this)
+ if (interferenceKey != null) {
+ interferenceDomain?.leave(interferenceKey)
+ }
+ }
+ }
+
+ /**
+ * A [SimProcessingUnit] of a virtual machine.
+ */
+ private class VCpu(
+ private val switch: FlowMultiplexer,
+ private val source: FlowConsumer,
+ override val model: ProcessingUnit
+ ) : SimProcessingUnit, FlowConsumer by source {
+ override var capacity: Double
+ get() = source.capacity
+ set(_) {
+ // Ignore capacity changes
+ }
+
+ override fun toString(): String = "SimAbstractHypervisor.VCpu[model=$model]"
+
+ /**
+ * Close the CPU
+ */
+ fun close() {
+ switch.removeInput(source)
+ }
+ }
+
+ /**
+ * A [ScalingPolicy] for a physical CPU of the hypervisor.
+ */
+ private class ScalingPolicyImpl(override val cpu: SimProcessingUnit) : ScalingPolicy {
+ override var target: Double
+ get() = cpu.capacity
+ set(value) {
+ cpu.capacity = value
+ }
+
+ override val max: Double = cpu.model.frequency
+
+ override val min: Double = 0.0
+ }
+
+ /**
+ * Implementation of [SimHypervisorCounters].
+ */
+ private class CountersImpl(private val hv: SimAbstractHypervisor) : SimHypervisorCounters {
+ @JvmField var d = 1.0 // Number of CPUs divided by total CPU capacity
+
+ override val cpuActiveTime: Long
+ get() = _cpuTime[0]
+ override val cpuIdleTime: Long
+ get() = _cpuTime[1]
+ override val cpuStealTime: Long
+ get() = _cpuTime[2]
+ override val cpuLostTime: Long
+ get() = _cpuTime[3]
+
+ private val _cpuTime = LongArray(4)
+ private val _previous = DoubleArray(4)
+
+ /**
+ * Record the CPU time of the hypervisor.
+ */
+ fun record() {
+ val cpuTime = _cpuTime
+ val previous = _previous
+ val counters = hv.mux.counters
+
+ val demand = counters.demand
+ val actual = counters.actual
+ val remaining = counters.remaining
+ val interference = counters.interference
+
+ val demandDelta = demand - previous[0]
+ val actualDelta = actual - previous[1]
+ val remainingDelta = remaining - previous[2]
+ val interferenceDelta = interference - previous[3]
+
+ previous[0] = demand
+ previous[1] = actual
+ previous[2] = remaining
+ previous[3] = interference
+
+ cpuTime[0] += (actualDelta * d).roundToLong()
+ cpuTime[1] += (remainingDelta * d).roundToLong()
+ cpuTime[2] += ((demandDelta - actualDelta) * d).roundToLong()
+ cpuTime[3] += (interferenceDelta * d).roundToLong()
+ }
+ }
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisor.kt
new file mode 100644
index 00000000..36f76650
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisor.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.simulator.compute.kernel
+
+import org.opendc.simulator.compute.SimMachine
+import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernor
+import org.opendc.simulator.compute.kernel.interference.VmInterferenceDomain
+import org.opendc.simulator.compute.model.MachineModel
+import org.opendc.simulator.compute.workload.SimWorkload
+import org.opendc.simulator.flow.FlowConvergenceListener
+import org.opendc.simulator.flow.FlowEngine
+import org.opendc.simulator.flow.mux.FlowMultiplexer
+import org.opendc.simulator.flow.mux.MaxMinFlowMultiplexer
+
+/**
+ * A [SimHypervisor] that distributes the computing requirements of multiple [SimWorkload]s on a single [SimMachine]
+ * concurrently using weighted fair sharing.
+ *
+ * @param engine The [FlowEngine] to manage the machine's resources.
+ * @param listener The listener for the convergence of the system.
+ * @param scalingGovernor The CPU frequency scaling governor to use for the hypervisor.
+ * @param interferenceDomain The resource interference domain to which the hypervisor belongs.
+ */
+public class SimFairShareHypervisor(
+ engine: FlowEngine,
+ listener: FlowConvergenceListener?,
+ scalingGovernor: ScalingGovernor?,
+ interferenceDomain: VmInterferenceDomain?,
+) : SimAbstractHypervisor(engine, listener, scalingGovernor, interferenceDomain) {
+ /**
+ * The multiplexer that distributes the computing capacity.
+ */
+ override val mux: FlowMultiplexer = MaxMinFlowMultiplexer(engine, this, interferenceDomain)
+
+ override fun canFit(model: MachineModel): Boolean = true
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimFairShareHypervisorProvider.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorProvider.kt
index 2ab3ea09..3136f4c8 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimFairShareHypervisorProvider.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorProvider.kt
@@ -20,11 +20,12 @@
* SOFTWARE.
*/
-package org.opendc.simulator.compute
+package org.opendc.simulator.compute.kernel
-import org.opendc.simulator.resources.SimResourceSchedulerTrampoline
-import java.time.Clock
-import kotlin.coroutines.CoroutineContext
+import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernor
+import org.opendc.simulator.compute.kernel.interference.VmInterferenceDomain
+import org.opendc.simulator.flow.FlowConvergenceListener
+import org.opendc.simulator.flow.FlowEngine
/**
* A [SimHypervisorProvider] for the [SimFairShareHypervisor] implementation.
@@ -32,7 +33,10 @@ import kotlin.coroutines.CoroutineContext
public class SimFairShareHypervisorProvider : SimHypervisorProvider {
override val id: String = "fair-share"
- override fun create(context: CoroutineContext, clock: Clock, listener: SimHypervisor.Listener?): SimHypervisor {
- return SimFairShareHypervisor(SimResourceSchedulerTrampoline(context, clock), listener)
- }
+ override fun create(
+ engine: FlowEngine,
+ listener: FlowConvergenceListener?,
+ scalingGovernor: ScalingGovernor?,
+ interferenceDomain: VmInterferenceDomain?,
+ ): SimHypervisor = SimFairShareHypervisor(engine, listener, scalingGovernor, interferenceDomain)
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisor.kt
index 4a233fec..57d4cf20 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisor.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisor.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 AtLarge Research
+ * 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
@@ -20,14 +20,15 @@
* SOFTWARE.
*/
-package org.opendc.simulator.compute
+package org.opendc.simulator.compute.kernel
-import org.opendc.simulator.compute.interference.PerformanceInterferenceModel
+import org.opendc.simulator.compute.SimMachine
+import org.opendc.simulator.compute.model.MachineModel
import org.opendc.simulator.compute.workload.SimWorkload
/**
* A SimHypervisor facilitates the execution of multiple concurrent [SimWorkload]s, while acting as a single workload
- * to a [SimBareMetalMachine].
+ * to another [SimMachine].
*/
public interface SimHypervisor : SimWorkload {
/**
@@ -36,36 +37,35 @@ public interface SimHypervisor : SimWorkload {
public val vms: Set<SimMachine>
/**
+ * The resource counters associated with the hypervisor.
+ */
+ public val counters: SimHypervisorCounters
+
+ /**
+ * The CPU usage of the hypervisor in MHz.
+ */
+ public val cpuUsage: Double
+
+ /**
+ * The CPU usage of the hypervisor in MHz.
+ */
+ public val cpuDemand: Double
+
+ /**
+ * The CPU capacity of the hypervisor in MHz.
+ */
+ public val cpuCapacity: Double
+
+ /**
* Determine whether the specified machine characterized by [model] can fit on this hypervisor at this moment.
*/
- public fun canFit(model: SimMachineModel): Boolean
+ public fun canFit(model: MachineModel): Boolean
/**
* Create a [SimMachine] instance on which users may run a [SimWorkload].
*
* @param model The machine to create.
- * @param performanceInterferenceModel The performance interference model to use.
- */
- public fun createMachine(
- model: SimMachineModel,
- performanceInterferenceModel: PerformanceInterferenceModel? = null
- ): SimMachine
-
- /**
- * Event listener for hypervisor events.
+ * @param interferenceId An identifier for the interference model.
*/
- public interface Listener {
- /**
- * This method is invoked when a slice is finished.
- */
- public fun onSliceFinish(
- hypervisor: SimHypervisor,
- requestedWork: Long,
- grantedWork: Long,
- overcommittedWork: Long,
- interferedWork: Long,
- cpuUsage: Double,
- cpuDemand: Double
- )
- }
+ public fun createMachine(model: MachineModel, interferenceId: String? = null): SimVirtualMachine
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisorCounters.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisorCounters.kt
new file mode 100644
index 00000000..030d9c5f
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisorCounters.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.simulator.compute.kernel
+
+/**
+ * Performance counters of a [SimHypervisor].
+ */
+public interface SimHypervisorCounters {
+ /**
+ * The amount of time (in milliseconds) the CPUs of the hypervisor were actively running.
+ */
+ public val cpuActiveTime: Long
+
+ /**
+ * The amount of time (in milliseconds) the CPUs of the hypervisor were idle.
+ */
+ public val cpuIdleTime: Long
+
+ /**
+ * The amount of CPU time (in milliseconds) that virtual machines were ready to run, but were not able to.
+ */
+ public val cpuStealTime: Long
+
+ /**
+ * The amount of CPU time (in milliseconds) that was lost due to interference between virtual machines.
+ */
+ public val cpuLostTime: Long
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisorProvider.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisorProvider.kt
index b66020f4..483217af 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisorProvider.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisorProvider.kt
@@ -20,10 +20,12 @@
* SOFTWARE.
*/
-package org.opendc.simulator.compute
+package org.opendc.simulator.compute.kernel
-import java.time.Clock
-import kotlin.coroutines.CoroutineContext
+import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernor
+import org.opendc.simulator.compute.kernel.interference.VmInterferenceDomain
+import org.opendc.simulator.flow.FlowConvergenceListener
+import org.opendc.simulator.flow.FlowEngine
/**
* A service provider interface for constructing a [SimHypervisor].
@@ -38,7 +40,12 @@ public interface SimHypervisorProvider {
public val id: String
/**
- * Create a [SimHypervisor] instance with the specified [listener].
+ * Create a new [SimHypervisor] instance.
*/
- public fun create(context: CoroutineContext, clock: Clock, listener: SimHypervisor.Listener? = null): SimHypervisor
+ public fun create(
+ engine: FlowEngine,
+ listener: FlowConvergenceListener? = null,
+ scalingGovernor: ScalingGovernor? = null,
+ interferenceDomain: VmInterferenceDomain? = null,
+ ): SimHypervisor
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisor.kt
new file mode 100644
index 00000000..82f8df38
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisor.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.simulator.compute.kernel
+
+import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernor
+import org.opendc.simulator.compute.model.MachineModel
+import org.opendc.simulator.flow.FlowConvergenceListener
+import org.opendc.simulator.flow.FlowEngine
+import org.opendc.simulator.flow.mux.FlowMultiplexer
+import org.opendc.simulator.flow.mux.ForwardingFlowMultiplexer
+
+/**
+ * A [SimHypervisor] that allocates its sub-resources exclusively for the virtual machine that it hosts.
+ */
+public class SimSpaceSharedHypervisor(
+ engine: FlowEngine,
+ listener: FlowConvergenceListener?,
+ scalingGovernor: ScalingGovernor?,
+) : SimAbstractHypervisor(engine, listener, scalingGovernor) {
+ override val mux: FlowMultiplexer = ForwardingFlowMultiplexer(engine)
+
+ override fun canFit(model: MachineModel): Boolean {
+ return mux.outputs.size - mux.inputs.size >= model.cpus.size
+ }
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorProvider.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorProvider.kt
new file mode 100644
index 00000000..dd6fb0b1
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorProvider.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.simulator.compute.kernel
+
+import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernor
+import org.opendc.simulator.compute.kernel.interference.VmInterferenceDomain
+import org.opendc.simulator.flow.FlowConvergenceListener
+import org.opendc.simulator.flow.FlowEngine
+
+/**
+ * A [SimHypervisorProvider] for the [SimSpaceSharedHypervisor] implementation.
+ */
+public class SimSpaceSharedHypervisorProvider : SimHypervisorProvider {
+ override val id: String = "space-shared"
+
+ override fun create(
+ engine: FlowEngine,
+ listener: FlowConvergenceListener?,
+ scalingGovernor: ScalingGovernor?,
+ interferenceDomain: VmInterferenceDomain?,
+ ): SimHypervisor = SimSpaceSharedHypervisor(engine, listener, scalingGovernor)
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/ScalingContext.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimVirtualMachine.kt
index 18338079..36219ef2 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/ScalingContext.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimVirtualMachine.kt
@@ -20,27 +20,31 @@
* SOFTWARE.
*/
-package org.opendc.simulator.compute.cpufreq
+package org.opendc.simulator.compute.kernel
import org.opendc.simulator.compute.SimMachine
-import org.opendc.simulator.compute.SimProcessingUnit
/**
- * A [ScalingContext] is used to communicate frequency scaling changes between the [ScalingGovernor] and driver.
+ * A virtual [SimMachine] running on top of another [SimMachine].
*/
-public interface ScalingContext {
+public interface SimVirtualMachine : SimMachine {
/**
- * The machine the processing unit belongs to.
+ * The resource counters associated with the virtual machine.
*/
- public val machine: SimMachine
+ public val counters: SimHypervisorCounters
/**
- * The processing unit associated with this context.
+ * The CPU usage of the VM in MHz.
*/
- public val cpu: SimProcessingUnit
+ public val cpuUsage: Double
/**
- * Target the processor to run at the specified target [frequency][freq].
+ * The CPU usage of the VM in MHz.
*/
- public fun setTarget(freq: Double)
+ public val cpuDemand: Double
+
+ /**
+ * The CPU capacity of the VM in MHz.
+ */
+ public val cpuCapacity: Double
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernor.kt
new file mode 100644
index 00000000..1a03221d
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernor.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.simulator.compute.kernel.cpufreq
+
+/**
+ * A CPUFreq [ScalingGovernor] that models the conservative scaling governor in the Linux kernel.
+ */
+public class ConservativeScalingGovernor(public val threshold: Double = 0.8, private val stepSize: Double = -1.0) :
+ ScalingGovernor {
+ override fun createLogic(policy: ScalingPolicy): ScalingGovernor.Logic = object : ScalingGovernor.Logic {
+ /**
+ * The step size to use.
+ */
+ private val stepSize = if (this@ConservativeScalingGovernor.stepSize < 0) {
+ // https://github.com/torvalds/linux/blob/master/drivers/cpufreq/cpufreq_conservative.c#L33
+ policy.max * 0.05
+ } else {
+ this@ConservativeScalingGovernor.stepSize.coerceAtMost(policy.max)
+ }
+
+ /**
+ * The previous load of the CPU.
+ */
+ private var previousLoad = threshold
+
+ override fun onStart() {
+ policy.target = policy.min
+ }
+
+ override fun onLimit(load: Double) {
+ val currentTarget = policy.target
+ if (load > threshold) {
+ // Check for load increase (see: https://github.com/torvalds/linux/blob/master/drivers/cpufreq/cpufreq_conservative.c#L102)
+ val step = when {
+ load > previousLoad -> stepSize
+ load < previousLoad -> -stepSize
+ else -> 0.0
+ }
+ policy.target = (currentTarget + step).coerceIn(policy.min, policy.max)
+ }
+ previousLoad = load
+ }
+ }
+
+ override fun toString(): String = "ConservativeScalingGovernor[threshold=$threshold,stepSize=$stepSize]"
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernor.kt
new file mode 100644
index 00000000..aef15ce9
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernor.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.simulator.compute.kernel.cpufreq
+
+/**
+ * A CPUFreq [ScalingGovernor] that models the on-demand scaling governor in the Linux kernel.
+ */
+public class OnDemandScalingGovernor(public val threshold: Double = 0.8) : ScalingGovernor {
+ override fun createLogic(policy: ScalingPolicy): ScalingGovernor.Logic = object : ScalingGovernor.Logic {
+ /**
+ * The multiplier used for the linear frequency scaling.
+ */
+ private val multiplier = (policy.max - policy.min) / 100
+
+ override fun onStart() {
+ policy.target = policy.min
+ }
+
+ override fun onLimit(load: Double) {
+ policy.target = if (load < threshold) {
+ /* Proportional scaling (see: https://github.com/torvalds/linux/blob/master/drivers/cpufreq/cpufreq_ondemand.c#L151). */
+ policy.min + load * multiplier
+ } else {
+ policy.max
+ }
+ }
+ }
+
+ override fun toString(): String = "OnDemandScalingGovernor[threshold=$threshold]"
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/PerformanceScalingGovernor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernor.kt
index 96f8775a..13109a9a 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/PerformanceScalingGovernor.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernor.kt
@@ -20,17 +20,17 @@
* SOFTWARE.
*/
-package org.opendc.simulator.compute.cpufreq
+package org.opendc.simulator.compute.kernel.cpufreq
/**
* A CPUFreq [ScalingGovernor] that causes the highest possible frequency to be requested from the resource.
*/
public class PerformanceScalingGovernor : ScalingGovernor {
- override fun createLogic(ctx: ScalingContext): ScalingGovernor.Logic = object : ScalingGovernor.Logic {
- override fun onLimit() {
- ctx.setTarget(ctx.cpu.model.frequency)
+ override fun createLogic(policy: ScalingPolicy): ScalingGovernor.Logic = object : ScalingGovernor.Logic {
+ override fun onStart() {
+ policy.target = policy.max
}
-
- override fun toString(): String = "PerformanceScalingGovernor.Logic"
}
+
+ override fun toString(): String = "PerformanceScalingGovernor"
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/DemandScalingGovernor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernor.kt
index ddbe1ca0..32c0703a 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/DemandScalingGovernor.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernor.kt
@@ -20,17 +20,17 @@
* SOFTWARE.
*/
-package org.opendc.simulator.compute.cpufreq
+package org.opendc.simulator.compute.kernel.cpufreq
/**
- * A CPUFreq [ScalingGovernor] that requests the frequency based on the utilization of the machine.
+ * A CPUFreq [ScalingGovernor] that causes the lowest possible frequency to be requested from the resource.
*/
-public class DemandScalingGovernor : ScalingGovernor {
- override fun createLogic(ctx: ScalingContext): ScalingGovernor.Logic = object : ScalingGovernor.Logic {
- override fun onLimit() {
- ctx.setTarget(ctx.cpu.speed)
+public class PowerSaveScalingGovernor : ScalingGovernor {
+ override fun createLogic(policy: ScalingPolicy): ScalingGovernor.Logic = object : ScalingGovernor.Logic {
+ override fun onStart() {
+ policy.target = policy.min
}
-
- override fun toString(): String = "DemandScalingGovernor.Logic"
}
+
+ override fun toString(): String = "PowerSaveScalingGovernor"
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/ScalingGovernor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernor.kt
index c9aea580..d33827db 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/ScalingGovernor.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernor.kt
@@ -20,7 +20,7 @@
* SOFTWARE.
*/
-package org.opendc.simulator.compute.cpufreq
+package org.opendc.simulator.compute.kernel.cpufreq
/**
* A [ScalingGovernor] in the CPUFreq subsystem of OpenDC is responsible for scaling the frequency of simulated CPUs
@@ -33,9 +33,9 @@ package org.opendc.simulator.compute.cpufreq
*/
public interface ScalingGovernor {
/**
- * Create the scaling logic for the specified [context]
+ * Create the scaling logic for the specified [policy]
*/
- public fun createLogic(ctx: ScalingContext): Logic
+ public fun createLogic(policy: ScalingPolicy): Logic
/**
* The logic of the scaling governor.
@@ -48,7 +48,9 @@ public interface ScalingGovernor {
/**
* This method is invoked when the governor should re-decide the frequency limits.
+ *
+ * @param load The load of the system.
*/
- public fun onLimit() {}
+ public fun onLimit(load: Double) {}
}
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ScalingPolicy.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ScalingPolicy.kt
new file mode 100644
index 00000000..f9351896
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ScalingPolicy.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.simulator.compute.kernel.cpufreq
+
+import org.opendc.simulator.compute.SimProcessingUnit
+
+/**
+ * An interface that holds the state managed by a [ScalingGovernor] and used by the underlying machine to control the
+ * CPU frequencies.
+ */
+public interface ScalingPolicy {
+ /**
+ * The processing unit that is associated with this policy.
+ */
+ public val cpu: SimProcessingUnit
+
+ /**
+ * The target frequency which the CPU should attempt to attain.
+ */
+ public var target: Double
+
+ /**
+ * The minimum frequency to which the CPU may scale.
+ */
+ public val min: Double
+
+ /**
+ * The maximum frequency to which the CPU may scale.
+ */
+ public val max: Double
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.kt
new file mode 100644
index 00000000..b737d61a
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.simulator.compute.kernel.interference
+
+import org.opendc.simulator.flow.interference.InterferenceDomain
+import org.opendc.simulator.flow.interference.InterferenceKey
+
+/**
+ * The interference domain of a hypervisor.
+ */
+public interface VmInterferenceDomain : InterferenceDomain {
+ /**
+ * Join this interference domain.
+ *
+ * @param id The identifier of the virtual machine.
+ */
+ public fun join(id: String): InterferenceKey
+
+ /**
+ * Leave this interference domain.
+ */
+ public fun leave(key: InterferenceKey)
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceGroup.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceGroup.kt
new file mode 100644
index 00000000..708ddede
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceGroup.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.simulator.compute.kernel.interference
+
+/**
+ * A group of virtual machines that together can interfere when operating on the same resources, causing performance
+ * variability.
+ */
+public data class VmInterferenceGroup(
+ /**
+ * The minimum load of the host before the interference occurs.
+ */
+ public val targetLoad: Double,
+
+ /**
+ * A score in [0, 1] representing the performance variability as a result of resource interference.
+ */
+ public val score: Double,
+
+ /**
+ * The members of this interference group.
+ */
+ public val members: Set<String>
+)
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.kt
new file mode 100644
index 00000000..b3d72507
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.kt
@@ -0,0 +1,170 @@
+/*
+ * 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.simulator.compute.kernel.interference
+
+import org.opendc.simulator.flow.interference.InterferenceKey
+import java.util.*
+
+/**
+ * An interference model that models the resource interference between virtual machines on a host.
+ *
+ * @param groups The groups of virtual machines that interfere with each other.
+ * @param random The [Random] instance to select the affected virtual machines.
+ */
+public class VmInterferenceModel(
+ private val groups: List<VmInterferenceGroup>,
+ private val random: Random = Random(0)
+) {
+ /**
+ * Construct a new [VmInterferenceDomain].
+ */
+ public fun newDomain(): VmInterferenceDomain = object : VmInterferenceDomain {
+ /**
+ * The stateful groups of this domain.
+ */
+ private val groups = this@VmInterferenceModel.groups.map { GroupContext(it) }
+
+ /**
+ * The set of keys active in this domain.
+ */
+ private val keys = mutableSetOf<InterferenceKeyImpl>()
+
+ override fun join(id: String): InterferenceKey {
+ val key = InterferenceKeyImpl(id, groups.filter { id in it }.sortedBy { it.group.targetLoad })
+ keys += key
+ return key
+ }
+
+ override fun leave(key: InterferenceKey) {
+ if (key is InterferenceKeyImpl) {
+ keys -= key
+ key.leave()
+ }
+ }
+
+ override fun apply(key: InterferenceKey?, load: Double): Double {
+ if (key == null || key !is InterferenceKeyImpl) {
+ return 1.0
+ }
+
+ val ctx = key.findGroup(load)
+ val group = ctx?.group
+
+ // Apply performance penalty to (on average) only one of the VMs
+ return if (group != null && random.nextInt(group.members.size) == 0) {
+ group.score
+ } else {
+ 1.0
+ }
+ }
+
+ override fun toString(): String = "VmInterferenceDomain"
+ }
+
+ /**
+ * An interference key.
+ *
+ * @param id The identifier of the member.
+ * @param groups The groups to which the key belongs.
+ */
+ private inner class InterferenceKeyImpl(val id: String, private val groups: List<GroupContext>) : InterferenceKey {
+ init {
+ for (group in groups) {
+ group.join(this)
+ }
+ }
+
+ /**
+ * Find the active group that applies for the interference member.
+ */
+ fun findGroup(load: Double): GroupContext? {
+ // Find the first active group whose target load is lower than the current load
+ val index = groups.binarySearchBy(load) { it.group.targetLoad }
+ val target = if (index >= 0) index else -(index + 1)
+
+ // Check whether there are active groups ahead of the index
+ for (i in target until groups.size) {
+ val group = groups[i]
+ if (group.group.targetLoad > load) {
+ break
+ } else if (group.isActive) {
+ return group
+ }
+ }
+
+ // Check whether there are active groups before the index
+ for (i in (target - 1) downTo 0) {
+ val group = groups[i]
+ if (group.isActive) {
+ return group
+ }
+ }
+
+ return null
+ }
+
+ /**
+ * Leave all the groups.
+ */
+ fun leave() {
+ for (group in groups) {
+ group.leave(this)
+ }
+ }
+ }
+
+ /**
+ * A group context is used to track the active keys per interference group.
+ */
+ private inner class GroupContext(val group: VmInterferenceGroup) {
+ /**
+ * The active keys that are part of this group.
+ */
+ private val keys = mutableSetOf<InterferenceKeyImpl>()
+
+ /**
+ * A flag to indicate that the group is active.
+ */
+ val isActive
+ get() = keys.size > 1
+
+ /**
+ * Determine whether the specified [id] is part of this group.
+ */
+ operator fun contains(id: String): Boolean = id in group.members
+
+ /**
+ * Join this group with the specified [key].
+ */
+ fun join(key: InterferenceKeyImpl) {
+ keys += key
+ }
+
+ /**
+ * Leave this group with the specified [key].
+ */
+ fun leave(key: InterferenceKeyImpl) {
+ keys -= key
+ }
+ }
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachineModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/MachineModel.kt
index 2b414540..7e4d7191 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachineModel.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/MachineModel.kt
@@ -20,15 +20,19 @@
* SOFTWARE.
*/
-package org.opendc.simulator.compute
-
-import org.opendc.simulator.compute.model.MemoryUnit
-import org.opendc.simulator.compute.model.ProcessingUnit
+package org.opendc.simulator.compute.model
/**
* A description of the physical or virtual machine on which a bootable image runs.
*
* @property cpus The list of processing units available to the image.
* @property memory The list of memory units available to the image.
+ * @property net A list of network adapters available to the machine.
+ * @property storage A list of storage devices available to the machine.
*/
-public data class SimMachineModel(public val cpus: List<ProcessingUnit>, public val memory: List<MemoryUnit>)
+public data class MachineModel(
+ public val cpus: List<ProcessingUnit>,
+ public val memory: List<MemoryUnit>,
+ public val net: List<NetworkAdapter> = emptyList(),
+ public val storage: List<StorageDevice> = emptyList()
+)
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/NetworkAdapter.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/NetworkAdapter.kt
new file mode 100644
index 00000000..46472144
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/NetworkAdapter.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.simulator.compute.model
+
+/**
+ * A description of a network adapter that is
+ *
+ * @property vendor The vendor of the network adapter.
+ * @property modelName The model name of the network adapter.
+ * @property bandwidth The bandwidth of the network adapter in Mbps.
+ */
+public data class NetworkAdapter(
+ public val vendor: String,
+ public val modelName: String,
+ public val bandwidth: Double
+)
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/StorageDevice.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/StorageDevice.kt
new file mode 100644
index 00000000..2621ad6d
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/StorageDevice.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.simulator.compute.model
+
+/**
+ * Model for a physical storage device attached to a machine.
+ *
+ * @property vendor The vendor of the storage device.
+ * @property modelName The model name of the device.
+ * @property capacity The capacity of the device.
+ * @property readBandwidth The read bandwidth of the device in MBps.
+ * @property writeBandwidth The write bandwidth of the device in MBps.
+ */
+public data class StorageDevice(
+ public val vendor: String,
+ public val modelName: String,
+ public val capacity: Double,
+ public val readBandwidth: Double,
+ public val writeBandwidth: Double
+)
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/AsymptoticPowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/AsymptoticPowerModel.kt
index ddc6d5b1..62b85e12 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/AsymptoticPowerModel.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/AsymptoticPowerModel.kt
@@ -1,3 +1,25 @@
+/*
+ * 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.simulator.compute.power
import kotlin.math.E
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ConstantPowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ConstantPowerModel.kt
index b8cb8412..0fe32b0d 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ConstantPowerModel.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ConstantPowerModel.kt
@@ -1,3 +1,25 @@
+/*
+ * 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.simulator.compute.power
/**
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/CubicPowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/CubicPowerModel.kt
index 9c44438a..0d3bf6cc 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/CubicPowerModel.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/CubicPowerModel.kt
@@ -1,3 +1,25 @@
+/*
+ * 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.simulator.compute.power
import kotlin.math.pow
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/InterpolationPowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/InterpolationPowerModel.kt
index cbfcd810..2694700c 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/InterpolationPowerModel.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/InterpolationPowerModel.kt
@@ -1,6 +1,27 @@
+/*
+ * 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.simulator.compute.power
-import org.yaml.snakeyaml.Yaml
import kotlin.math.ceil
import kotlin.math.floor
import kotlin.math.max
@@ -15,8 +36,6 @@ import kotlin.math.min
* @see <a href="http://www.spec.org/power_ssj2008/results/res2011q1/">Machines used in the SPEC benchmark</a>
*/
public class InterpolationPowerModel(private val powerValues: List<Double>) : PowerModel {
- public constructor(hardwareName: String) : this(loadAveragePowerValue(hardwareName))
-
public override fun computePower(utilization: Double): Double {
val clampedUtilization = min(1.0, max(0.0, utilization))
val utilizationFlr = floor(clampedUtilization * 10).toInt()
@@ -41,14 +60,4 @@ public class InterpolationPowerModel(private val powerValues: List<Double>) : Po
* @return the power consumption for the given utilization percentage
*/
private fun getAveragePowerValue(index: Int): Double = powerValues[index]
-
- private companion object {
- private fun loadAveragePowerValue(hardwareName: String, path: String = "spec_machines.yml"): List<Double> {
- val content = this::class
- .java.classLoader
- .getResourceAsStream(path)
- val hardwareToAveragePowerValues: Map<String, List<Double>> = Yaml().load(content)
- return hardwareToAveragePowerValues.getOrDefault(hardwareName, listOf())
- }
- }
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/LinearPowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/LinearPowerModel.kt
index 0f45afae..dadc56ec 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/LinearPowerModel.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/LinearPowerModel.kt
@@ -1,3 +1,25 @@
+/*
+ * 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.simulator.compute.power
/**
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/MsePowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/MsePowerModel.kt
index 8486d680..612ce2fc 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/MsePowerModel.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/MsePowerModel.kt
@@ -1,3 +1,25 @@
+/*
+ * 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.simulator.compute.power
import kotlin.math.pow
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/PStateScalingDriver.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PStatePowerDriver.kt
index 6f44d778..f71446f8 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/PStateScalingDriver.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PStatePowerDriver.kt
@@ -20,67 +20,41 @@
* SOFTWARE.
*/
-package org.opendc.simulator.compute.cpufreq
+package org.opendc.simulator.compute.power
import org.opendc.simulator.compute.SimMachine
import org.opendc.simulator.compute.SimProcessingUnit
-import org.opendc.simulator.compute.power.PowerModel
import java.util.*
import kotlin.math.max
import kotlin.math.min
/**
- * A [ScalingDriver] that scales the frequency of the processor based on a discrete set of frequencies.
+ * A [PowerDriver] that computes the power draw using multiple [PowerModel]s based on multiple frequency states.
*
* @param states A map describing the states of the driver.
*/
-public class PStateScalingDriver(states: Map<Double, PowerModel>) : ScalingDriver {
+public class PStatePowerDriver(states: Map<Double, PowerModel>) : PowerDriver {
/**
* The P-States defined by the user and ordered by key.
*/
- private val states = TreeMap(states)
-
- override fun createLogic(machine: SimMachine): ScalingDriver.Logic = object : ScalingDriver.Logic {
- /**
- * The scaling contexts.
- */
- private val contexts = mutableListOf<ScalingContextImpl>()
-
- override fun createContext(cpu: SimProcessingUnit): ScalingContext {
- val ctx = ScalingContextImpl(machine, cpu)
- contexts.add(ctx)
- return ctx
- }
+ private val states: TreeMap<Double, PowerModel> = TreeMap(states)
+ override fun createLogic(machine: SimMachine, cpus: List<SimProcessingUnit>): PowerDriver.Logic = object : PowerDriver.Logic {
override fun computePower(): Double {
var targetFreq = 0.0
var totalSpeed = 0.0
- for (ctx in contexts) {
- targetFreq = max(ctx.target, targetFreq)
- totalSpeed += ctx.cpu.speed
+ for (cpu in cpus) {
+ targetFreq = max(cpu.capacity, targetFreq)
+ totalSpeed += cpu.rate
}
val maxFreq = states.lastKey()
val (actualFreq, model) = states.ceilingEntry(min(maxFreq, targetFreq))
- val utilization = totalSpeed / (actualFreq * contexts.size)
+ val utilization = totalSpeed / (actualFreq * cpus.size)
return model.computePower(utilization)
}
-
- override fun toString(): String = "PStateScalingDriver.Logic"
}
- private class ScalingContextImpl(
- override val machine: SimMachine,
- override val cpu: SimProcessingUnit
- ) : ScalingContext {
- var target = cpu.model.frequency
- private set
-
- override fun setTarget(freq: Double) {
- target = freq
- }
-
- override fun toString(): String = "PStateScalingDriver.Context"
- }
+ override fun toString(): String = "PStatePowerDriver[states=$states]"
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/ScalingDriver.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerDriver.kt
index b4fd7550..1a46dd4a 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/ScalingDriver.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerDriver.kt
@@ -20,33 +20,28 @@
* SOFTWARE.
*/
-package org.opendc.simulator.compute.cpufreq
+package org.opendc.simulator.compute.power
import org.opendc.simulator.compute.SimMachine
import org.opendc.simulator.compute.SimProcessingUnit
/**
- * A [ScalingDriver] is responsible for switching the processor to the correct frequency.
+ * A [PowerDriver] is responsible for tracking the power usage for a component of the machine.
*/
-public interface ScalingDriver {
+public interface PowerDriver {
/**
- * Create the scaling logic for the specified [machine]
+ * Create the driver logic for the specified [machine].
*/
- public fun createLogic(machine: SimMachine): Logic
+ public fun createLogic(machine: SimMachine, cpus: List<SimProcessingUnit>): Logic
/**
- * The logic of the scaling driver.
+ * The logic of the power driver.
*/
public interface Logic {
/**
- * Create the [ScalingContext] for the specified [cpu] instance.
- */
- public fun createContext(cpu: SimProcessingUnit): ScalingContext
-
- /**
- * Compute the power consumption of the processor.
+ * Compute the power consumption of the component.
*
- * @return The power consumption of the processor in W.
+ * @return The power consumption of the component in W.
*/
public fun computePower(): Double
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerModel.kt
index 1387e65a..decb2420 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerModel.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerModel.kt
@@ -1,3 +1,25 @@
+/*
+ * 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.simulator.compute.power
import org.opendc.simulator.compute.SimMachine
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/SimpleScalingDriver.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SimplePowerDriver.kt
index cf0bbb28..34e91c35 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/SimpleScalingDriver.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SimplePowerDriver.kt
@@ -20,30 +20,29 @@
* SOFTWARE.
*/
-package org.opendc.simulator.compute.cpufreq
+package org.opendc.simulator.compute.power
import org.opendc.simulator.compute.SimMachine
import org.opendc.simulator.compute.SimProcessingUnit
-import org.opendc.simulator.compute.power.PowerModel
/**
- * A [ScalingDriver] that ignores the instructions of the [ScalingGovernor] and directly computes the power consumption
- * based on the specified [power model][model].
+ * A [PowerDriver] that computes the power consumption based on a single specified [power model][model].
*/
-public class SimpleScalingDriver(private val model: PowerModel) : ScalingDriver {
- override fun createLogic(machine: SimMachine): ScalingDriver.Logic = object : ScalingDriver.Logic {
- override fun createContext(cpu: SimProcessingUnit): ScalingContext {
- return object : ScalingContext {
- override val machine: SimMachine = machine
+public class SimplePowerDriver(private val model: PowerModel) : PowerDriver {
+ override fun createLogic(machine: SimMachine, cpus: List<SimProcessingUnit>): PowerDriver.Logic = object : PowerDriver.Logic {
- override val cpu: SimProcessingUnit = cpu
+ override fun computePower(): Double {
+ var targetFreq = 0.0
+ var totalSpeed = 0.0
- override fun setTarget(freq: Double) {}
+ for (cpu in cpus) {
+ targetFreq += cpu.capacity
+ totalSpeed += cpu.rate
}
- }
-
- override fun computePower(): Double = model.computePower(machine.usage.value)
- override fun toString(): String = "SimpleScalingDriver.Logic"
+ return model.computePower(totalSpeed / targetFreq)
+ }
}
+
+ override fun toString(): String = "SimplePowerDriver[model=$model]"
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SqrtPowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SqrtPowerModel.kt
index afa1d82f..0665dbd9 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SqrtPowerModel.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SqrtPowerModel.kt
@@ -1,3 +1,25 @@
+/*
+ * 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.simulator.compute.power
import kotlin.math.sqrt
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SquarePowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SquarePowerModel.kt
index 82a9d37d..e4ae88a9 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SquarePowerModel.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SquarePowerModel.kt
@@ -1,3 +1,25 @@
+/*
+ * 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.simulator.compute.power
import kotlin.math.pow
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ZeroIdlePowerDecorator.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ZeroIdlePowerDecorator.kt
index 19dfcadd..886227e1 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ZeroIdlePowerDecorator.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ZeroIdlePowerDecorator.kt
@@ -1,3 +1,25 @@
+/*
+ * 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.simulator.compute.power
/**
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkload.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkload.kt
index 63c9d28c..99f4a1e1 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkload.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkload.kt
@@ -23,9 +23,7 @@
package org.opendc.simulator.compute.workload
import org.opendc.simulator.compute.SimMachineContext
-import org.opendc.simulator.compute.model.ProcessingUnit
-import org.opendc.simulator.resources.SimResourceConsumer
-import org.opendc.simulator.resources.consumer.SimWorkConsumer
+import org.opendc.simulator.flow.source.FixedFlowSource
/**
* A [SimWorkload] that models applications as a static number of floating point operations ([flops]) executed on
@@ -43,10 +41,11 @@ public class SimFlopsWorkload(
require(utilization > 0.0 && utilization <= 1.0) { "Utilization must be in (0, 1]" }
}
- override fun onStart(ctx: SimMachineContext) {}
-
- override fun getConsumer(ctx: SimMachineContext, cpu: ProcessingUnit): SimResourceConsumer {
- return SimWorkConsumer(flops.toDouble() / ctx.cpus.size, utilization)
+ override fun onStart(ctx: SimMachineContext) {
+ val lifecycle = SimWorkloadLifecycle(ctx)
+ for (cpu in ctx.cpus) {
+ cpu.startConsumer(lifecycle.waitFor(FixedFlowSource(flops.toDouble() / ctx.cpus.size, utilization)))
+ }
}
override fun toString(): String = "SimFlopsWorkload(FLOPs=$flops,utilization=$utilization)"
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimRuntimeWorkload.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimRuntimeWorkload.kt
index a3420e32..2ef3bc43 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimRuntimeWorkload.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimRuntimeWorkload.kt
@@ -23,9 +23,7 @@
package org.opendc.simulator.compute.workload
import org.opendc.simulator.compute.SimMachineContext
-import org.opendc.simulator.compute.model.ProcessingUnit
-import org.opendc.simulator.resources.SimResourceConsumer
-import org.opendc.simulator.resources.consumer.SimWorkConsumer
+import org.opendc.simulator.flow.source.FixedFlowSource
/**
* A [SimWorkload] that models application execution as a single duration.
@@ -42,11 +40,12 @@ public class SimRuntimeWorkload(
require(utilization > 0.0 && utilization <= 1.0) { "Utilization must be in (0, 1]" }
}
- override fun onStart(ctx: SimMachineContext) {}
-
- override fun getConsumer(ctx: SimMachineContext, cpu: ProcessingUnit): SimResourceConsumer {
- val limit = cpu.frequency * utilization
- return SimWorkConsumer((limit / 1000) * duration, utilization)
+ override fun onStart(ctx: SimMachineContext) {
+ val lifecycle = SimWorkloadLifecycle(ctx)
+ for (cpu in ctx.cpus) {
+ val limit = cpu.capacity * utilization
+ cpu.startConsumer(lifecycle.waitFor(FixedFlowSource((limit / 1000) * duration, utilization)))
+ }
}
override fun toString(): String = "SimRuntimeWorkload(duration=$duration,utilization=$utilization)"
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTrace.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTrace.kt
new file mode 100644
index 00000000..4f567b55
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTrace.kt
@@ -0,0 +1,233 @@
+/*
+ * 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.simulator.compute.workload
+
+import org.opendc.simulator.compute.model.ProcessingUnit
+import org.opendc.simulator.flow.FlowConnection
+import org.opendc.simulator.flow.FlowSource
+import kotlin.math.min
+
+/**
+ * A workload trace that describes the resource utilization over time in a collection of [SimTraceFragment]s.
+ *
+ * @param usageCol The column containing the CPU usage of each fragment (in MHz).
+ * @param timestampCol The column containing the starting timestamp for each fragment (in epoch millis).
+ * @param deadlineCol The column containing the ending timestamp for each fragment (in epoch millis).
+ * @param coresCol The column containing the utilized cores.
+ * @param size The number of fragments in the trace.
+ */
+public class SimTrace(
+ private val usageCol: DoubleArray,
+ private val timestampCol: LongArray,
+ private val deadlineCol: LongArray,
+ private val coresCol: IntArray,
+ private val size: Int,
+) {
+ init {
+ require(size >= 0) { "Invalid trace size" }
+ require(usageCol.size >= size) { "Invalid number of usage entries" }
+ require(timestampCol.size >= size) { "Invalid number of timestamp entries" }
+ require(deadlineCol.size >= size) { "Invalid number of deadline entries" }
+ require(coresCol.size >= size) { "Invalid number of core entries" }
+ }
+
+ public companion object {
+ /**
+ * Construct a [SimTrace] with the specified fragments.
+ */
+ public fun ofFragments(fragments: List<SimTraceFragment>): SimTrace {
+ val size = fragments.size
+ val usageCol = DoubleArray(size)
+ val timestampCol = LongArray(size)
+ val deadlineCol = LongArray(size)
+ val coresCol = IntArray(size)
+
+ for (i in fragments.indices) {
+ val fragment = fragments[i]
+ usageCol[i] = fragment.usage
+ timestampCol[i] = fragment.timestamp
+ deadlineCol[i] = fragment.timestamp + fragment.duration
+ coresCol[i] = fragment.cores
+ }
+
+ return SimTrace(usageCol, timestampCol, deadlineCol, coresCol, size)
+ }
+
+ /**
+ * Construct a [SimTrace] with the specified fragments.
+ */
+ @JvmStatic
+ public fun ofFragments(vararg fragments: SimTraceFragment): SimTrace {
+ val size = fragments.size
+ val usageCol = DoubleArray(size)
+ val timestampCol = LongArray(size)
+ val deadlineCol = LongArray(size)
+ val coresCol = IntArray(size)
+
+ for (i in fragments.indices) {
+ val fragment = fragments[i]
+ usageCol[i] = fragment.usage
+ timestampCol[i] = fragment.timestamp
+ deadlineCol[i] = fragment.timestamp + fragment.duration
+ coresCol[i] = fragment.cores
+ }
+
+ return SimTrace(usageCol, timestampCol, deadlineCol, coresCol, size)
+ }
+
+ /**
+ * Create a [SimTrace.Builder] instance.
+ */
+ @JvmStatic
+ public fun builder(): Builder = Builder()
+ }
+
+ /**
+ * Construct a new [FlowSource] for the specified [cpu].
+ */
+ public fun newSource(cpu: ProcessingUnit, offset: Long): FlowSource {
+ return CpuConsumer(cpu, offset, usageCol, timestampCol, deadlineCol, coresCol, size)
+ }
+
+ /**
+ * A builder class for a [SimTrace].
+ */
+ public class Builder internal constructor() {
+ /**
+ * The columns of the trace.
+ */
+ private var usageCol: DoubleArray = DoubleArray(16)
+ private var timestampCol: LongArray = LongArray(16)
+ private var deadlineCol: LongArray = LongArray(16)
+ private var coresCol: IntArray = IntArray(16)
+
+ /**
+ * The number of entries in the trace.
+ */
+ private var size = 0
+
+ /**
+ * Add the specified [SimTraceFragment] to the trace.
+ */
+ public fun add(fragment: SimTraceFragment) {
+ add(fragment.timestamp, fragment.timestamp + fragment.duration, fragment.usage, fragment.cores)
+ }
+
+ /**
+ * Add a fragment to the trace.
+ *
+ * @param timestamp Timestamp at which the fragment starts (in epoch millis).
+ * @param deadline Timestamp at which the fragment ends (in epoch millis).
+ * @param usage CPU usage of this fragment.
+ * @param cores Number of cores used.
+ */
+ public fun add(timestamp: Long, deadline: Long, usage: Double, cores: Int) {
+ val size = size
+
+ if (size == usageCol.size) {
+ grow()
+ }
+
+ timestampCol[size] = timestamp
+ deadlineCol[size] = deadline
+ usageCol[size] = usage
+ coresCol[size] = cores
+
+ this.size++
+ }
+
+ /**
+ * Helper function to grow the capacity of the column arrays.
+ */
+ private fun grow() {
+ val arraySize = usageCol.size
+ val newSize = arraySize * 2
+
+ usageCol = usageCol.copyOf(newSize)
+ timestampCol = timestampCol.copyOf(newSize)
+ deadlineCol = deadlineCol.copyOf(newSize)
+ coresCol = coresCol.copyOf(newSize)
+ }
+
+ /**
+ * Construct the immutable [SimTrace].
+ */
+ public fun build(): SimTrace {
+ return SimTrace(usageCol, timestampCol, deadlineCol, coresCol, size)
+ }
+ }
+
+ /**
+ * A CPU consumer for the trace workload.
+ */
+ private class CpuConsumer(
+ cpu: ProcessingUnit,
+ private val offset: Long,
+ private val usageCol: DoubleArray,
+ private val timestampCol: LongArray,
+ private val deadlineCol: LongArray,
+ private val coresCol: IntArray,
+ private val size: Int
+ ) : FlowSource {
+ private val id = cpu.id
+ private val coreCount = cpu.node.coreCount
+
+ /**
+ * The index in the trace.
+ */
+ private var _idx = 0
+
+ override fun onPull(conn: FlowConnection, now: Long, delta: Long): Long {
+ val size = size
+ val nowOffset = now - offset
+
+ var idx = _idx
+ val deadlines = deadlineCol
+ var deadline = deadlines[idx]
+
+ while (deadline <= nowOffset && ++idx < size) {
+ deadline = deadlines[idx]
+ }
+
+ if (idx >= size) {
+ conn.close()
+ return Long.MAX_VALUE
+ }
+
+ _idx = idx
+ val timestamp = timestampCol[idx]
+
+ // Fragment is in the future
+ if (timestamp > nowOffset) {
+ conn.push(0.0)
+ return timestamp - nowOffset
+ }
+
+ val cores = min(coreCount, coresCol[idx])
+ val usage = usageCol[idx]
+
+ conn.push(if (id < cores) usage / cores else 0.0)
+ return deadline - nowOffset
+ }
+ }
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceFragment.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceFragment.kt
new file mode 100644
index 00000000..5285847f
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceFragment.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.simulator.compute.workload
+
+/**
+ * A fragment of the workload trace.
+ *
+ * @param timestamp The timestamp at which the fragment starts (in epoch millis).
+ * @param duration The duration of the fragment (in milliseconds).
+ * @param usage The CPU usage during the fragment (in MHz).
+ * @param cores The amount of cores utilized during the fragment.
+ */
+public data class SimTraceFragment(
+ @JvmField val timestamp: Long,
+ @JvmField val duration: Long,
+ @JvmField val usage: Double,
+ @JvmField val cores: Int
+)
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt
index ffb332d1..53c98409 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt
@@ -23,72 +23,22 @@
package org.opendc.simulator.compute.workload
import org.opendc.simulator.compute.SimMachineContext
-import org.opendc.simulator.compute.model.ProcessingUnit
-import org.opendc.simulator.resources.SimResourceCommand
-import org.opendc.simulator.resources.SimResourceConsumer
-import org.opendc.simulator.resources.SimResourceContext
-import org.opendc.simulator.resources.consumer.SimConsumerBarrier
/**
* A [SimWorkload] that replays a workload trace consisting of multiple fragments, each indicating the resource
* consumption for some period of time.
+ *
+ * @param trace The trace of fragments to use.
+ * @param offset The offset for the timestamps.
*/
-public class SimTraceWorkload(public val trace: Sequence<Fragment>) : SimWorkload {
- private var offset = Long.MIN_VALUE
- private val iterator = trace.iterator()
- private var fragment: Fragment? = null
- private lateinit var barrier: SimConsumerBarrier
-
+public class SimTraceWorkload(private val trace: SimTrace, private val offset: Long = 0L) : SimWorkload {
override fun onStart(ctx: SimMachineContext) {
- check(offset == Long.MIN_VALUE) { "Workload does not support re-use" }
-
- barrier = SimConsumerBarrier(ctx.cpus.size)
- fragment = nextFragment()
- offset = ctx.clock.millis()
- }
-
- override fun getConsumer(ctx: SimMachineContext, cpu: ProcessingUnit): SimResourceConsumer {
- return object : SimResourceConsumer {
- override fun onNext(ctx: SimResourceContext): SimResourceCommand {
- val now = ctx.clock.millis()
- val fragment = fragment ?: return SimResourceCommand.Exit
- val usage = fragment.usage / fragment.cores
- val work = (fragment.duration / 1000) * usage
- val deadline = offset + fragment.duration
-
- assert(deadline >= now) { "Deadline already passed" }
+ val lifecycle = SimWorkloadLifecycle(ctx)
- val cmd =
- if (cpu.id < fragment.cores && work > 0.0)
- SimResourceCommand.Consume(work, usage, deadline)
- else
- SimResourceCommand.Idle(deadline)
-
- if (barrier.enter()) {
- this@SimTraceWorkload.fragment = nextFragment()
- this@SimTraceWorkload.offset += fragment.duration
- }
-
- return cmd
- }
+ for (cpu in ctx.cpus) {
+ cpu.startConsumer(lifecycle.waitFor(trace.newSource(cpu.model, offset)))
}
}
override fun toString(): String = "SimTraceWorkload"
-
- /**
- * Obtain the next fragment.
- */
- private fun nextFragment(): Fragment? {
- return if (iterator.hasNext()) {
- iterator.next()
- } else {
- null
- }
- }
-
- /**
- * A fragment of the workload.
- */
- public data class Fragment(val duration: Long, val usage: Double, val cores: Int)
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkload.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkload.kt
index bdc12bb5..b80665fa 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkload.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkload.kt
@@ -23,8 +23,6 @@
package org.opendc.simulator.compute.workload
import org.opendc.simulator.compute.SimMachineContext
-import org.opendc.simulator.compute.model.ProcessingUnit
-import org.opendc.simulator.resources.SimResourceConsumer
/**
* A model that characterizes the runtime behavior of some particular workload.
@@ -35,11 +33,8 @@ import org.opendc.simulator.resources.SimResourceConsumer
public interface SimWorkload {
/**
* This method is invoked when the workload is started.
+ *
+ * @param ctx The execution context in which the machine runs.
*/
public fun onStart(ctx: SimMachineContext)
-
- /**
- * Obtain the resource consumer for the specified processing unit.
- */
- public fun getConsumer(ctx: SimMachineContext, cpu: ProcessingUnit): SimResourceConsumer
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkloadLifecycle.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkloadLifecycle.kt
new file mode 100644
index 00000000..cc4f1f6a
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkloadLifecycle.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.simulator.compute.workload
+
+import org.opendc.simulator.compute.SimMachineContext
+import org.opendc.simulator.flow.FlowConnection
+import org.opendc.simulator.flow.FlowSource
+
+/**
+ * A helper class to manage the lifecycle of a [SimWorkload]
+ */
+public class SimWorkloadLifecycle(private val ctx: SimMachineContext) {
+ /**
+ * The resource consumers which represent the lifecycle of the workload.
+ */
+ private val waiting = mutableSetOf<FlowSource>()
+
+ /**
+ * Wait for the specified [consumer] to complete before ending the lifecycle of the workload.
+ */
+ public fun waitFor(consumer: FlowSource): FlowSource {
+ waiting.add(consumer)
+ return object : FlowSource by consumer {
+ override fun onStop(conn: FlowConnection, now: Long, delta: Long) {
+ try {
+ consumer.onStop(conn, now, delta)
+ } finally {
+ complete(consumer)
+ }
+ }
+ override fun toString(): String = "SimWorkloadLifecycle.Consumer[delegate=$consumer]"
+ }
+ }
+
+ /**
+ * Complete the specified [FlowSource].
+ */
+ private fun complete(consumer: FlowSource) {
+ if (waiting.remove(consumer) && waiting.isEmpty()) {
+ ctx.close()
+ }
+ }
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimHypervisorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimHypervisorTest.kt
deleted file mode 100644
index 8886caa7..00000000
--- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimHypervisorTest.kt
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * 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.simulator.compute
-
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.flow.toList
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.yield
-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.simulator.compute.cpufreq.PerformanceScalingGovernor
-import org.opendc.simulator.compute.cpufreq.SimpleScalingDriver
-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.ConstantPowerModel
-import org.opendc.simulator.compute.workload.SimTraceWorkload
-import org.opendc.simulator.core.runBlockingSimulation
-import org.opendc.simulator.resources.SimResourceSchedulerTrampoline
-
-/**
- * Test suite for the [SimHypervisor] class.
- */
-@OptIn(ExperimentalCoroutinesApi::class)
-internal class SimHypervisorTest {
- private lateinit var model: SimMachineModel
-
- @BeforeEach
- fun setUp() {
- val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 1)
- model = SimMachineModel(
- cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) },
- memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }
- )
- }
-
- /**
- * Test overcommitting of resources via the hypervisor with a single VM.
- */
- @Test
- fun testOvercommittedSingle() = runBlockingSimulation {
- val listener = object : SimHypervisor.Listener {
- var totalRequestedWork = 0L
- var totalGrantedWork = 0L
- var totalOvercommittedWork = 0L
-
- override fun onSliceFinish(
- hypervisor: SimHypervisor,
- requestedWork: Long,
- grantedWork: Long,
- overcommittedWork: Long,
- interferedWork: Long,
- cpuUsage: Double,
- cpuDemand: Double
- ) {
- totalRequestedWork += requestedWork
- totalGrantedWork += grantedWork
- totalOvercommittedWork += overcommittedWork
- }
- }
-
- val duration = 5 * 60L
- val workloadA =
- SimTraceWorkload(
- sequenceOf(
- SimTraceWorkload.Fragment(duration * 1000, 28.0, 1),
- SimTraceWorkload.Fragment(duration * 1000, 3500.0, 1),
- SimTraceWorkload.Fragment(duration * 1000, 0.0, 1),
- SimTraceWorkload.Fragment(duration * 1000, 183.0, 1)
- ),
- )
-
- val machine = SimBareMetalMachine(coroutineContext, clock, model, PerformanceScalingGovernor(), SimpleScalingDriver(ConstantPowerModel(0.0)))
- val hypervisor = SimFairShareHypervisor(SimResourceSchedulerTrampoline(coroutineContext, clock), listener)
-
- launch {
- machine.run(hypervisor)
- println("Hypervisor finished")
- }
- yield()
- val vm = hypervisor.createMachine(model)
- val res = mutableListOf<Double>()
- val job = launch { machine.usage.toList(res) }
-
- vm.run(workloadA)
- yield()
- job.cancel()
- machine.close()
-
- assertAll(
- { assertEquals(1113300, listener.totalRequestedWork, "Requested Burst does not match") },
- { assertEquals(1023300, listener.totalGrantedWork, "Granted Burst does not match") },
- { assertEquals(90000, listener.totalOvercommittedWork, "Overcommissioned Burst does not match") },
- { assertEquals(listOf(0.0, 0.00875, 1.0, 0.0, 0.0571875, 0.0), res) { "VM usage is correct" } },
- { assertEquals(1200000, clock.millis()) { "Current time is correct" } }
- )
- }
-
- /**
- * Test overcommitting of resources via the hypervisor with two VMs.
- */
- @Test
- fun testOvercommittedDual() = runBlockingSimulation {
- val listener = object : SimHypervisor.Listener {
- var totalRequestedWork = 0L
- var totalGrantedWork = 0L
- var totalOvercommittedWork = 0L
-
- override fun onSliceFinish(
- hypervisor: SimHypervisor,
- requestedWork: Long,
- grantedWork: Long,
- overcommittedWork: Long,
- interferedWork: Long,
- cpuUsage: Double,
- cpuDemand: Double
- ) {
- totalRequestedWork += requestedWork
- totalGrantedWork += grantedWork
- totalOvercommittedWork += overcommittedWork
- }
- }
-
- val duration = 5 * 60L
- val workloadA =
- SimTraceWorkload(
- sequenceOf(
- SimTraceWorkload.Fragment(duration * 1000, 28.0, 1),
- SimTraceWorkload.Fragment(duration * 1000, 3500.0, 1),
- SimTraceWorkload.Fragment(duration * 1000, 0.0, 1),
- SimTraceWorkload.Fragment(duration * 1000, 183.0, 1)
- ),
- )
- val workloadB =
- SimTraceWorkload(
- sequenceOf(
- SimTraceWorkload.Fragment(duration * 1000, 28.0, 1),
- SimTraceWorkload.Fragment(duration * 1000, 3100.0, 1),
- SimTraceWorkload.Fragment(duration * 1000, 0.0, 1),
- SimTraceWorkload.Fragment(duration * 1000, 73.0, 1)
- )
- )
-
- val machine = SimBareMetalMachine(
- coroutineContext, clock, model, PerformanceScalingGovernor(),
- SimpleScalingDriver(ConstantPowerModel(0.0))
- )
- val hypervisor = SimFairShareHypervisor(SimResourceSchedulerTrampoline(coroutineContext, clock), listener)
-
- launch {
- machine.run(hypervisor)
- }
-
- yield()
- coroutineScope {
- launch {
- val vm = hypervisor.createMachine(model)
- vm.run(workloadA)
- vm.close()
- }
- val vm = hypervisor.createMachine(model)
- vm.run(workloadB)
- vm.close()
- }
- yield()
- machine.close()
- yield()
-
- assertAll(
- { assertEquals(2082000, listener.totalRequestedWork, "Requested Burst does not match") },
- { assertEquals(1062000, listener.totalGrantedWork, "Granted Burst does not match") },
- { assertEquals(1020000, listener.totalOvercommittedWork, "Overcommissioned Burst does not match") },
- { assertEquals(1200000, clock.millis()) }
- )
- }
-}
diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt
index 205f2eca..0bb24ed8 100644
--- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt
@@ -23,41 +23,47 @@
package org.opendc.simulator.compute
import kotlinx.coroutines.*
-import kotlinx.coroutines.flow.toList
+import org.junit.jupiter.api.*
import org.junit.jupiter.api.Assertions.assertEquals
-import org.junit.jupiter.api.BeforeEach
-import org.junit.jupiter.api.Test
-import org.junit.jupiter.api.assertDoesNotThrow
-import org.junit.jupiter.api.assertThrows
-import org.opendc.simulator.compute.cpufreq.PerformanceScalingGovernor
-import org.opendc.simulator.compute.cpufreq.SimpleScalingDriver
-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.device.SimNetworkAdapter
+import org.opendc.simulator.compute.model.*
import org.opendc.simulator.compute.power.ConstantPowerModel
+import org.opendc.simulator.compute.power.LinearPowerModel
+import org.opendc.simulator.compute.power.SimplePowerDriver
import org.opendc.simulator.compute.workload.SimFlopsWorkload
+import org.opendc.simulator.compute.workload.SimWorkload
+import org.opendc.simulator.compute.workload.SimWorkloadLifecycle
import org.opendc.simulator.core.runBlockingSimulation
+import org.opendc.simulator.flow.FlowEngine
+import org.opendc.simulator.flow.source.FixedFlowSource
+import org.opendc.simulator.network.SimNetworkSink
+import org.opendc.simulator.power.SimPowerSource
/**
* Test suite for the [SimBareMetalMachine] class.
*/
-@OptIn(ExperimentalCoroutinesApi::class)
class SimMachineTest {
- private lateinit var machineModel: SimMachineModel
+ private lateinit var machineModel: MachineModel
@BeforeEach
fun setUp() {
val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2)
- machineModel = SimMachineModel(
+ machineModel = MachineModel(
cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) },
- memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }
+ memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) },
+ net = listOf(NetworkAdapter("Mellanox", "ConnectX-5", 25000.0)),
+ storage = listOf(StorageDevice("Samsung", "EVO", 1000.0, 250.0, 250.0))
)
}
@Test
fun testFlopsWorkload() = runBlockingSimulation {
- val machine = SimBareMetalMachine(coroutineContext, clock, machineModel, PerformanceScalingGovernor(), SimpleScalingDriver(ConstantPowerModel(0.0)))
+ val machine = SimBareMetalMachine(
+ FlowEngine(coroutineContext, clock),
+ machineModel,
+ SimplePowerDriver(ConstantPowerModel(0.0))
+ )
try {
machine.run(SimFlopsWorkload(2_000, utilization = 1.0))
@@ -72,11 +78,15 @@ class SimMachineTest {
@Test
fun testDualSocketMachine() = runBlockingSimulation {
val cpuNode = machineModel.cpus[0].node
- val machineModel = SimMachineModel(
+ val machineModel = MachineModel(
cpus = List(cpuNode.coreCount * 2) { ProcessingUnit(cpuNode, it % 2, 1000.0) },
memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }
)
- val machine = SimBareMetalMachine(coroutineContext, clock, machineModel, PerformanceScalingGovernor(), SimpleScalingDriver(ConstantPowerModel(0.0)))
+ val machine = SimBareMetalMachine(
+ FlowEngine(coroutineContext, clock),
+ machineModel,
+ SimplePowerDriver(ConstantPowerModel(0.0))
+ )
try {
machine.run(SimFlopsWorkload(2_000, utilization = 1.0))
@@ -89,17 +99,212 @@ class SimMachineTest {
}
@Test
- fun testUsage() = runBlockingSimulation {
- val machine = SimBareMetalMachine(coroutineContext, clock, machineModel, PerformanceScalingGovernor(), SimpleScalingDriver(ConstantPowerModel(0.0)))
+ fun testPower() = runBlockingSimulation {
+ val engine = FlowEngine(coroutineContext, clock)
+ val machine = SimBareMetalMachine(
+ engine,
+ machineModel,
+ SimplePowerDriver(LinearPowerModel(100.0, 50.0))
+ )
+ val source = SimPowerSource(engine, capacity = 1000.0)
+ source.connect(machine.psu)
- val res = mutableListOf<Double>()
- val job = launch { machine.usage.toList(res) }
+ try {
+ coroutineScope {
+ launch { machine.run(SimFlopsWorkload(2_000, utilization = 1.0)) }
+ assertAll(
+ { assertEquals(100.0, machine.psu.powerDraw) },
+ { assertEquals(100.0, source.powerDraw) }
+ )
+ }
+ } finally {
+ machine.close()
+ }
+ }
+
+ @Test
+ fun testCapacityClamp() = runBlockingSimulation {
+ val machine = SimBareMetalMachine(
+ FlowEngine(coroutineContext, clock),
+ machineModel,
+ SimplePowerDriver(ConstantPowerModel(0.0))
+ )
try {
- machine.run(SimFlopsWorkload(2_000, utilization = 1.0))
- yield()
- job.cancel()
- assertEquals(listOf(0.0, 0.5, 1.0, 0.5, 0.0), res) { "Machine is fully utilized" }
+ machine.run(object : SimWorkload {
+ override fun onStart(ctx: SimMachineContext) {
+ val cpu = ctx.cpus[0]
+
+ cpu.capacity = cpu.model.frequency + 1000.0
+ assertEquals(cpu.model.frequency, cpu.capacity)
+ cpu.capacity = -1.0
+ assertEquals(0.0, cpu.capacity)
+
+ ctx.close()
+ }
+ })
+ } finally {
+ machine.close()
+ }
+ }
+
+ @Test
+ fun testMemory() = runBlockingSimulation {
+ val machine = SimBareMetalMachine(
+ FlowEngine(coroutineContext, clock),
+ machineModel,
+ SimplePowerDriver(ConstantPowerModel(0.0))
+ )
+
+ try {
+ machine.run(object : SimWorkload {
+ override fun onStart(ctx: SimMachineContext) {
+ assertEquals(32_000 * 4.0, ctx.memory.capacity)
+ ctx.close()
+ }
+ })
+ } finally {
+ machine.close()
+ }
+ }
+
+ @Test
+ fun testMemoryUsage() = runBlockingSimulation {
+ val machine = SimBareMetalMachine(
+ FlowEngine(coroutineContext, clock),
+ machineModel,
+ SimplePowerDriver(ConstantPowerModel(0.0))
+ )
+
+ try {
+ machine.run(object : SimWorkload {
+ override fun onStart(ctx: SimMachineContext) {
+ val lifecycle = SimWorkloadLifecycle(ctx)
+ ctx.memory.startConsumer(lifecycle.waitFor(FixedFlowSource(ctx.memory.capacity, utilization = 0.8)))
+ }
+ })
+
+ assertEquals(1250, clock.millis())
+ } finally {
+ machine.close()
+ }
+ }
+
+ @Test
+ fun testNetUsage() = runBlockingSimulation {
+ val engine = FlowEngine(coroutineContext, clock)
+ val machine = SimBareMetalMachine(
+ engine,
+ machineModel,
+ SimplePowerDriver(ConstantPowerModel(0.0))
+ )
+
+ val adapter = (machine.peripherals[0] as SimNetworkAdapter)
+ adapter.connect(SimNetworkSink(engine, adapter.bandwidth))
+
+ try {
+ machine.run(object : SimWorkload {
+ override fun onStart(ctx: SimMachineContext) {
+ val lifecycle = SimWorkloadLifecycle(ctx)
+ val iface = ctx.net[0]
+ iface.tx.startConsumer(lifecycle.waitFor(FixedFlowSource(iface.bandwidth, utilization = 0.8)))
+ }
+ })
+
+ assertEquals(1250, clock.millis())
+ } finally {
+ machine.close()
+ }
+ }
+
+ @Test
+ fun testDiskReadUsage() = runBlockingSimulation {
+ val engine = FlowEngine(coroutineContext, clock)
+ val machine = SimBareMetalMachine(
+ engine,
+ machineModel,
+ SimplePowerDriver(ConstantPowerModel(0.0))
+ )
+
+ try {
+ machine.run(object : SimWorkload {
+ override fun onStart(ctx: SimMachineContext) {
+ val lifecycle = SimWorkloadLifecycle(ctx)
+ val disk = ctx.storage[0]
+ disk.read.startConsumer(lifecycle.waitFor(FixedFlowSource(disk.read.capacity, utilization = 0.8)))
+ }
+ })
+
+ assertEquals(1250, clock.millis())
+ } finally {
+ machine.close()
+ }
+ }
+
+ @Test
+ fun testDiskWriteUsage() = runBlockingSimulation {
+ val engine = FlowEngine(coroutineContext, clock)
+ val machine = SimBareMetalMachine(
+ engine,
+ machineModel,
+ SimplePowerDriver(ConstantPowerModel(0.0))
+ )
+
+ try {
+ machine.run(object : SimWorkload {
+ override fun onStart(ctx: SimMachineContext) {
+ val lifecycle = SimWorkloadLifecycle(ctx)
+ val disk = ctx.storage[0]
+ disk.write.startConsumer(lifecycle.waitFor(FixedFlowSource(disk.write.capacity, utilization = 0.8)))
+ }
+ })
+
+ assertEquals(1250, clock.millis())
+ } finally {
+ machine.close()
+ }
+ }
+
+ @Test
+ fun testCancellation() = runBlockingSimulation {
+ val machine = SimBareMetalMachine(
+ FlowEngine(coroutineContext, clock),
+ machineModel,
+ SimplePowerDriver(ConstantPowerModel(0.0))
+ )
+
+ try {
+ coroutineScope {
+ launch { machine.run(SimFlopsWorkload(2_000, utilization = 1.0)) }
+ cancel()
+ }
+ } catch (_: CancellationException) {
+ // Ignore
+ } finally {
+ machine.close()
+ }
+
+ assertEquals(0, clock.millis())
+ }
+
+ @Test
+ fun testConcurrentRuns() = runBlockingSimulation {
+ val machine = SimBareMetalMachine(
+ FlowEngine(coroutineContext, clock),
+ machineModel,
+ SimplePowerDriver(ConstantPowerModel(0.0))
+ )
+
+ try {
+ coroutineScope {
+ launch {
+ machine.run(SimFlopsWorkload(2_000, utilization = 1.0))
+ }
+
+ assertThrows<IllegalStateException> {
+ machine.run(SimFlopsWorkload(2_000, utilization = 1.0))
+ }
+ }
} finally {
machine.close()
}
@@ -107,7 +312,11 @@ class SimMachineTest {
@Test
fun testClose() = runBlockingSimulation {
- val machine = SimBareMetalMachine(coroutineContext, clock, machineModel, PerformanceScalingGovernor(), SimpleScalingDriver(ConstantPowerModel(0.0)))
+ val machine = SimBareMetalMachine(
+ FlowEngine(coroutineContext, clock),
+ machineModel,
+ SimplePowerDriver(ConstantPowerModel(0.0))
+ )
machine.close()
assertDoesNotThrow { machine.close() }
diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/device/SimPsuTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/device/SimPsuTest.kt
new file mode 100644
index 00000000..e5b509f0
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/device/SimPsuTest.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.simulator.compute.device
+
+import io.mockk.every
+import io.mockk.mockk
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.assertThrows
+import org.opendc.simulator.compute.power.PowerDriver
+import org.opendc.simulator.core.runBlockingSimulation
+import org.opendc.simulator.flow.FlowEngine
+import org.opendc.simulator.power.SimPowerSource
+
+/**
+ * Test suite for [SimPsu]
+ */
+internal class SimPsuTest {
+
+ @Test
+ fun testInvalidInput() {
+ assertThrows<IllegalArgumentException> { SimPsu(1.0, emptyMap()) }
+ }
+
+ @Test
+ fun testDoubleConnect() {
+ val psu = SimPsu(1.0, mapOf(0.0 to 1.0))
+ val cpuLogic = mockk<PowerDriver.Logic>()
+ psu.connect(cpuLogic)
+ assertThrows<IllegalStateException> { psu.connect(mockk()) }
+ }
+
+ @Test
+ fun testPsuIdle() = runBlockingSimulation {
+ val ratedOutputPower = 240.0
+ val energyEfficiency = mapOf(0.0 to 1.0)
+
+ val engine = FlowEngine(coroutineContext, clock)
+ val source = SimPowerSource(engine, capacity = ratedOutputPower)
+
+ val cpuLogic = mockk<PowerDriver.Logic>()
+ every { cpuLogic.computePower() } returns 0.0
+
+ val psu = SimPsu(ratedOutputPower, energyEfficiency)
+ psu.connect(cpuLogic)
+ source.connect(psu)
+
+ assertEquals(0.0, source.powerDraw, 0.01)
+ }
+
+ @Test
+ fun testPsuPowerLoss() = runBlockingSimulation {
+ val ratedOutputPower = 240.0
+ // Efficiency of 80 Plus Titanium PSU
+ val energyEfficiency = sortedMapOf(
+ 0.3 to 0.9,
+ 0.7 to 0.92,
+ 1.0 to 0.94,
+ )
+
+ val engine = FlowEngine(coroutineContext, clock)
+ val source = SimPowerSource(engine, capacity = ratedOutputPower)
+
+ val cpuLogic = mockk<PowerDriver.Logic>()
+ every { cpuLogic.computePower() } returnsMany listOf(50.0, 100.0, 150.0, 200.0)
+
+ val psu = SimPsu(ratedOutputPower, energyEfficiency)
+ psu.connect(cpuLogic)
+ source.connect(psu)
+
+ assertEquals(55.55, source.powerDraw, 0.01)
+
+ psu.update()
+ assertEquals(108.695, source.powerDraw, 0.01)
+
+ psu.update()
+ assertEquals(163.043, source.powerDraw, 0.01)
+
+ psu.update()
+ assertEquals(212.765, source.powerDraw, 0.01)
+ }
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt
new file mode 100644
index 00000000..6f32cf46
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt
@@ -0,0 +1,240 @@
+/*
+ * 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.simulator.compute.kernel
+
+import kotlinx.coroutines.*
+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.junit.jupiter.api.assertDoesNotThrow
+import org.opendc.simulator.compute.SimBareMetalMachine
+import org.opendc.simulator.compute.kernel.cpufreq.PerformanceScalingGovernor
+import org.opendc.simulator.compute.kernel.interference.VmInterferenceGroup
+import org.opendc.simulator.compute.kernel.interference.VmInterferenceModel
+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.ConstantPowerModel
+import org.opendc.simulator.compute.power.SimplePowerDriver
+import org.opendc.simulator.compute.workload.SimTrace
+import org.opendc.simulator.compute.workload.SimTraceFragment
+import org.opendc.simulator.compute.workload.SimTraceWorkload
+import org.opendc.simulator.core.runBlockingSimulation
+import org.opendc.simulator.flow.FlowEngine
+
+/**
+ * Test suite for the [SimHypervisor] class.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+internal class SimFairShareHypervisorTest {
+ private lateinit var model: MachineModel
+
+ @BeforeEach
+ fun setUp() {
+ val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 1)
+ model = MachineModel(
+ cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) },
+ memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }
+ )
+ }
+
+ /**
+ * Test overcommitting of resources via the hypervisor with a single VM.
+ */
+ @Test
+ fun testOvercommittedSingle() = runBlockingSimulation {
+ val duration = 5 * 60L
+ val workloadA =
+ SimTraceWorkload(
+ SimTrace.ofFragments(
+ SimTraceFragment(0, duration * 1000, 28.0, 1),
+ SimTraceFragment(duration * 1000, duration * 1000, 3500.0, 1),
+ SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1),
+ SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1)
+ ),
+ )
+
+ val platform = FlowEngine(coroutineContext, clock)
+ val machine = SimBareMetalMachine(platform, model, SimplePowerDriver(ConstantPowerModel(0.0)))
+ val hypervisor = SimFairShareHypervisor(platform, null, PerformanceScalingGovernor(), null)
+
+ launch {
+ machine.run(hypervisor)
+ println("Hypervisor finished")
+ }
+ yield()
+
+ val vm = hypervisor.createMachine(model)
+ vm.run(workloadA)
+
+ yield()
+ machine.close()
+
+ assertAll(
+ { assertEquals(319781, hypervisor.counters.cpuActiveTime, "Active time does not match") },
+ { assertEquals(880219, hypervisor.counters.cpuIdleTime, "Idle time does not match") },
+ { assertEquals(28125, hypervisor.counters.cpuStealTime, "Steal time does not match") },
+ { assertEquals(1200000, clock.millis()) { "Current time is correct" } }
+ )
+ }
+
+ /**
+ * Test overcommitting of resources via the hypervisor with two VMs.
+ */
+ @Test
+ fun testOvercommittedDual() = runBlockingSimulation {
+ val duration = 5 * 60L
+ val workloadA =
+ SimTraceWorkload(
+ SimTrace.ofFragments(
+ SimTraceFragment(0, duration * 1000, 28.0, 1),
+ SimTraceFragment(duration * 1000, duration * 1000, 3500.0, 1),
+ SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1),
+ SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1)
+ ),
+ )
+ val workloadB =
+ SimTraceWorkload(
+ SimTrace.ofFragments(
+ SimTraceFragment(0, duration * 1000, 28.0, 1),
+ SimTraceFragment(duration * 1000, duration * 1000, 3100.0, 1),
+ SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1),
+ SimTraceFragment(duration * 3000, duration * 1000, 73.0, 1)
+ )
+ )
+
+ val platform = FlowEngine(coroutineContext, clock)
+ val machine = SimBareMetalMachine(
+ platform, model, SimplePowerDriver(ConstantPowerModel(0.0))
+ )
+ val hypervisor = SimFairShareHypervisor(platform, null, null, null)
+
+ launch {
+ machine.run(hypervisor)
+ }
+
+ yield()
+ coroutineScope {
+ launch {
+ val vm = hypervisor.createMachine(model)
+ vm.run(workloadA)
+ vm.close()
+ }
+ val vm = hypervisor.createMachine(model)
+ vm.run(workloadB)
+ vm.close()
+ }
+ yield()
+ machine.close()
+ yield()
+
+ assertAll(
+ { assertEquals(329250, hypervisor.counters.cpuActiveTime, "Active time does not match") },
+ { assertEquals(870750, hypervisor.counters.cpuIdleTime, "Idle time does not match") },
+ { assertEquals(318750, hypervisor.counters.cpuStealTime, "Steal time does not match") },
+ { assertEquals(1200000, clock.millis()) }
+ )
+ }
+
+ @Test
+ fun testMultipleCPUs() = runBlockingSimulation {
+ val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2)
+ val model = MachineModel(
+ cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) },
+ memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }
+ )
+
+ val platform = FlowEngine(coroutineContext, clock)
+ val machine = SimBareMetalMachine(platform, model, SimplePowerDriver(ConstantPowerModel(0.0)))
+ val hypervisor = SimFairShareHypervisor(platform, null, null, null)
+
+ assertDoesNotThrow {
+ launch {
+ machine.run(hypervisor)
+ }
+ }
+
+ machine.close()
+ }
+
+ @Test
+ fun testInterference() = runBlockingSimulation {
+ val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2)
+ val model = MachineModel(
+ cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) },
+ memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }
+ )
+
+ val groups = listOf(
+ VmInterferenceGroup(targetLoad = 0.0, score = 0.9, members = setOf("a", "b")),
+ VmInterferenceGroup(targetLoad = 0.0, score = 0.6, members = setOf("a", "c")),
+ VmInterferenceGroup(targetLoad = 0.1, score = 0.8, members = setOf("a", "n"))
+ )
+ val interferenceModel = VmInterferenceModel(groups)
+
+ val platform = FlowEngine(coroutineContext, clock)
+ val machine = SimBareMetalMachine(
+ platform, model, SimplePowerDriver(ConstantPowerModel(0.0))
+ )
+ val hypervisor = SimFairShareHypervisor(platform, null, null, interferenceModel.newDomain())
+
+ val duration = 5 * 60L
+ val workloadA =
+ SimTraceWorkload(
+ SimTrace.ofFragments(
+ SimTraceFragment(0, duration * 1000, 0.0, 1),
+ SimTraceFragment(duration * 1000, duration * 1000, 28.0, 1),
+ SimTraceFragment(duration * 2000, duration * 1000, 3500.0, 1),
+ SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1)
+ ),
+ )
+ val workloadB =
+ SimTraceWorkload(
+ SimTrace.ofFragments(
+ SimTraceFragment(0, duration * 1000, 0.0, 1),
+ SimTraceFragment(duration * 1000, duration * 1000, 28.0, 1),
+ SimTraceFragment(duration * 2000, duration * 1000, 3100.0, 1),
+ SimTraceFragment(duration * 3000, duration * 1000, 73.0, 1)
+ )
+ )
+
+ launch {
+ machine.run(hypervisor)
+ }
+
+ coroutineScope {
+ launch {
+ val vm = hypervisor.createMachine(model, "a")
+ vm.run(workloadA)
+ vm.close()
+ }
+ val vm = hypervisor.createMachine(model, "b")
+ vm.run(workloadB)
+ vm.close()
+ }
+
+ machine.close()
+ }
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimSpaceSharedHypervisorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt
index ef6f536d..02d308ff 100644
--- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimSpaceSharedHypervisorTest.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt
@@ -20,38 +20,37 @@
* SOFTWARE.
*/
-package org.opendc.simulator.compute
+package org.opendc.simulator.compute.kernel
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
-import org.opendc.simulator.compute.cpufreq.PerformanceScalingGovernor
-import org.opendc.simulator.compute.cpufreq.SimpleScalingDriver
+import org.opendc.simulator.compute.SimBareMetalMachine
+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.ConstantPowerModel
-import org.opendc.simulator.compute.workload.SimFlopsWorkload
-import org.opendc.simulator.compute.workload.SimRuntimeWorkload
-import org.opendc.simulator.compute.workload.SimTraceWorkload
+import org.opendc.simulator.compute.power.SimplePowerDriver
+import org.opendc.simulator.compute.workload.*
import org.opendc.simulator.core.runBlockingSimulation
+import org.opendc.simulator.flow.FlowEngine
/**
* A test suite for the [SimSpaceSharedHypervisor].
*/
@OptIn(ExperimentalCoroutinesApi::class)
internal class SimSpaceSharedHypervisorTest {
- private lateinit var machineModel: SimMachineModel
+ private lateinit var machineModel: MachineModel
@BeforeEach
fun setUp() {
val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 1)
- machineModel = SimMachineModel(
+ machineModel = MachineModel(
cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) },
memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }
)
@@ -62,47 +61,30 @@ internal class SimSpaceSharedHypervisorTest {
*/
@Test
fun testTrace() = runBlockingSimulation {
- val usagePm = mutableListOf<Double>()
- val usageVm = mutableListOf<Double>()
-
val duration = 5 * 60L
val workloadA =
SimTraceWorkload(
- sequenceOf(
- SimTraceWorkload.Fragment(duration * 1000, 28.0, 1),
- SimTraceWorkload.Fragment(duration * 1000, 3500.0, 1),
- SimTraceWorkload.Fragment(duration * 1000, 0.0, 1),
- SimTraceWorkload.Fragment(duration * 1000, 183.0, 1)
+ SimTrace.ofFragments(
+ SimTraceFragment(0, duration * 1000, 28.0, 1),
+ SimTraceFragment(duration * 1000, duration * 1000, 3500.0, 1),
+ SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1),
+ SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1)
),
)
- val machine = SimBareMetalMachine(
- coroutineContext, clock, machineModel, PerformanceScalingGovernor(),
- SimpleScalingDriver(ConstantPowerModel(0.0))
- )
- val hypervisor = SimSpaceSharedHypervisor()
+ val engine = FlowEngine(coroutineContext, clock)
+ val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0)))
+ val hypervisor = SimSpaceSharedHypervisor(engine, null, null)
- val colA = launch { machine.usage.toList(usagePm) }
launch { machine.run(hypervisor) }
-
- yield()
-
val vm = hypervisor.createMachine(machineModel)
- val colB = launch { vm.usage.toList(usageVm) }
vm.run(workloadA)
yield()
vm.close()
machine.close()
- colA.cancel()
- colB.cancel()
- assertAll(
- { assertEquals(listOf(0.0, 0.00875, 1.0, 0.0, 0.0571875, 0.0), usagePm) { "Correct PM usage" } },
- // Temporary limitation is that VMs do not emit usage information
- // { assertEquals(listOf(0.0, 0.00875, 1.0, 0.0, 0.0571875, 0.0), usageVm) { "Correct VM usage" } },
- { assertEquals(5 * 60L * 4000, clock.millis()) { "Took enough time" } }
- )
+ assertEquals(5 * 60L * 4000, clock.millis()) { "Took enough time" }
}
/**
@@ -112,11 +94,9 @@ internal class SimSpaceSharedHypervisorTest {
fun testRuntimeWorkload() = runBlockingSimulation {
val duration = 5 * 60L * 1000
val workload = SimRuntimeWorkload(duration)
- val machine = SimBareMetalMachine(
- coroutineContext, clock, machineModel, PerformanceScalingGovernor(),
- SimpleScalingDriver(ConstantPowerModel(0.0))
- )
- val hypervisor = SimSpaceSharedHypervisor()
+ val engine = FlowEngine(coroutineContext, clock)
+ val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0)))
+ val hypervisor = SimSpaceSharedHypervisor(engine, null, null)
launch { machine.run(hypervisor) }
yield()
@@ -135,11 +115,11 @@ internal class SimSpaceSharedHypervisorTest {
fun testFlopsWorkload() = runBlockingSimulation {
val duration = 5 * 60L * 1000
val workload = SimFlopsWorkload((duration * 3.2).toLong(), 1.0)
+ val engine = FlowEngine(coroutineContext, clock)
val machine = SimBareMetalMachine(
- coroutineContext, clock, machineModel, PerformanceScalingGovernor(),
- SimpleScalingDriver(ConstantPowerModel(0.0))
+ engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))
)
- val hypervisor = SimSpaceSharedHypervisor()
+ val hypervisor = SimSpaceSharedHypervisor(engine, null, null)
launch { machine.run(hypervisor) }
yield()
@@ -156,11 +136,11 @@ internal class SimSpaceSharedHypervisorTest {
@Test
fun testTwoWorkloads() = runBlockingSimulation {
val duration = 5 * 60L * 1000
+ val engine = FlowEngine(coroutineContext, clock)
val machine = SimBareMetalMachine(
- coroutineContext, clock, machineModel, PerformanceScalingGovernor(),
- SimpleScalingDriver(ConstantPowerModel(0.0))
+ engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))
)
- val hypervisor = SimSpaceSharedHypervisor()
+ val hypervisor = SimSpaceSharedHypervisor(engine, null, null)
launch { machine.run(hypervisor) }
yield()
@@ -169,6 +149,8 @@ internal class SimSpaceSharedHypervisorTest {
vm.run(SimRuntimeWorkload(duration))
vm.close()
+ yield()
+
val vm2 = hypervisor.createMachine(machineModel)
vm2.run(SimRuntimeWorkload(duration))
vm2.close()
@@ -182,11 +164,9 @@ internal class SimSpaceSharedHypervisorTest {
*/
@Test
fun testConcurrentWorkloadFails() = runBlockingSimulation {
- val machine = SimBareMetalMachine(
- coroutineContext, clock, machineModel, PerformanceScalingGovernor(),
- SimpleScalingDriver(ConstantPowerModel(0.0))
- )
- val hypervisor = SimSpaceSharedHypervisor()
+ val engine = FlowEngine(coroutineContext, clock)
+ val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0)))
+ val hypervisor = SimSpaceSharedHypervisor(engine, null, null)
launch { machine.run(hypervisor) }
yield()
@@ -206,11 +186,11 @@ internal class SimSpaceSharedHypervisorTest {
*/
@Test
fun testConcurrentWorkloadSucceeds() = runBlockingSimulation {
+ val interpreter = FlowEngine(coroutineContext, clock)
val machine = SimBareMetalMachine(
- coroutineContext, clock, machineModel, PerformanceScalingGovernor(),
- SimpleScalingDriver(ConstantPowerModel(0.0))
+ interpreter, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))
)
- val hypervisor = SimSpaceSharedHypervisor()
+ val hypervisor = SimSpaceSharedHypervisor(interpreter, null, null)
launch { machine.run(hypervisor) }
yield()
diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernorTest.kt
new file mode 100644
index 00000000..ef354569
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernorTest.kt
@@ -0,0 +1,98 @@
+/*
+ * 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.simulator.compute.kernel.cpufreq
+
+import io.mockk.every
+import io.mockk.mockk
+import io.mockk.verify
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
+
+/**
+ * Test suite for the [ConservativeScalingGovernor]
+ */
+internal class ConservativeScalingGovernorTest {
+ @Test
+ fun testSetStartLimitWithoutPStates() {
+ val cpuCapacity = 4100.0
+ val minSpeed = cpuCapacity / 2
+ val defaultThreshold = 0.8
+ val defaultStepSize = 0.05 * cpuCapacity
+ val governor = ConservativeScalingGovernor()
+
+ val policy = mockk<ScalingPolicy>(relaxUnitFun = true)
+ every { policy.max } returns cpuCapacity
+ every { policy.min } returns minSpeed
+
+ var target = 0.0
+ every { policy.target } answers { target }
+ every { policy.target = any() } propertyType Double::class answers { target = value }
+
+ val logic = governor.createLogic(policy)
+ logic.onStart()
+ assertEquals(defaultThreshold, governor.threshold)
+
+ logic.onLimit(0.5)
+
+ /* Upwards scaling */
+ logic.onLimit(defaultThreshold + 0.2)
+
+ /* Downwards scaling */
+ logic.onLimit(defaultThreshold + 0.1)
+
+ verify(exactly = 2) { policy.target = minSpeed }
+ verify(exactly = 1) { policy.target = minSpeed + defaultStepSize }
+ }
+
+ @Test
+ fun testSetStartLimitWithPStatesAndParams() {
+ val firstPState = 1000.0
+ val cpuCapacity = 4100.0
+ val minSpeed = firstPState
+ val threshold = 0.5
+ val stepSize = 0.02 * cpuCapacity
+ val governor = ConservativeScalingGovernor(threshold, stepSize)
+
+ val policy = mockk<ScalingPolicy>(relaxUnitFun = true)
+ every { policy.max } returns cpuCapacity
+ every { policy.min } returns firstPState
+
+ var target = 0.0
+ every { policy.target } answers { target }
+ every { policy.target = any() } propertyType Double::class answers { target = value }
+
+ val logic = governor.createLogic(policy)
+ logic.onStart()
+ assertEquals(threshold, governor.threshold)
+ logic.onLimit(0.5)
+
+ /* Upwards scaling */
+ logic.onLimit(threshold + 0.2)
+
+ /* Downwards scaling */
+ logic.onLimit(threshold + 0.1)
+
+ verify(exactly = 2) { policy.target = minSpeed }
+ verify(exactly = 1) { policy.target = minSpeed + stepSize }
+ }
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernorTest.kt
new file mode 100644
index 00000000..ca759e39
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernorTest.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.simulator.compute.kernel.cpufreq
+
+import io.mockk.every
+import io.mockk.mockk
+import io.mockk.verify
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
+
+/**
+ * Test suite for the [OnDemandScalingGovernor]
+ */
+internal class OnDemandScalingGovernorTest {
+ @Test
+ fun testSetStartLimitWithoutPStates() {
+ val cpuCapacity = 4100.0
+ val minSpeed = cpuCapacity / 2
+ val defaultThreshold = 0.8
+ val governor = OnDemandScalingGovernor()
+
+ val policy = mockk<ScalingPolicy>(relaxUnitFun = true)
+ every { policy.min } returns minSpeed
+ every { policy.max } returns cpuCapacity
+
+ val logic = governor.createLogic(policy)
+ logic.onStart()
+ assertEquals(defaultThreshold, governor.threshold)
+ verify(exactly = 1) { policy.target = minSpeed }
+
+ logic.onLimit(0.5)
+ verify(exactly = 1) { policy.target = minSpeed + 0.5 * (cpuCapacity - minSpeed) / 100 }
+
+ logic.onLimit(defaultThreshold)
+ verify(exactly = 1) { policy.target = cpuCapacity }
+ }
+
+ @Test
+ fun testSetStartLimitWithPStatesAndParams() {
+ val firstPState = 1000.0
+ val cpuCapacity = 4100.0
+ val threshold = 0.5
+ val governor = OnDemandScalingGovernor(threshold)
+
+ val policy = mockk<ScalingPolicy>(relaxUnitFun = true)
+ every { policy.max } returns cpuCapacity
+ every { policy.min } returns firstPState
+
+ val logic = governor.createLogic(policy)
+
+ logic.onStart()
+ assertEquals(threshold, governor.threshold)
+ verify(exactly = 1) { policy.target = firstPState }
+
+ logic.onLimit(0.1)
+ verify(exactly = 1) { policy.target = firstPState + 0.1 * (cpuCapacity - firstPState) / 100 }
+
+ logic.onLimit(threshold)
+ verify(exactly = 1) { policy.target = cpuCapacity }
+ }
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/cpufreq/DemandScalingGovernorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernorTest.kt
index c482d348..a4bb24f2 100644
--- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/cpufreq/DemandScalingGovernorTest.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernorTest.kt
@@ -20,29 +20,31 @@
* SOFTWARE.
*/
-package org.opendc.simulator.compute.cpufreq
+package org.opendc.simulator.compute.kernel.cpufreq
import io.mockk.every
-import io.mockk.mockk
+import io.mockk.spyk
import io.mockk.verify
import org.junit.jupiter.api.Test
/**
- * Test suite for the [DemandScalingGovernor]
+ * Test suite for the [PerformanceScalingGovernor]
*/
-internal class DemandScalingGovernorTest {
+internal class PerformanceScalingGovernorTest {
@Test
- fun testSetDemandLimit() {
- val ctx = mockk<ScalingContext>(relaxUnitFun = true)
+ fun testSetStartLimit() {
+ val policy = spyk<ScalingPolicy>()
+ val logic = PerformanceScalingGovernor().createLogic(policy)
- every { ctx.cpu.speed } returns 2100.0
-
- val logic = DemandScalingGovernor().createLogic(ctx)
+ every { policy.max } returns 4100.0
logic.onStart()
- verify(exactly = 0) { ctx.setTarget(any()) }
+ verify(exactly = 1) { policy.target = 4100.0 }
+
+ logic.onLimit(0.0)
+ verify(exactly = 1) { policy.target = 4100.0 }
- logic.onLimit()
- verify(exactly = 1) { ctx.setTarget(2100.0) }
+ logic.onLimit(1.0)
+ verify(exactly = 1) { policy.target = 4100.0 }
}
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernorTest.kt
new file mode 100644
index 00000000..662d55fb
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernorTest.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.simulator.compute.kernel.cpufreq
+
+import io.mockk.every
+import io.mockk.mockk
+import io.mockk.verify
+import org.junit.jupiter.api.Test
+
+/**
+ * Test suite for the [PowerSaveScalingGovernor]
+ */
+internal class PowerSaveScalingGovernorTest {
+ @Test
+ fun testSetStartLimitWithoutPStates() {
+ val cpuCapacity = 4100.0
+ val minSpeed = cpuCapacity / 2
+ val policy = mockk<ScalingPolicy>(relaxUnitFun = true)
+ val logic = PowerSaveScalingGovernor().createLogic(policy)
+
+ every { policy.max } returns cpuCapacity
+ every { policy.min } returns minSpeed
+
+ logic.onStart()
+
+ logic.onLimit(0.0)
+ verify(exactly = 1) { policy.target = minSpeed }
+
+ logic.onLimit(1.0)
+ verify(exactly = 1) { policy.target = minSpeed }
+ }
+
+ @Test
+ fun testSetStartLimitWithPStates() {
+ val cpuCapacity = 4100.0
+ val firstPState = 1000.0
+ val policy = mockk<ScalingPolicy>(relaxUnitFun = true)
+ val logic = PowerSaveScalingGovernor().createLogic(policy)
+
+ every { policy.max } returns cpuCapacity
+ every { policy.min } returns firstPState
+
+ logic.onStart()
+ verify(exactly = 1) { policy.target = firstPState }
+
+ logic.onLimit(0.0)
+ verify(exactly = 1) { policy.target = firstPState }
+
+ logic.onLimit(1.0)
+ verify(exactly = 1) { policy.target = firstPState }
+ }
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/cpufreq/PStateScalingDriverTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PStatePowerDriverTest.kt
index bbea3ee2..f557c8d3 100644
--- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/cpufreq/PStateScalingDriverTest.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PStatePowerDriverTest.kt
@@ -20,7 +20,7 @@
* SOFTWARE.
*/
-package org.opendc.simulator.compute.cpufreq
+package org.opendc.simulator.compute.power
import io.mockk.every
import io.mockk.mockk
@@ -28,18 +28,16 @@ import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.opendc.simulator.compute.SimBareMetalMachine
import org.opendc.simulator.compute.SimProcessingUnit
-import org.opendc.simulator.compute.power.ConstantPowerModel
-import org.opendc.simulator.compute.power.LinearPowerModel
/**
- * Test suite for [PStateScalingDriver].
+ * Test suite for [PStatePowerDriver].
*/
-internal class PStateScalingDriverTest {
+internal class PStatePowerDriverTest {
@Test
- fun testPowerWithoutGovernor() {
+ fun testPowerBaseline() {
val machine = mockk<SimBareMetalMachine>()
- val driver = PStateScalingDriver(
+ val driver = PStatePowerDriver(
sortedMapOf(
2800.0 to ConstantPowerModel(200.0),
3300.0 to ConstantPowerModel(300.0),
@@ -47,19 +45,19 @@ internal class PStateScalingDriverTest {
)
)
- val logic = driver.createLogic(machine)
+ val logic = driver.createLogic(machine, emptyList())
assertEquals(200.0, logic.computePower())
}
@Test
- fun testPowerWithSingleGovernor() {
+ fun testPowerWithSingleCpu() {
val machine = mockk<SimBareMetalMachine>()
- val cpu = mockk<SimProcessingUnit>()
+ val cpu = mockk<SimProcessingUnit>(relaxUnitFun = true)
- every { cpu.model.frequency } returns 4100.0
- every { cpu.speed } returns 1200.0
+ every { cpu.capacity } returns 3200.0
+ every { cpu.rate } returns 1200.0
- val driver = PStateScalingDriver(
+ val driver = PStatePowerDriver(
sortedMapOf(
2800.0 to ConstantPowerModel(200.0),
3300.0 to ConstantPowerModel(300.0),
@@ -67,23 +65,24 @@ internal class PStateScalingDriverTest {
)
)
- val logic = driver.createLogic(machine)
-
- val scalingContext = logic.createContext(cpu)
- scalingContext.setTarget(3200.0)
+ val logic = driver.createLogic(machine, listOf(cpu))
assertEquals(300.0, logic.computePower())
}
@Test
- fun testPowerWithMultipleGovernors() {
+ fun testPowerWithMultipleCpus() {
val machine = mockk<SimBareMetalMachine>()
- val cpu = mockk<SimProcessingUnit>()
+ val cpu = mockk<SimProcessingUnit>(relaxUnitFun = true)
+ val cpus = listOf(cpu, cpu)
+
+ every { cpus[0].capacity } returns 1000.0
+ every { cpus[0].rate } returns 1200.0
- every { cpu.model.frequency } returns 4100.0
- every { cpu.speed } returns 1200.0
+ every { cpus[1].capacity } returns 3500.0
+ every { cpus[1].rate } returns 1200.0
- val driver = PStateScalingDriver(
+ val driver = PStatePowerDriver(
sortedMapOf(
2800.0 to ConstantPowerModel(200.0),
3300.0 to ConstantPowerModel(300.0),
@@ -91,13 +90,7 @@ internal class PStateScalingDriverTest {
)
)
- val logic = driver.createLogic(machine)
-
- val scalingContextA = logic.createContext(cpu)
- scalingContextA.setTarget(1000.0)
-
- val scalingContextB = logic.createContext(cpu)
- scalingContextB.setTarget(3400.0)
+ val logic = driver.createLogic(machine, cpus)
assertEquals(350.0, logic.computePower())
}
@@ -105,11 +98,11 @@ internal class PStateScalingDriverTest {
@Test
fun testPowerBasedOnUtilization() {
val machine = mockk<SimBareMetalMachine>()
- val cpu = mockk<SimProcessingUnit>()
+ val cpu = mockk<SimProcessingUnit>(relaxUnitFun = true)
every { cpu.model.frequency } returns 4200.0
- val driver = PStateScalingDriver(
+ val driver = PStatePowerDriver(
sortedMapOf(
2800.0 to LinearPowerModel(200.0, 100.0),
3300.0 to LinearPowerModel(250.0, 150.0),
@@ -117,16 +110,14 @@ internal class PStateScalingDriverTest {
)
)
- val logic = driver.createLogic(machine)
-
- val scalingContext = logic.createContext(cpu)
+ val logic = driver.createLogic(machine, listOf(cpu))
- every { cpu.speed } returns 1400.0
- scalingContext.setTarget(1400.0)
+ every { cpu.rate } returns 1400.0
+ every { cpu.capacity } returns 1400.0
assertEquals(150.0, logic.computePower())
- every { cpu.speed } returns 1400.0
- scalingContext.setTarget(4000.0)
+ every { cpu.rate } returns 1400.0
+ every { cpu.capacity } returns 4000.0
assertEquals(235.0, logic.computePower())
}
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PowerModelTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PowerModelTest.kt
index dd93302b..7852534a 100644
--- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PowerModelTest.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PowerModelTest.kt
@@ -1,3 +1,25 @@
+/*
+ * 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.simulator.compute.power
import org.junit.jupiter.api.Assertions.assertAll
@@ -39,7 +61,8 @@ internal class PowerModelTest {
@Test
fun `compute power draw by the SPEC benchmark model`() {
- val powerModel = InterpolationPowerModel("IBMx3550M3_XeonX5675")
+ val ibm = listOf(58.4, 98.0, 109.0, 118.0, 128.0, 140.0, 153.0, 170.0, 189.0, 205.0, 222.0)
+ val powerModel = InterpolationPowerModel(ibm)
assertAll(
{ assertEquals(58.4, powerModel.computePower(0.0)) },
diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt
new file mode 100644
index 00000000..574860e8
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt
@@ -0,0 +1,160 @@
+/*
+ * 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.simulator.compute.workload
+
+import kotlinx.coroutines.delay
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import org.opendc.simulator.compute.SimBareMetalMachine
+import org.opendc.simulator.compute.model.*
+import org.opendc.simulator.compute.power.ConstantPowerModel
+import org.opendc.simulator.compute.power.SimplePowerDriver
+import org.opendc.simulator.core.runBlockingSimulation
+import org.opendc.simulator.flow.FlowEngine
+
+/**
+ * Test suite for the [SimTraceWorkloadTest] class.
+ */
+class SimTraceWorkloadTest {
+ private lateinit var machineModel: MachineModel
+
+ @BeforeEach
+ fun setUp() {
+ val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2)
+
+ machineModel = MachineModel(
+ cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) },
+ memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }
+ )
+ }
+
+ @Test
+ fun testSmoke() = runBlockingSimulation {
+ val machine = SimBareMetalMachine(
+ FlowEngine(coroutineContext, clock),
+ machineModel,
+ SimplePowerDriver(ConstantPowerModel(0.0))
+ )
+
+ val workload = SimTraceWorkload(
+ SimTrace.ofFragments(
+ SimTraceFragment(0, 1000, 2 * 28.0, 2),
+ SimTraceFragment(1000, 1000, 2 * 3100.0, 2),
+ SimTraceFragment(2000, 1000, 0.0, 2),
+ SimTraceFragment(3000, 1000, 2 * 73.0, 2)
+ ),
+ offset = 0
+ )
+
+ try {
+ machine.run(workload)
+
+ assertEquals(4000, clock.millis())
+ } finally {
+ machine.close()
+ }
+ }
+
+ @Test
+ fun testOffset() = runBlockingSimulation {
+ val machine = SimBareMetalMachine(
+ FlowEngine(coroutineContext, clock),
+ machineModel,
+ SimplePowerDriver(ConstantPowerModel(0.0))
+ )
+
+ val workload = SimTraceWorkload(
+ SimTrace.ofFragments(
+ SimTraceFragment(0, 1000, 2 * 28.0, 2),
+ SimTraceFragment(1000, 1000, 2 * 3100.0, 2),
+ SimTraceFragment(2000, 1000, 0.0, 2),
+ SimTraceFragment(3000, 1000, 2 * 73.0, 2)
+ ),
+ offset = 1000
+ )
+
+ try {
+ machine.run(workload)
+
+ assertEquals(5000, clock.millis())
+ } finally {
+ machine.close()
+ }
+ }
+
+ @Test
+ fun testSkipFragment() = runBlockingSimulation {
+ val machine = SimBareMetalMachine(
+ FlowEngine(coroutineContext, clock),
+ machineModel,
+ SimplePowerDriver(ConstantPowerModel(0.0))
+ )
+
+ val workload = SimTraceWorkload(
+ SimTrace.ofFragments(
+ SimTraceFragment(0, 1000, 2 * 28.0, 2),
+ SimTraceFragment(1000, 1000, 2 * 3100.0, 2),
+ SimTraceFragment(2000, 1000, 0.0, 2),
+ SimTraceFragment(3000, 1000, 2 * 73.0, 2)
+ ),
+ offset = 0
+ )
+
+ try {
+ delay(1000L)
+ machine.run(workload)
+
+ assertEquals(4000, clock.millis())
+ } finally {
+ machine.close()
+ }
+ }
+
+ @Test
+ fun testZeroCores() = runBlockingSimulation {
+ val machine = SimBareMetalMachine(
+ FlowEngine(coroutineContext, clock),
+ machineModel,
+ SimplePowerDriver(ConstantPowerModel(0.0))
+ )
+
+ val workload = SimTraceWorkload(
+ SimTrace.ofFragments(
+ SimTraceFragment(0, 1000, 2 * 28.0, 2),
+ SimTraceFragment(1000, 1000, 2 * 3100.0, 2),
+ SimTraceFragment(2000, 1000, 0.0, 0),
+ SimTraceFragment(3000, 1000, 2 * 73.0, 2)
+ ),
+ offset = 0
+ )
+
+ try {
+ machine.run(workload)
+
+ assertEquals(4000, clock.millis())
+ } finally {
+ machine.close()
+ }
+ }
+}