From 0ad600eb8e14c0ef3ba5529c59d300dc20c85ab2 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Tue, 23 Mar 2021 17:07:31 +0100 Subject: simulator: Add support for transforming resource consumers This change adds support for transforming the resource commands emitted by the resource consumers. The SimResourceForwarder is modified to also support transforming the resource commands. --- .../main/kotlin/org/opendc/simulator/compute/SimAbstractHypervisor.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'simulator/opendc-simulator/opendc-simulator-compute/src/main') diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractHypervisor.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractHypervisor.kt index 81d09f12..8046dd53 100644 --- a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractHypervisor.kt +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractHypervisor.kt @@ -142,11 +142,11 @@ public abstract class SimAbstractHypervisor : SimHypervisor { */ override fun close() { if (!isTerminated) { + isTerminated = true + cpus.forEach { (_, provider) -> provider.close() } _vms.remove(this) } - - isTerminated = true } } -- cgit v1.2.3 From 69598598be2c248acc49e40607b3dd0998ec1ca5 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Wed, 24 Mar 2021 13:05:33 +0100 Subject: simulator: Move power models to simulator module This change moves the power models from the `opendc-compute-simulator` to the `opendc-simulator-compute` module, since it better fits the scope of the models and allows them to be re-used for other purposes. --- .../simulator/compute/SimBareMetalMachine.kt | 17 +++- .../simulator/compute/power/ConstantPowerModel.kt | 8 ++ .../simulator/compute/power/CubicPowerModel.kt | 25 ++++++ .../compute/power/InterpolationPowerModel.kt | 33 ++++++++ .../simulator/compute/power/LinearPowerModel.kt | 23 ++++++ .../simulator/compute/power/MachinePowerModel.kt | 18 ++++ .../simulator/compute/power/PStatePowerModel.kt | 96 ++++++++++++++++++++++ .../simulator/compute/power/SqrtPowerModel.kt | 25 ++++++ .../simulator/compute/power/SquarePowerModel.kt | 25 ++++++ .../compute/power/ZeroIdlePowerDecorator.kt | 12 +++ 10 files changed, 278 insertions(+), 4 deletions(-) create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ConstantPowerModel.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/CubicPowerModel.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/InterpolationPowerModel.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/LinearPowerModel.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/MachinePowerModel.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PStatePowerModel.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SqrtPowerModel.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SquarePowerModel.kt create mode 100644 simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ZeroIdlePowerDecorator.kt (limited to 'simulator/opendc-simulator/opendc-simulator-compute/src/main') diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt index 19479719..f86c4198 100644 --- a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt @@ -23,7 +23,10 @@ package org.opendc.simulator.compute import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* import org.opendc.simulator.compute.model.ProcessingUnit +import org.opendc.simulator.compute.power.ConstantPowerModel +import org.opendc.simulator.compute.power.MachinePowerModel import org.opendc.simulator.resources.* import org.opendc.utils.TimerScheduler import java.time.Clock @@ -43,14 +46,20 @@ import kotlin.coroutines.* public class SimBareMetalMachine( context: CoroutineContext, private val clock: Clock, - override val model: SimMachineModel + override val model: SimMachineModel, + private val powerModel: MachinePowerModel = ConstantPowerModel(0.0) ) : SimAbstractMachine(clock) { /** * The [Job] associated with this machine. */ - private val job = Job() + private val scope = CoroutineScope(context + Job()) - override val context: CoroutineContext = context + job + /** + * The power draw of the machine. + */ + public val powerDraw: StateFlow = usage.map { powerModel.computeCpuPower(it) }.stateIn(scope, SharingStarted.Eagerly, 0.0) + + override val context: CoroutineContext = scope.coroutineContext /** * The [TimerScheduler] to use for scheduling the interrupts. @@ -63,6 +72,6 @@ public class SimBareMetalMachine( override fun close() { super.close() scheduler.close() - job.cancel() + scope.cancel() } } diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ConstantPowerModel.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ConstantPowerModel.kt new file mode 100644 index 00000000..5d7ae8ad --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ConstantPowerModel.kt @@ -0,0 +1,8 @@ +package org.opendc.simulator.compute.power + +/** + * A power model which produces a constant value [constant]. + */ +public class ConstantPowerModel(private val constant: Double) : MachinePowerModel { + public override fun computeCpuPower(cpuUtil: Double): Double = constant +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/CubicPowerModel.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/CubicPowerModel.kt new file mode 100644 index 00000000..8e47f571 --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/CubicPowerModel.kt @@ -0,0 +1,25 @@ +package org.opendc.simulator.compute.power + +import kotlin.math.pow + +/** + * The cubic power model partially adapted from CloudSim. + * + * @param maxPower The maximum power draw in Watts of the server. + * @param staticPowerPercent The static power percentage. + * @property staticPower The static power consumption that is not dependent on resource usage. + * It is the amount of energy consumed even when the host is idle. + * @property constPower The constant power consumption for each fraction of resource used. + */ +public class CubicPowerModel( + private var maxPower: Double, + staticPowerPercent: Double +) : MachinePowerModel { + private var staticPower: Double = staticPowerPercent * maxPower + private var constPower: Double = (maxPower - staticPower) / 100.0.pow(3) + + public override fun computeCpuPower(cpuUtil: Double): Double { + require(cpuUtil in 0.0..1.0) { "CPU utilization must be in [0, 1]" } + return staticPower + constPower * (cpuUtil * 100).pow(3) + } +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/InterpolationPowerModel.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/InterpolationPowerModel.kt new file mode 100644 index 00000000..af7b6e6d --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/InterpolationPowerModel.kt @@ -0,0 +1,33 @@ +package org.opendc.simulator.compute.power + +import kotlin.math.ceil +import kotlin.math.floor + +/** + * The linear interpolation power model partially adapted from CloudSim. + * + * @param maxPower The maximum power draw in Watts of the server. + * @param staticPowerPercent The static power percentage. + * @property staticPower The static power consumption that is not dependent on resource usage. + * It is the amount of energy consumed even when the host is idle. + * @property constPower The constant power consumption for each fraction of resource used. + */ +public abstract class InterpolationPowerModel : MachinePowerModel { + + public override fun computeCpuPower(cpuUtil: Double): Double { + require(cpuUtil in 0.0..1.0) { "CPU utilization must be in [0, 1]" } + + val cpuUtilFlr = floor(cpuUtil * 10).toInt() + val cpuUtilCil = ceil(cpuUtil * 10).toInt() + val power1: Double = getPowerData(cpuUtilFlr) + val power2: Double = getPowerData(cpuUtilCil) + val delta = (power2 - power1) / 10 + + return if (cpuUtil % 0.1 == 0.0) + getPowerData((cpuUtil * 10).toInt()) + else + power1 + delta * (cpuUtil - cpuUtilFlr.toDouble() / 10) * 100 + } + + public abstract fun getPowerData(index: Int): Double +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/LinearPowerModel.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/LinearPowerModel.kt new file mode 100644 index 00000000..14443aff --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/LinearPowerModel.kt @@ -0,0 +1,23 @@ +package org.opendc.simulator.compute.power + +/** + * The linear power model partially adapted from CloudSim. + * + * @param maxPower The maximum power draw in Watts of the server. + * @param staticPowerPercent The static power percentage. + * @property staticPower The static power consumption that is not dependent on resource usage. + * It is the amount of energy consumed even when the host is idle. + * @property constPower The constant power consumption for each fraction of resource used. + */ +public class LinearPowerModel( + private var maxPower: Double, + staticPowerPercent: Double +) : MachinePowerModel { + private var staticPower: Double = staticPowerPercent * maxPower + private var constPower: Double = (maxPower - staticPower) / 100 + + public override fun computeCpuPower(cpuUtil: Double): Double { + require(cpuUtil in 0.0..1.0) { "CPU utilization must be in [0, 1]" } + return staticPower + constPower * cpuUtil * 100 + } +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/MachinePowerModel.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/MachinePowerModel.kt new file mode 100644 index 00000000..9bf03b87 --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/MachinePowerModel.kt @@ -0,0 +1,18 @@ +package org.opendc.simulator.compute.power + +import org.opendc.simulator.compute.SimMachine + +/** + * A model for estimating the power usage of a [SimMachine]. + */ +public interface MachinePowerModel { + /** + * Computes CPU power consumption for each host. + * + * @param cpuUtil The CPU utilization percentage. + * @return A [Double] value of CPU power consumption. + * @throws IllegalArgumentException Will throw an error if [cpuUtil] is out of range. + */ + @Throws(IllegalArgumentException::class) + public fun computeCpuPower(cpuUtil: Double): Double +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PStatePowerModel.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PStatePowerModel.kt new file mode 100644 index 00000000..722f478d --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PStatePowerModel.kt @@ -0,0 +1,96 @@ +package org.opendc.simulator.compute.power + +import org.opendc.simulator.compute.SimBareMetalMachine +import java.time.Clock +import java.util.* + +/** + * The CPU power model derived from the iCanCloud simulator. + * + * @param machine The [SimBareMetalMachine] that the model is measuring. + * @param clock The virtual [Clock] to track the time spent at each p-state. + * @property cpuPowerMeter A [MutableMap] that contains CPU frequencies ([Double]) in GHz + * as keys and the time ([Long]) spent in that frequency range in seconds. + * @property pStatesToPower A [TreeMap] that contains the frequency ([Double]) of corresponding p-state in GHz + * as keys and the energy ([Double]) consumption of that state in Watts. + * @property pStatesRange A [Pair] in which the fist and second elements are the lower and upper bounds of the + * consumption values of the p-states respectively. + * @property lastMeasureTime The last update time of the [cpuPowerMeter]. + * @property currPState The p-state that the model is currently in. + */ +public class PStatePowerModel( + private val machine: SimBareMetalMachine, + private val clock: Clock, +) { + // TODO: Extract the power meter out of the model. + private val cpuPowerMeter = mutableMapOf() + private val pStatesToPower = TreeMap() + private val pStatesRange: Pair + private var lastMeasureTime: Long + private var currPState: Double + + init { + loadPStates(this) + pStatesRange = Pair(pStatesToPower.keys.first(), pStatesToPower.keys.last()) + pStatesToPower.keys.forEach { cpuPowerMeter[it] = 0L } + currPState = pStatesRange.first + lastMeasureTime = getClockInstant() + updateCpuPowerMeter() + } + + /** Recorde the elapsed time to the corresponding p-state. */ + public fun updateCpuPowerMeter() { + val newMeasureTime = getClockInstant() + val newMaxFreq: Double = getMaxCpuSpeedInGHz() + assert(newMaxFreq in pStatesRange.first..pStatesRange.second) { + "The maximum frequency $newMaxFreq is not in the range of the P-state frequency " + + "from ${pStatesRange.first} to ${pStatesRange.second}." + } + + // Update the current p-state level on which the CPU is running. + val newPState = pStatesToPower.ceilingKey(newMaxFreq) + + // Add the time elapsed to the previous state. + cpuPowerMeter.merge(currPState, newMeasureTime - lastMeasureTime, Long::plus) + + // Update the current states. + currPState = newPState + lastMeasureTime = newMeasureTime + } + + /** Get the power value of the energy consumption level at which the CPU is working. */ + public fun getInstantCpuPower(): Double = + pStatesToPower.getOrDefault(currPState, 0.0) + + /** Get the accumulated power consumption up until now. */ + public fun getAccumulatedCpuPower(): Double = + pStatesToPower.keys + .map { + pStatesToPower.getOrDefault(it, 0.0) * + cpuPowerMeter.getOrDefault(it, 0.0).toDouble() + }.sum() + + private fun getClockInstant() = clock.millis() / 1000 + + /** Get the maximum frequency of the CPUs in GHz as that of the package. + * @see + * on page 34. + */ + private fun getMaxCpuSpeedInGHz() = (machine.speed.maxOrNull() ?: 0.0) / 1000 + + public companion object PStatesLoader { + private fun loadPStates(pStatePowerModel: PStatePowerModel) { + // TODO: Dynamically load configuration. + // See P4 of https://www.intel.com/content/dam/support/us/en/documents/motherboards/server/sb/power_management_of_intel_architecture_servers.pdf + pStatePowerModel.pStatesToPower.putAll( + sortedMapOf( + 3.6 to 103.0, + 3.4 to 94.0, + 3.2 to 85.0, + 3.0 to 76.0, + 2.8 to 8.0, + ) + ) + } + } +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SqrtPowerModel.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SqrtPowerModel.kt new file mode 100644 index 00000000..bf177aff --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SqrtPowerModel.kt @@ -0,0 +1,25 @@ +package org.opendc.simulator.compute.power + +import kotlin.math.sqrt + +/** + * The square root power model partially adapted from CloudSim. + * + * @param maxPower The maximum power draw in Watts of the server. + * @param staticPowerPercent The static power percentage. + * @property staticPower The static power consumption that is not dependent on resource usage. + * It is the amount of energy consumed even when the host is idle. + * @property constPower The constant power consumption for each fraction of resource used. + */ +public class SqrtPowerModel( + private var maxPower: Double, + staticPowerPercent: Double +) : MachinePowerModel { + private var staticPower: Double = staticPowerPercent * maxPower + private var constPower: Double = (maxPower - staticPower) / sqrt(100.0) + + override fun computeCpuPower(cpuUtil: Double): Double { + require(cpuUtil in 0.0..1.0) { "CPU utilization must be in [0, 1]" } + return staticPower + constPower * sqrt(cpuUtil * 100) + } +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SquarePowerModel.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SquarePowerModel.kt new file mode 100644 index 00000000..cbfad530 --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SquarePowerModel.kt @@ -0,0 +1,25 @@ +package org.opendc.simulator.compute.power + +import kotlin.math.pow + +/** + * The square power model partially adapted from CloudSim. + * + * @param maxPower The maximum power draw in Watts of the server. + * @param staticPowerPercent The static power percentage. + * @property staticPower The static power consumption that is not dependent on resource usage. + * It is the amount of energy consumed even when the host is idle. + * @property constPower The constant power consumption for each fraction of resource used. + */ +public class SquarePowerModel( + private var maxPower: Double, + staticPowerPercent: Double +) : MachinePowerModel { + private var staticPower: Double = staticPowerPercent * maxPower + private var constPower: Double = (maxPower - staticPower) / 100.0.pow(2) + + override fun computeCpuPower(cpuUtil: Double): Double { + require(cpuUtil in 0.0..1.0) { "CPU utilization must be in [0, 1]" } + return staticPower + constPower * (cpuUtil * 100).pow(2) + } +} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ZeroIdlePowerDecorator.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ZeroIdlePowerDecorator.kt new file mode 100644 index 00000000..01deac5b --- /dev/null +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ZeroIdlePowerDecorator.kt @@ -0,0 +1,12 @@ +package org.opendc.simulator.compute.power + +/** + * A decorator for ignoring the idle power when computing energy consumption of components. + * + * @param delegate The [MachinePowerModel] to delegate to. + */ +public class ZeroIdlePowerDecorator(private val delegate: MachinePowerModel) : MachinePowerModel { + override fun computeCpuPower(cpuUtil: Double): Double { + return if (cpuUtil == 0.0) 0.0 else delegate.computeCpuPower(cpuUtil) + } +} -- cgit v1.2.3