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.kt28
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractHypervisor.kt150
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractMachine.kt143
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt110
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimFairShareHypervisor.kt71
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimFairShareHypervisorProvider.kt13
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimHypervisorProvider.kt10
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachineContext.kt13
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimProcessingUnit.kt8
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimSpaceSharedHypervisor.kt6
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimSpaceSharedHypervisorProvider.kt12
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/DemandScalingGovernor.kt36
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/PerformanceScalingGovernor.kt8
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/ScalingGovernor.kt10
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/SimpleScalingDriver.kt49
-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)44
-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)13
-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/ScalingContext.kt)25
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/util/SimWorkloadLifecycle.kt76
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkload.kt12
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimRuntimeWorkload.kt14
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt55
-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/test/kotlin/org/opendc/simulator/compute/SimHypervisorTest.kt43
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt30
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimSpaceSharedHypervisorTest.kt40
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/cpufreq/PerformanceScalingGovernorTest.kt (renamed from opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/cpufreq/DemandScalingGovernorTest.kt)19
-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)59
28 files changed, 551 insertions, 555 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..fb753de2 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,17 +25,15 @@ 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.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.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.resources.SimResourceInterpreter
import org.openjdk.jmh.annotations.*
import java.util.concurrent.TimeUnit
@@ -46,13 +44,13 @@ import java.util.concurrent.TimeUnit
@OptIn(ExperimentalCoroutinesApi::class)
class SimMachineBenchmarks {
private lateinit var scope: SimulationCoroutineScope
- private lateinit var scheduler: SimResourceScheduler
+ private lateinit var interpreter: SimResourceInterpreter
private lateinit var machineModel: SimMachineModel
@Setup
fun setUp() {
scope = SimulationCoroutineScope()
- scheduler = SimResourceSchedulerTrampoline(scope.coroutineContext, scope.clock)
+ interpreter = SimResourceInterpreter(scope.coroutineContext, scope.clock)
val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2)
@@ -85,8 +83,7 @@ class SimMachineBenchmarks {
fun benchmarkBareMetal(state: Workload) {
return scope.runBlockingSimulation {
val machine = SimBareMetalMachine(
- coroutineContext, clock, machineModel, PerformanceScalingGovernor(),
- SimpleScalingDriver(ConstantPowerModel(0.0))
+ interpreter, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))
)
return@runBlockingSimulation machine.run(SimTraceWorkload(state.trace))
}
@@ -96,10 +93,9 @@ class SimMachineBenchmarks {
fun benchmarkSpaceSharedHypervisor(state: Workload) {
return scope.runBlockingSimulation {
val machine = SimBareMetalMachine(
- coroutineContext, clock, machineModel, PerformanceScalingGovernor(),
- SimpleScalingDriver(ConstantPowerModel(0.0))
+ interpreter, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))
)
- val hypervisor = SimSpaceSharedHypervisor()
+ val hypervisor = SimSpaceSharedHypervisor(interpreter)
launch { machine.run(hypervisor) }
@@ -118,10 +114,9 @@ class SimMachineBenchmarks {
fun benchmarkFairShareHypervisorSingle(state: Workload) {
return scope.runBlockingSimulation {
val machine = SimBareMetalMachine(
- coroutineContext, clock, machineModel, PerformanceScalingGovernor(),
- SimpleScalingDriver(ConstantPowerModel(0.0))
+ interpreter, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))
)
- val hypervisor = SimFairShareHypervisor(scheduler)
+ val hypervisor = SimFairShareHypervisor(interpreter)
launch { machine.run(hypervisor) }
@@ -140,10 +135,9 @@ class SimMachineBenchmarks {
fun benchmarkFairShareHypervisorDouble(state: Workload) {
return scope.runBlockingSimulation {
val machine = SimBareMetalMachine(
- coroutineContext, clock, machineModel, PerformanceScalingGovernor(),
- SimpleScalingDriver(ConstantPowerModel(0.0))
+ interpreter, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))
)
- val hypervisor = SimFairShareHypervisor(scheduler)
+ val hypervisor = SimFairShareHypervisor(interpreter)
launch { machine.run(hypervisor) }
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
index 713376e7..57c25b86 100644
--- 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
@@ -22,21 +22,22 @@
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.cpufreq.ScalingGovernor
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
+import org.opendc.simulator.resources.SimResourceSwitch
/**
* Abstract implementation of the [SimHypervisor] interface.
+ *
+ * @param interpreter The resource interpreter to use.
+ * @param scalingGovernor The scaling governor to use for scaling the CPU frequency of the underlying hardware.
*/
-public abstract class SimAbstractHypervisor : SimHypervisor {
+public abstract class SimAbstractHypervisor(
+ private val interpreter: SimResourceInterpreter,
+ private val scalingGovernor: ScalingGovernor?
+) : SimHypervisor {
/**
* The machine on which the hypervisor runs.
*/
@@ -55,6 +56,11 @@ public abstract class SimAbstractHypervisor : SimHypervisor {
get() = _vms
/**
+ * The scaling governors attached to the physical CPUs backing this hypervisor.
+ */
+ private val governors = mutableListOf<ScalingGovernor.Logic>()
+
+ /**
* Construct the [SimResourceSwitch] implementation that performs the actual scheduling of the CPUs.
*/
public abstract fun createSwitch(ctx: SimMachineContext): SimResourceSwitch
@@ -64,6 +70,16 @@ public abstract class SimAbstractHypervisor : SimHypervisor {
*/
public abstract fun canFit(model: SimMachineModel, switch: SimResourceSwitch): Boolean
+ /**
+ * Trigger the governors to recompute the scaling limits.
+ */
+ protected fun triggerGovernors(load: Double) {
+ for (governor in governors) {
+ governor.onLimit(load)
+ }
+ }
+
+ /* SimHypervisor */
override fun canFit(model: SimMachineModel): Boolean {
return canFit(model, switch)
}
@@ -78,6 +94,21 @@ public abstract class SimAbstractHypervisor : SimHypervisor {
return vm
}
+ /* SimWorkload */
+ override fun onStart(ctx: SimMachineContext) {
+ context = ctx
+ switch = createSwitch(ctx)
+
+ for (cpu in ctx.cpus) {
+ val governor = scalingGovernor?.createLogic(cpu)
+ if (governor != null) {
+ governors.add(governor)
+ governor.onStart()
+ }
+ switch.addInput(cpu)
+ }
+ }
+
/**
* A virtual machine running on the hypervisor.
*
@@ -85,105 +116,34 @@ public abstract class SimAbstractHypervisor : SimHypervisor {
* @property performanceInterferenceModel The performance interference model to utilize.
*/
private inner class VirtualMachine(
- override val model: SimMachineModel,
+ 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
-
+ ) : SimAbstractMachine(interpreter, parent = null, model) {
/**
* 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 cpus = model.cpus.map { VCpu(switch.newOutput(), it) }
- 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
+ super.close()
- cpus.forEach(SimProcessingUnit::close)
- _vms.remove(this)
- }
+ _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.
+ * A [SimProcessingUnit] of a virtual 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()
- }
+ private class VCpu(
+ private val source: SimResourceProvider,
+ override val model: ProcessingUnit
+ ) : SimProcessingUnit, SimResourceProvider by source {
+ override var capacity: Double
+ get() = source.capacity
+ set(_) {
+ // Ignore capacity changes
+ }
- override fun close() {
- source.close()
- }
+ override fun toString(): String = "SimAbstractHypervisor.VCpu[model=$model]"
}
}
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 e501033a..e12ac72b 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
@@ -27,17 +27,27 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import org.opendc.simulator.compute.model.MemoryUnit
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.resources.*
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.resume
/**
* Abstract implementation of the [SimMachine] interface.
+ *
+ * @param interpreter The interpreter 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 {
+public abstract class SimAbstractMachine(
+ protected val interpreter: SimResourceInterpreter,
+ final override val parent: SimResourceSystem?,
+ final override val model: SimMachineModel
+) : SimMachine, SimResourceSystem {
+ /**
+ * A [StateFlow] representing the CPU usage of the simulated machine.
+ */
private val _usage = MutableStateFlow(0.0)
- override val usage: StateFlow<Double>
+ public final override val usage: StateFlow<Double>
get() = _usage
/**
@@ -48,67 +58,76 @@ public abstract class SimAbstractMachine(private val clock: Clock) : SimMachine
private var _speed = doubleArrayOf()
/**
- * A flag to indicate that the machine is terminated.
- */
- private var isTerminated = false
-
- /**
- * The [CoroutineContext] to run in.
- */
- protected abstract val context: CoroutineContext
-
- /**
* The resources allocated for this machine.
*/
protected abstract val cpus: List<SimProcessingUnit>
/**
- * The execution context in which the workload runs.
+ * A flag to indicate that the machine is terminated.
*/
- private inner class Context(override val meta: Map<String, Any>) : SimMachineContext {
- override val clock: Clock
- get() = this@SimAbstractMachine.clock
-
- override val cpus: List<SimProcessingUnit> = this@SimAbstractMachine.cpus
+ private var isTerminated = false
- override val memory: List<MemoryUnit> = model.memory
- }
+ /**
+ * The continuation to resume when the virtual machine workload has finished.
+ */
+ private var cont: Continuation<Unit>? = null
/**
* 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>): Unit = withContext(context) {
- require(!isTerminated) { "Machine is terminated" }
- val ctx = Context(meta)
- val totalCapacity = model.cpus.sumOf { it.frequency }
+ override suspend fun run(workload: SimWorkload, meta: Map<String, Any>) {
+ check(!isTerminated) { "Machine is terminated" }
+ check(cont == null) { "A machine cannot run concurrently" }
- _speed = DoubleArray(model.cpus.size) { 0.0 }
- var totalSpeed = 0.0
+ val ctx = Context(meta)
// Before the workload starts, initialize the initial power draw
+ _speed = DoubleArray(model.cpus.size) { 0.0 }
updateUsage(0.0)
- workload.onStart(ctx)
+ return suspendCancellableCoroutine { cont ->
+ this.cont = cont
- 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
-
- val oldSpeed = _speed[model.id]
- _speed[model.id] = newSpeed
- totalSpeed = totalSpeed - oldSpeed + newSpeed
-
- val newUsage = totalSpeed / totalCapacity
- if (_usage.value != newUsage) {
- updateUsage(totalSpeed / totalCapacity)
+ // Cancel all cpus on cancellation
+ cont.invokeOnCancellation {
+ this.cont = null
+
+ interpreter.batch {
+ for (cpu in cpus) {
+ cpu.cancel()
+ }
}
}
- launch { cpu.consume(adapter) }
+ interpreter.batch { workload.onStart(ctx) }
+ }
+ }
+
+ override fun close() {
+ if (isTerminated) {
+ return
+ }
+
+ isTerminated = true
+ cancel()
+ interpreter.batch {
+ for (cpu in cpus) {
+ cpu.close()
+ }
+ }
+ }
+
+ /* SimResourceSystem */
+ override fun onConverge(timestamp: Long) {
+ val totalCapacity = model.cpus.sumOf { it.frequency }
+ val cpus = cpus
+ var totalSpeed = 0.0
+ for (cpu in cpus) {
+ _speed[cpu.model.id] = cpu.speed
+ totalSpeed += cpu.speed
}
+
+ updateUsage(totalSpeed / totalCapacity)
}
/**
@@ -118,10 +137,34 @@ public abstract class SimAbstractMachine(private val clock: Clock) : SimMachine
_usage.value = usage
}
- override fun close() {
- if (!isTerminated) {
- isTerminated = true
- cpus.forEach(SimProcessingUnit::close)
+ /**
+ * Cancel the workload that is currently running on the machine.
+ */
+ private fun cancel() {
+ interpreter.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 interpreter: SimResourceInterpreter
+ get() = this@SimAbstractMachine.interpreter
+
+ override val cpus: List<SimProcessingUnit> = this@SimAbstractMachine.cpus
+
+ override val memory: List<MemoryUnit> = model.memory
+
+ override fun close() = cancel()
+ }
}
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..5d5d1e5a 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,62 +22,36 @@
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.model.ProcessingUnit
+import org.opendc.simulator.compute.power.PowerDriver
import org.opendc.simulator.resources.*
-import org.opendc.utils.TimerScheduler
-import java.time.Clock
-import kotlin.coroutines.*
+import org.opendc.simulator.resources.SimResourceInterpreter
/**
* 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].
+ * 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 interpreter The [SimResourceInterpreter] to drive the simulation.
* @param model The machine model to simulate.
+ * @param powerDriver The power driver to use.
+ * @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) {
- /**
- * The [Job] associated with this machine.
- */
- 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) }
+ interpreter: SimResourceInterpreter,
+ model: SimMachineModel,
+ powerDriver: PowerDriver,
+ parent: SimResourceSystem? = null,
+) : SimAbstractMachine(interpreter, parent, model) {
+ override val cpus: List<SimProcessingUnit> = model.cpus.map { cpu ->
+ Cpu(SimResourceSource(cpu.frequency, interpreter, this@SimBareMetalMachine), cpu)
+ }
/**
- * Construct the [ScalingDriver.Logic] for this machine.
+ * Construct the [PowerDriver.Logic] for this machine.
*/
- private val scalingDriver = scalingDriver.createLogic(this)
-
- /**
- * The scaling contexts associated with each CPU.
- */
- private val scalingGovernors = cpus.map { cpu ->
- scalingGovernor.createLogic(this.scalingDriver.createContext(cpu))
- }
-
- init {
- scalingGovernors.forEach { it.onStart() }
- }
+ private val powerDriver = powerDriver.createLogic(this, cpus)
/**
* The power draw of the machine.
@@ -88,45 +62,25 @@ public class SimBareMetalMachine(
override fun updateUsage(usage: Double) {
super.updateUsage(usage)
- scalingGovernors.forEach { it.onLimit() }
- powerDraw = scalingDriver.computePower()
- }
-
- override fun close() {
- super.close()
-
- scope.cancel()
+ powerDraw = powerDriver.computePower()
}
/**
- * 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: SimResourceSource,
+ override val model: ProcessingUnit
+ ) : SimProcessingUnit, SimResourceProvider by source {
+ override var capacity: Double
+ get() = source.capacity
+ set(value) {
+ // Clamp the capacity of the CPU between [0.0, maxFreq]
+ if (value >= 0.0 && value <= model.frequency) {
+ source.capacity = value
+ }
+ }
+
+ 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
index 11aec2de..e7776c81 100644
--- 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
@@ -22,35 +22,72 @@
package org.opendc.simulator.compute
+import org.opendc.simulator.compute.cpufreq.ScalingGovernor
import org.opendc.simulator.compute.workload.SimWorkload
-import org.opendc.simulator.resources.*
+import org.opendc.simulator.resources.SimResourceInterpreter
+import org.opendc.simulator.resources.SimResourceSwitch
+import org.opendc.simulator.resources.SimResourceSwitchMaxMin
+import org.opendc.simulator.resources.SimResourceSystem
/**
* A [SimHypervisor] that distributes the computing requirements of multiple [SimWorkload] on a single
* [SimBareMetalMachine] concurrently using weighted fair sharing.
*
+ * @param interpreter The interpreter to manage the machine's resources.
+ * @param parent The parent simulation system.
+ * @param scalingGovernor The CPU frequency scaling governor to use for the hypervisor.
* @param listener The hypervisor listener to use.
*/
-public class SimFairShareHypervisor(private val scheduler: SimResourceScheduler, private val listener: SimHypervisor.Listener? = null) : SimAbstractHypervisor() {
+public class SimFairShareHypervisor(
+ private val interpreter: SimResourceInterpreter,
+ private val parent: SimResourceSystem? = null,
+ scalingGovernor: ScalingGovernor? = null,
+ private val listener: SimHypervisor.Listener? = null
+) : SimAbstractHypervisor(interpreter, scalingGovernor) {
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)
- }
+ return SwitchSystem(ctx).switch
+ }
+
+ private inner class SwitchSystem(private val ctx: SimMachineContext) : SimResourceSystem {
+ val switch = SimResourceSwitchMaxMin(interpreter, this)
+
+ override val parent: SimResourceSystem? = this@SimFairShareHypervisor.parent
+
+ private var lastCpuUsage = 0.0
+ private var lastCpuDemand = 0.0
+ private var lastDemand = 0.0
+ private var lastActual = 0.0
+ private var lastOvercommit = 0.0
+ private var lastReport = Long.MIN_VALUE
+
+ override fun onConverge(timestamp: Long) {
+ val listener = listener ?: return
+ val counters = switch.counters
+
+ if (timestamp > lastReport) {
+ listener.onSliceFinish(
+ this@SimFairShareHypervisor,
+ (counters.demand - lastDemand).toLong(),
+ (counters.actual - lastActual).toLong(),
+ (counters.overcommit - lastOvercommit).toLong(),
+ 0L,
+ lastCpuUsage,
+ lastCpuDemand
+ )
}
- )
+ lastReport = timestamp
+
+ lastCpuDemand = switch.inputs.sumOf { it.demand }
+ lastCpuUsage = switch.inputs.sumOf { it.speed }
+ lastDemand = counters.demand
+ lastActual = counters.actual
+ lastOvercommit = counters.overcommit
+
+ val load = lastCpuDemand / ctx.cpus.sumOf { it.model.frequency }
+ triggerGovernors(load)
+ }
}
}
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/SimFairShareHypervisorProvider.kt
index 2ab3ea09..94c905b2 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/SimFairShareHypervisorProvider.kt
@@ -22,9 +22,8 @@
package org.opendc.simulator.compute
-import org.opendc.simulator.resources.SimResourceSchedulerTrampoline
-import java.time.Clock
-import kotlin.coroutines.CoroutineContext
+import org.opendc.simulator.resources.SimResourceInterpreter
+import org.opendc.simulator.resources.SimResourceSystem
/**
* A [SimHypervisorProvider] for the [SimFairShareHypervisor] implementation.
@@ -32,7 +31,9 @@ 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(
+ interpreter: SimResourceInterpreter,
+ parent: SimResourceSystem?,
+ listener: SimHypervisor.Listener?
+ ): SimHypervisor = SimFairShareHypervisor(interpreter, parent, listener = listener)
}
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/SimHypervisorProvider.kt
index b66020f4..8e8c3698 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/SimHypervisorProvider.kt
@@ -22,8 +22,8 @@
package org.opendc.simulator.compute
-import java.time.Clock
-import kotlin.coroutines.CoroutineContext
+import org.opendc.simulator.resources.SimResourceInterpreter
+import org.opendc.simulator.resources.SimResourceSystem
/**
* A service provider interface for constructing a [SimHypervisor].
@@ -40,5 +40,9 @@ public interface SimHypervisorProvider {
/**
* Create a [SimHypervisor] instance with the specified [listener].
*/
- public fun create(context: CoroutineContext, clock: Clock, listener: SimHypervisor.Listener? = null): SimHypervisor
+ public fun create(
+ interpreter: SimResourceInterpreter,
+ parent: SimResourceSystem? = null,
+ listener: SimHypervisor.Listener? = null
+ ): SimHypervisor
}
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..5cbabc86 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
@@ -23,18 +23,18 @@
package org.opendc.simulator.compute
import org.opendc.simulator.compute.model.MemoryUnit
-import java.time.Clock
+import org.opendc.simulator.resources.SimResourceInterpreter
/**
* 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 resource interpreter that simulates the machine.
*/
- public val clock: Clock
+ public val interpreter: SimResourceInterpreter
/**
* The metadata associated with the context.
@@ -50,4 +50,9 @@ public interface SimMachineContext {
* The memory available on the machine
*/
public val memory: List<MemoryUnit>
+
+ /**
+ * Stop the workload.
+ */
+ public override fun close()
}
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..93c9ddfa 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
@@ -30,12 +30,12 @@ import org.opendc.simulator.resources.SimResourceProvider
*/
public interface SimProcessingUnit : SimResourceProvider {
/**
- * 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/SimSpaceSharedHypervisor.kt
index fd8e546f..f6ae18f7 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/SimSpaceSharedHypervisor.kt
@@ -22,12 +22,14 @@
package org.opendc.simulator.compute
-import org.opendc.simulator.resources.*
+import org.opendc.simulator.resources.SimResourceInterpreter
+import org.opendc.simulator.resources.SimResourceSwitch
+import org.opendc.simulator.resources.SimResourceSwitchExclusive
/**
* A [SimHypervisor] that allocates its sub-resources exclusively for the virtual machine that it hosts.
*/
-public class SimSpaceSharedHypervisor : SimAbstractHypervisor() {
+public class SimSpaceSharedHypervisor(interpreter: SimResourceInterpreter) : SimAbstractHypervisor(interpreter, null) {
override fun canFit(model: SimMachineModel, switch: SimResourceSwitch): Boolean {
return switch.inputs.size - switch.outputs.size >= model.cpus.size
}
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/SimSpaceSharedHypervisorProvider.kt
index 83b924d7..923b5bab 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/SimSpaceSharedHypervisorProvider.kt
@@ -22,8 +22,8 @@
package org.opendc.simulator.compute
-import java.time.Clock
-import kotlin.coroutines.CoroutineContext
+import org.opendc.simulator.resources.SimResourceInterpreter
+import org.opendc.simulator.resources.SimResourceSystem
/**
* A [SimHypervisorProvider] for the [SimSpaceSharedHypervisor] implementation.
@@ -31,7 +31,9 @@ import kotlin.coroutines.CoroutineContext
public class SimSpaceSharedHypervisorProvider : SimHypervisorProvider {
override val id: String = "space-shared"
- override fun create(context: CoroutineContext, clock: Clock, listener: SimHypervisor.Listener?): SimHypervisor {
- return SimSpaceSharedHypervisor()
- }
+ override fun create(
+ interpreter: SimResourceInterpreter,
+ parent: SimResourceSystem?,
+ listener: SimHypervisor.Listener?
+ ): SimHypervisor = SimSpaceSharedHypervisor(interpreter)
}
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/cpufreq/DemandScalingGovernor.kt
deleted file mode 100644
index ddbe1ca0..00000000
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/DemandScalingGovernor.kt
+++ /dev/null
@@ -1,36 +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.cpufreq
-
-/**
- * A CPUFreq [ScalingGovernor] that requests the frequency based on the utilization of the machine.
- */
-public class DemandScalingGovernor : ScalingGovernor {
- override fun createLogic(ctx: ScalingContext): ScalingGovernor.Logic = object : ScalingGovernor.Logic {
- override fun onLimit() {
- ctx.setTarget(ctx.cpu.speed)
- }
-
- override fun toString(): String = "DemandScalingGovernor.Logic"
- }
-}
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/cpufreq/PerformanceScalingGovernor.kt
index 96f8775a..245877be 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/cpufreq/PerformanceScalingGovernor.kt
@@ -22,13 +22,15 @@
package org.opendc.simulator.compute.cpufreq
+import org.opendc.simulator.compute.SimProcessingUnit
+
/**
* 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(cpu: SimProcessingUnit): ScalingGovernor.Logic = object : ScalingGovernor.Logic {
+ override fun onStart() {
+ cpu.capacity = cpu.model.frequency
}
override fun toString(): String = "PerformanceScalingGovernor.Logic"
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/cpufreq/ScalingGovernor.kt
index c9aea580..b7e7ffc6 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/cpufreq/ScalingGovernor.kt
@@ -22,6 +22,8 @@
package org.opendc.simulator.compute.cpufreq
+import org.opendc.simulator.compute.SimProcessingUnit
+
/**
* A [ScalingGovernor] in the CPUFreq subsystem of OpenDC is responsible for scaling the frequency of simulated CPUs
* independent of the particular implementation of the CPU.
@@ -33,9 +35,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 [cpu]
*/
- public fun createLogic(ctx: ScalingContext): Logic
+ public fun createLogic(cpu: SimProcessingUnit): Logic
/**
* The logic of the scaling governor.
@@ -48,7 +50,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/cpufreq/SimpleScalingDriver.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/SimpleScalingDriver.kt
deleted file mode 100644
index cf0bbb28..00000000
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/cpufreq/SimpleScalingDriver.kt
+++ /dev/null
@@ -1,49 +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.cpufreq
-
-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].
- */
-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
-
- override val cpu: SimProcessingUnit = cpu
-
- override fun setTarget(freq: Double) {}
- }
- }
-
- override fun computePower(): Double = model.computePower(machine.usage.value)
-
- override fun toString(): String = "SimpleScalingDriver.Logic"
- }
-}
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..6328c8e4 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
- }
-
+ 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.speed
}
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.Logic"
}
}
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..a1a2b911 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,30 +20,25 @@
* 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 switching the processor to the correct frequency.
*/
-public interface ScalingDriver {
+public interface PowerDriver {
/**
* Create the scaling 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.
*/
public interface Logic {
/**
- * Create the [ScalingContext] for the specified [cpu] instance.
- */
- public fun createContext(cpu: SimProcessingUnit): ScalingContext
-
- /**
* Compute the power consumption of the processor.
*
* @return The power consumption of the processor in W.
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/power/SimplePowerDriver.kt
index 18338079..5c5ceff5 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/power/SimplePowerDriver.kt
@@ -20,27 +20,20 @@
* 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 [ScalingContext] is used to communicate frequency scaling changes between the [ScalingGovernor] and driver.
+ * A [PowerDriver] that computes the power consumption based on a single specified [power model][model].
*/
-public interface ScalingContext {
- /**
- * The machine the processing unit belongs to.
- */
- public val machine: SimMachine
+public class SimplePowerDriver(private val model: PowerModel) : PowerDriver {
+ override fun createLogic(machine: SimMachine, cpus: List<SimProcessingUnit>): PowerDriver.Logic = object : PowerDriver.Logic {
+ override fun computePower(): Double {
+ return model.computePower(machine.usage.value)
+ }
- /**
- * The processing unit associated with this context.
- */
- public val cpu: SimProcessingUnit
-
- /**
- * Target the processor to run at the specified target [frequency][freq].
- */
- public fun setTarget(freq: Double)
+ override fun toString(): String = "SimplePowerDriver.Logic"
+ }
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/util/SimWorkloadLifecycle.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/util/SimWorkloadLifecycle.kt
new file mode 100644
index 00000000..43662d93
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/util/SimWorkloadLifecycle.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.util
+
+import org.opendc.simulator.compute.SimMachineContext
+import org.opendc.simulator.compute.workload.SimWorkload
+import org.opendc.simulator.resources.SimResourceConsumer
+import org.opendc.simulator.resources.SimResourceContext
+import org.opendc.simulator.resources.SimResourceEvent
+
+/**
+ * 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<SimResourceConsumer>()
+
+ /**
+ * Wait for the specified [consumer] to complete before ending the lifecycle of the workload.
+ */
+ public fun waitFor(consumer: SimResourceConsumer): SimResourceConsumer {
+ waiting.add(consumer)
+ return object : SimResourceConsumer by consumer {
+ override fun onEvent(ctx: SimResourceContext, event: SimResourceEvent) {
+ try {
+ consumer.onEvent(ctx, event)
+ } finally {
+ if (event == SimResourceEvent.Exit) {
+ complete(consumer)
+ }
+ }
+ }
+
+ override fun onFailure(ctx: SimResourceContext, cause: Throwable) {
+ try {
+ consumer.onFailure(ctx, cause)
+ } finally {
+ complete(consumer)
+ }
+ }
+
+ override fun toString(): String = "SimWorkloadLifecycle.Consumer[delegate=$consumer]"
+ }
+ }
+
+ /**
+ * Complete the specified [SimResourceConsumer].
+ */
+ private fun complete(consumer: SimResourceConsumer) {
+ if (waiting.remove(consumer) && waiting.isEmpty()) {
+ ctx.close()
+ }
+ }
+}
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..de6832ca 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,8 +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.compute.util.SimWorkloadLifecycle
import org.opendc.simulator.resources.consumer.SimWorkConsumer
/**
@@ -43,10 +42,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(SimWorkConsumer(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..318a6b49 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,8 +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.compute.util.SimWorkloadLifecycle
import org.opendc.simulator.resources.consumer.SimWorkConsumer
/**
@@ -42,11 +41,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(SimWorkConsumer((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/SimTraceWorkload.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt
index ffb332d1..6929f4d2 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
@@ -24,6 +24,7 @@ package org.opendc.simulator.compute.workload
import org.opendc.simulator.compute.SimMachineContext
import org.opendc.simulator.compute.model.ProcessingUnit
+import org.opendc.simulator.compute.util.SimWorkloadLifecycle
import org.opendc.simulator.resources.SimResourceCommand
import org.opendc.simulator.resources.SimResourceConsumer
import org.opendc.simulator.resources.SimResourceContext
@@ -44,33 +45,12 @@ public class SimTraceWorkload(public val trace: Sequence<Fragment>) : SimWorkloa
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 cmd =
- if (cpu.id < fragment.cores && work > 0.0)
- SimResourceCommand.Consume(work, usage, deadline)
- else
- SimResourceCommand.Idle(deadline)
+ offset = ctx.interpreter.clock.millis()
- if (barrier.enter()) {
- this@SimTraceWorkload.fragment = nextFragment()
- this@SimTraceWorkload.offset += fragment.duration
- }
+ val lifecycle = SimWorkloadLifecycle(ctx)
- return cmd
- }
+ for (cpu in ctx.cpus) {
+ cpu.startConsumer(lifecycle.waitFor(Consumer(cpu.model)))
}
}
@@ -87,6 +67,31 @@ public class SimTraceWorkload(public val trace: Sequence<Fragment>) : SimWorkloa
}
}
+ private inner class Consumer(val cpu: ProcessingUnit) : 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 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
+ }
+ }
+
/**
* A fragment of the workload.
*/
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/test/kotlin/org/opendc/simulator/compute/SimHypervisorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimHypervisorTest.kt
index 8886caa7..b15692ec 100644
--- 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
@@ -31,15 +31,16 @@ 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.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.power.SimplePowerDriver
import org.opendc.simulator.compute.workload.SimTraceWorkload
import org.opendc.simulator.core.runBlockingSimulation
-import org.opendc.simulator.resources.SimResourceSchedulerTrampoline
+import org.opendc.simulator.resources.SimResourceInterpreter
/**
* Test suite for the [SimHypervisor] class.
@@ -93,8 +94,9 @@ internal class SimHypervisorTest {
),
)
- val machine = SimBareMetalMachine(coroutineContext, clock, model, PerformanceScalingGovernor(), SimpleScalingDriver(ConstantPowerModel(0.0)))
- val hypervisor = SimFairShareHypervisor(SimResourceSchedulerTrampoline(coroutineContext, clock), listener)
+ val platform = SimResourceInterpreter(coroutineContext, clock)
+ val machine = SimBareMetalMachine(platform, model, SimplePowerDriver(ConstantPowerModel(0.0)))
+ val hypervisor = SimFairShareHypervisor(platform, scalingGovernor = PerformanceScalingGovernor(), listener = listener)
launch {
machine.run(hypervisor)
@@ -164,11 +166,11 @@ internal class SimHypervisorTest {
)
)
+ val platform = SimResourceInterpreter(coroutineContext, clock)
val machine = SimBareMetalMachine(
- coroutineContext, clock, model, PerformanceScalingGovernor(),
- SimpleScalingDriver(ConstantPowerModel(0.0))
+ platform, model, SimplePowerDriver(ConstantPowerModel(0.0))
)
- val hypervisor = SimFairShareHypervisor(SimResourceSchedulerTrampoline(coroutineContext, clock), listener)
+ val hypervisor = SimFairShareHypervisor(platform, listener = listener)
launch {
machine.run(hypervisor)
@@ -190,10 +192,33 @@ internal class SimHypervisorTest {
yield()
assertAll(
- { assertEquals(2082000, listener.totalRequestedWork, "Requested Burst does not match") },
- { assertEquals(1062000, listener.totalGrantedWork, "Granted Burst does not match") },
+ { assertEquals(2073600, listener.totalRequestedWork, "Requested Burst does not match") },
+ { assertEquals(1053600, listener.totalGrantedWork, "Granted Burst does not match") },
{ assertEquals(1020000, listener.totalOvercommittedWork, "Overcommissioned Burst does not match") },
{ assertEquals(1200000, clock.millis()) }
)
}
+
+ @Test
+ fun testMultipleCPUs() = runBlockingSimulation {
+ val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2)
+ val model = SimMachineModel(
+ cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) },
+ memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }
+ )
+
+ val platform = SimResourceInterpreter(coroutineContext, clock)
+ val machine = SimBareMetalMachine(
+ platform, model, SimplePowerDriver(ConstantPowerModel(0.0))
+ )
+ val hypervisor = SimFairShareHypervisor(platform)
+
+ assertDoesNotThrow {
+ launch {
+ machine.run(hypervisor)
+ }
+ }
+
+ machine.close()
+ }
}
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..69f562d2 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
@@ -29,14 +29,14 @@ 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.power.ConstantPowerModel
+import org.opendc.simulator.compute.power.SimplePowerDriver
import org.opendc.simulator.compute.workload.SimFlopsWorkload
import org.opendc.simulator.core.runBlockingSimulation
+import org.opendc.simulator.resources.SimResourceInterpreter
/**
* Test suite for the [SimBareMetalMachine] class.
@@ -57,7 +57,11 @@ class SimMachineTest {
@Test
fun testFlopsWorkload() = runBlockingSimulation {
- val machine = SimBareMetalMachine(coroutineContext, clock, machineModel, PerformanceScalingGovernor(), SimpleScalingDriver(ConstantPowerModel(0.0)))
+ val machine = SimBareMetalMachine(
+ SimResourceInterpreter(coroutineContext, clock),
+ machineModel,
+ SimplePowerDriver(ConstantPowerModel(0.0))
+ )
try {
machine.run(SimFlopsWorkload(2_000, utilization = 1.0))
@@ -76,7 +80,11 @@ class SimMachineTest {
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(
+ SimResourceInterpreter(coroutineContext, clock),
+ machineModel,
+ SimplePowerDriver(ConstantPowerModel(0.0))
+ )
try {
machine.run(SimFlopsWorkload(2_000, utilization = 1.0))
@@ -90,7 +98,11 @@ class SimMachineTest {
@Test
fun testUsage() = runBlockingSimulation {
- val machine = SimBareMetalMachine(coroutineContext, clock, machineModel, PerformanceScalingGovernor(), SimpleScalingDriver(ConstantPowerModel(0.0)))
+ val machine = SimBareMetalMachine(
+ SimResourceInterpreter(coroutineContext, clock),
+ machineModel,
+ SimplePowerDriver(ConstantPowerModel(0.0))
+ )
val res = mutableListOf<Double>()
val job = launch { machine.usage.toList(res) }
@@ -99,7 +111,7 @@ class SimMachineTest {
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" }
+ assertEquals(listOf(0.0, 1.0, 0.0), res) { "Machine is fully utilized" }
} finally {
machine.close()
}
@@ -107,7 +119,11 @@ class SimMachineTest {
@Test
fun testClose() = runBlockingSimulation {
- val machine = SimBareMetalMachine(coroutineContext, clock, machineModel, PerformanceScalingGovernor(), SimpleScalingDriver(ConstantPowerModel(0.0)))
+ val machine = SimBareMetalMachine(
+ SimResourceInterpreter(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/SimSpaceSharedHypervisorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimSpaceSharedHypervisorTest.kt
index ef6f536d..dba3e9a1 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/SimSpaceSharedHypervisorTest.kt
@@ -30,16 +30,16 @@ 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.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.SimFlopsWorkload
import org.opendc.simulator.compute.workload.SimRuntimeWorkload
import org.opendc.simulator.compute.workload.SimTraceWorkload
import org.opendc.simulator.core.runBlockingSimulation
+import org.opendc.simulator.resources.SimResourceInterpreter
/**
* A test suite for the [SimSpaceSharedHypervisor].
@@ -76,11 +76,11 @@ internal class SimSpaceSharedHypervisorTest {
),
)
+ val interpreter = SimResourceInterpreter(coroutineContext, clock)
val machine = SimBareMetalMachine(
- coroutineContext, clock, machineModel, PerformanceScalingGovernor(),
- SimpleScalingDriver(ConstantPowerModel(0.0))
+ SimResourceInterpreter(coroutineContext, clock), machineModel, SimplePowerDriver(ConstantPowerModel(0.0))
)
- val hypervisor = SimSpaceSharedHypervisor()
+ val hypervisor = SimSpaceSharedHypervisor(interpreter)
val colA = launch { machine.usage.toList(usagePm) }
launch { machine.run(hypervisor) }
@@ -112,11 +112,11 @@ internal class SimSpaceSharedHypervisorTest {
fun testRuntimeWorkload() = runBlockingSimulation {
val duration = 5 * 60L * 1000
val workload = SimRuntimeWorkload(duration)
+ val interpreter = SimResourceInterpreter(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)
launch { machine.run(hypervisor) }
yield()
@@ -135,11 +135,11 @@ internal class SimSpaceSharedHypervisorTest {
fun testFlopsWorkload() = runBlockingSimulation {
val duration = 5 * 60L * 1000
val workload = SimFlopsWorkload((duration * 3.2).toLong(), 1.0)
+ val interpreter = SimResourceInterpreter(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)
launch { machine.run(hypervisor) }
yield()
@@ -156,11 +156,11 @@ internal class SimSpaceSharedHypervisorTest {
@Test
fun testTwoWorkloads() = runBlockingSimulation {
val duration = 5 * 60L * 1000
+ val interpreter = SimResourceInterpreter(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)
launch { machine.run(hypervisor) }
yield()
@@ -182,11 +182,11 @@ internal class SimSpaceSharedHypervisorTest {
*/
@Test
fun testConcurrentWorkloadFails() = runBlockingSimulation {
+ val interpreter = SimResourceInterpreter(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)
launch { machine.run(hypervisor) }
yield()
@@ -206,11 +206,11 @@ internal class SimSpaceSharedHypervisorTest {
*/
@Test
fun testConcurrentWorkloadSucceeds() = runBlockingSimulation {
+ val interpreter = SimResourceInterpreter(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)
launch { machine.run(hypervisor) }
yield()
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/cpufreq/PerformanceScalingGovernorTest.kt
index c482d348..8e8b09c8 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/cpufreq/PerformanceScalingGovernorTest.kt
@@ -26,23 +26,24 @@ import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.jupiter.api.Test
+import org.opendc.simulator.compute.SimProcessingUnit
/**
- * 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 cpu = mockk<SimProcessingUnit>(relaxUnitFun = true)
- every { ctx.cpu.speed } returns 2100.0
+ every { cpu.model.frequency } returns 4100.0
+ every { cpu.speed } returns 2100.0
- val logic = DemandScalingGovernor().createLogic(ctx)
+ val logic = PerformanceScalingGovernor().createLogic(cpu)
logic.onStart()
- verify(exactly = 0) { ctx.setTarget(any()) }
+ logic.onLimit(1.0)
- logic.onLimit()
- verify(exactly = 1) { ctx.setTarget(2100.0) }
+ verify(exactly = 1) { cpu.capacity = 4100.0 }
}
}
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..35fd7c4c 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>()
- every { cpu.model.frequency } returns 4100.0
+ every { cpu.capacity } returns 3200.0
every { cpu.speed } 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,26 @@ 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 cpus = listOf(
+ mockk<SimProcessingUnit>(),
+ mockk()
+ )
- every { cpu.model.frequency } returns 4100.0
- every { cpu.speed } returns 1200.0
+ every { cpus[0].capacity } returns 1000.0
+ every { cpus[0].speed } returns 1200.0
- val driver = PStateScalingDriver(
+ every { cpus[1].capacity } returns 3500.0
+ every { cpus[1].speed } returns 1200.0
+
+ val driver = PStatePowerDriver(
sortedMapOf(
2800.0 to ConstantPowerModel(200.0),
3300.0 to ConstantPowerModel(300.0),
@@ -91,13 +92,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())
}
@@ -109,7 +104,7 @@ internal class PStateScalingDriverTest {
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 +112,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.capacity } returns 1400.0
assertEquals(150.0, logic.computePower())
every { cpu.speed } returns 1400.0
- scalingContext.setTarget(4000.0)
+ every { cpu.capacity } returns 4000.0
assertEquals(235.0, logic.computePower())
}
}