diff options
| author | Hongyu <hongyuhe.cs@googlemail.com> | 2021-04-05 12:50:19 +0200 |
|---|---|---|
| committer | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2021-04-07 12:13:16 +0200 |
| commit | f3384ca33f84fc261aeb20ca7752ab052971dcf4 (patch) | |
| tree | f3c942f2ce6c8f38081ccf6200172815d539ab6a /simulator | |
| parent | e38e6b9341907e28d029054995cf43cbd5e8bb4d (diff) | |
simulator: Polish power models
This change updates the power models by fixing some of the documentation
and adding toString() methods.
Diffstat (limited to 'simulator')
11 files changed, 66 insertions, 225 deletions
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 index 1b9733d1..b8cb8412 100644 --- 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 @@ -1,8 +1,10 @@ package org.opendc.simulator.compute.power /** - * A power model which produces a constant value [constant]. + * A power model which produces a constant value [power]. */ -public class ConstantPowerModel(private val constant: Double) : PowerModel { - public override fun computePower(utilization: Double): Double = constant +public class ConstantPowerModel(private val power: Double) : PowerModel { + public override fun computePower(utilization: Double): Double = power + + override fun toString(): String = "ConstantPowerModel[power=$power]" } 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 index e5bc013c..48edace6 100644 --- 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 @@ -5,21 +5,15 @@ 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. + * @param maxPower The maximum power draw of the server in W. + * @param idlePower The power draw of the server in idle state in W. */ -public class CubicPowerModel( - private var maxPower: Double, - staticPowerPercent: Double -) : PowerModel { - private var staticPower: Double = staticPowerPercent * maxPower - private var constPower: Double = (maxPower - staticPower) / 100.0.pow(3) +public class CubicPowerModel(private val maxPower: Double, private val idlePower: Double) : PowerModel { + private val factor: Double = (maxPower - idlePower) / 100.0.pow(3) public override fun computePower(utilization: Double): Double { - require(utilization in 0.0..1.0) { "CPU utilization must be in [0, 1]" } - return staticPower + constPower * (utilization * 100).pow(3) + return idlePower + factor * (utilization * 100).pow(3) } + + override fun toString(): String = "CubicPowerModel[max=$maxPower,idle=$idlePower]" } 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 index ce9b8197..85613a57 100644 --- 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 @@ -3,6 +3,8 @@ package org.opendc.simulator.compute.power import org.yaml.snakeyaml.Yaml import kotlin.math.ceil import kotlin.math.floor +import kotlin.math.max +import kotlin.math.min /** * The linear interpolation power model partially adapted from CloudSim. @@ -10,30 +12,30 @@ import kotlin.math.floor * * @param hardwareName The name of the hardware vendor. * @see <a href="http://www.spec.org/power_ssj2008/results/res2011q1/">Machines used in the SPEC benchmark</a> - * @property averagePowerValues A [List] of average active power measured by the power analyzer(s) - * and accumulated by the PTDaemon (Power and Temperature Daemon) for this - * measurement interval, displayed as watts (W). */ -public class InterpolationPowerModel( - hardwareName: String, -) : PowerModel { +public class InterpolationPowerModel(hardwareName: String) : PowerModel { + /** + * A [List] of average active power measured by the power analyzer(s) and accumulated by the + * PTDaemon (Power and Temperature Daemon) for this measurement interval, displayed as watts (W). + */ private val averagePowerValues: List<Double> = loadAveragePowerValue(hardwareName) public override fun computePower(utilization: Double): Double { - require(utilization in 0.0..1.0) { "CPU utilization must be in [0, 1]" } - - val cpuUtilFlr = floor(utilization * 10).toInt() - val cpuUtilCil = ceil(utilization * 10).toInt() - val powerFlr: Double = getAveragePowerValue(cpuUtilFlr) - val powerCil: Double = getAveragePowerValue(cpuUtilCil) + val clampedUtilization = min(1.0, max(0.0, utilization)) + val utilizationFlr = floor(clampedUtilization * 10).toInt() + val utilizationCil = ceil(clampedUtilization * 10).toInt() + val powerFlr: Double = getAveragePowerValue(utilizationFlr) + val powerCil: Double = getAveragePowerValue(utilizationCil) val delta = (powerCil - powerFlr) / 10 return if (utilization % 0.1 == 0.0) - getAveragePowerValue((utilization * 10).toInt()) + getAveragePowerValue((clampedUtilization * 10).toInt()) else - powerFlr + delta * (utilization - cpuUtilFlr.toDouble() / 10) * 100 + powerFlr + delta * (clampedUtilization - utilizationFlr.toDouble() / 10) * 100 } + override fun toString(): String = "InterpolationPowerModel[entries=${averagePowerValues.size}]" + /** * Gets the power consumption for a given utilization percentage. * 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 index 56a75bb6..1a0cc07b 100644 --- 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 @@ -3,21 +3,18 @@ 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. + * @param maxPower The maximum power draw of the server in W. + * @param idlePower The power draw of the server in idle state in W. */ -public class LinearPowerModel( - private var maxPower: Double, - staticPowerPercent: Double -) : PowerModel { - private var staticPower: Double = staticPowerPercent * maxPower - private var constPower: Double = (maxPower - staticPower) / 100 +public class LinearPowerModel(private val maxPower: Double, private val idlePower: Double) : PowerModel { + /** + * The linear interpolation factor of the model. + */ + private val factor: Double = (maxPower - idlePower) / 100 public override fun computePower(utilization: Double): Double { - require(utilization in 0.0..1.0) { "CPU utilization must be in [0, 1]" } - return staticPower + constPower * utilization * 100 + return idlePower + factor * utilization * 100 } + + override fun toString(): String = "LinearPowerModel[max=$maxPower,idle=$idlePower]" } 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 deleted file mode 100644 index 722f478d..00000000 --- a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PStatePowerModel.kt +++ /dev/null @@ -1,96 +0,0 @@ -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<Double, Long>() - private val pStatesToPower = TreeMap<Double, Double>() - private val pStatesRange: Pair<Double, Double> - 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 <a href="https://www.intel.vn/content/dam/www/public/us/en/documents/datasheets/10th-gen-core-families-datasheet-vol-1-datasheet.pdf"> - * 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/PowerModel.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerModel.kt index 19dff88c..1387e65a 100644 --- a/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerModel.kt +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerModel.kt @@ -7,10 +7,10 @@ import org.opendc.simulator.compute.SimMachine */ public interface PowerModel { /** - * Compute the power consumption of the CPU based on its utilization. + * Computes CPU power consumption for each host. * - * @param utilization The CPU utilization percentage in [0, 1]. - * @return The power consumption of the CPU in terms of watts (W). + * @param utilization The CPU utilization percentage. + * @return A [Double] value of CPU power consumption. */ public fun computePower(utilization: Double): Double } 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 index b78421d8..da40ca85 100644 --- 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 @@ -5,21 +5,15 @@ 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. + * @param maxPower The maximum power draw of the server in W. + * @param idlePower The power draw of the server in idle state in W. */ -public class SqrtPowerModel( - private var maxPower: Double, - staticPowerPercent: Double -) : PowerModel { - private var staticPower: Double = staticPowerPercent * maxPower - private var constPower: Double = (maxPower - staticPower) / sqrt(100.0) +public class SqrtPowerModel(private val maxPower: Double, private val idlePower: Double) : PowerModel { + private val factor: Double = (maxPower - idlePower) / sqrt(100.0) override fun computePower(utilization: Double): Double { - require(utilization in 0.0..1.0) { "CPU utilization must be in [0, 1]" } - return staticPower + constPower * sqrt(utilization * 100) + return idlePower + factor * sqrt(utilization * 100) } + + override fun toString(): String = "SqrtPowerModel[max=$maxPower,idle=$idlePower]" } 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 index b5953daa..4f914ddf 100644 --- 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 @@ -5,21 +5,15 @@ 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. + * @param maxPower The maximum power draw of the server in W. + * @param idlePower The power draw of the server in idle state in W. */ -public class SquarePowerModel( - private var maxPower: Double, - staticPowerPercent: Double -) : PowerModel { - private var staticPower: Double = staticPowerPercent * maxPower - private var constPower: Double = (maxPower - staticPower) / 100.0.pow(2) +public class SquarePowerModel(private val maxPower: Double, private val idlePower: Double) : PowerModel { + private val factor: Double = (maxPower - idlePower) / 100.0.pow(2) override fun computePower(utilization: Double): Double { - require(utilization in 0.0..1.0) { "CPU utilization must be in [0, 1]" } - return staticPower + constPower * (utilization * 100).pow(2) + return idlePower + factor * (utilization * 100).pow(2) } + + override fun toString(): String = "SquarePowerModel[max=$maxPower,idle=$idlePower]" } 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 index 50f04a4f..19dfcadd 100644 --- 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 @@ -7,6 +7,11 @@ package org.opendc.simulator.compute.power */ public class ZeroIdlePowerDecorator(private val delegate: PowerModel) : PowerModel { override fun computePower(utilization: Double): Double { - return if (utilization == 0.0) 0.0 else delegate.computePower(utilization) + return if (utilization == 0.0) + 0.0 + else + delegate.computePower(utilization) } + + override fun toString(): String = "ZeroIdlePowerDecorator[delegate=$delegate]" } diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PStatePowerModelTest.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PStatePowerModelTest.kt deleted file mode 100644 index 9116f928..00000000 --- a/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PStatePowerModelTest.kt +++ /dev/null @@ -1,44 +0,0 @@ -package org.opendc.simulator.compute.power - -import io.mockk.* -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.opendc.simulator.compute.SimBareMetalMachine -import java.time.Clock - -internal class PStatePowerModelTest { - @Test - fun `update CPU power meter with P-states`() { - val p0Power = 8.0 - val p3Power = 94.0 - val p4Power = 103.0 - val expectedP0Power = 8.0 * 10 - val expectedP0P4Power = expectedP0Power + 103.0 * 10 - - val clock = mockkClass(Clock::class) - val machine = mockkClass(SimBareMetalMachine::class) - every { clock.millis() } returnsMany listOf(0L, 0L, 10_000L, 20_000L) - every { machine.speed } returns - listOf(2.8, 2.8, 2.8, 2.8).map { it * 1000 } andThen // Max. 2.8MHz covered by P0 - listOf(1.5, 3.1, 3.3, 3.6).map { it * 1000 } andThen // Max. 3.6MHz covered by P4 - listOf(1.5, 3.1, 3.1, 3.3).map { it * 1000 } // Max. 3.3MHz covered by P3 - - // Power meter initialization. - val pStatePowerModel = PStatePowerModel(machine, clock) - verify(exactly = 2) { clock.millis() } - verify(exactly = 1) { machine.speed } - assertEquals(p0Power, pStatePowerModel.getInstantCpuPower()) - - // The first measure. - pStatePowerModel.updateCpuPowerMeter() - assertEquals(p4Power, pStatePowerModel.getInstantCpuPower()) - assertEquals(expectedP0Power, pStatePowerModel.getAccumulatedCpuPower()) - - // The second measure. - pStatePowerModel.updateCpuPowerMeter() - assertEquals(p3Power, pStatePowerModel.getInstantCpuPower()) - assertEquals(expectedP0P4Power, pStatePowerModel.getAccumulatedCpuPower()) - - verify(exactly = 4) { clock.millis() } - } -} diff --git a/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/MachinePowerModelTest.kt b/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PowerModelTest.kt index f42e2e73..8ec9910c 100644 --- a/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/MachinePowerModelTest.kt +++ b/simulator/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PowerModelTest.kt @@ -1,6 +1,5 @@ package org.opendc.simulator.compute.power -import kotlinx.coroutines.ExperimentalCoroutinesApi import org.junit.jupiter.api.Assertions.assertAll import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test @@ -11,8 +10,7 @@ import org.junit.jupiter.params.provider.MethodSource import java.util.stream.Stream import kotlin.math.pow -@OptIn(ExperimentalCoroutinesApi::class) -internal class MachinePowerModelTest { +internal class PowerModelTest { private val epsilon = 10.0.pow(-3) private val cpuUtil = 0.9 @@ -42,11 +40,6 @@ internal class MachinePowerModelTest { val powerModel = InterpolationPowerModel("IBMx3550M3_XeonX5675") assertAll( - { assertThrows<IllegalArgumentException> { powerModel.computePower(-1.3) } }, - { assertThrows<IllegalArgumentException> { powerModel.computePower(1.3) } }, - ) - - assertAll( { assertEquals(58.4, powerModel.computePower(0.0)) }, { assertEquals(58.4 + (98 - 58.4) / 5, powerModel.computePower(0.02)) }, { assertEquals(98.0, powerModel.computePower(0.1)) }, @@ -63,10 +56,10 @@ internal class MachinePowerModelTest { @JvmStatic fun MachinePowerModelArgs(): Stream<Arguments> = Stream.of( Arguments.of(ConstantPowerModel(0.0), 0.0), - Arguments.of(LinearPowerModel(350.0, 200 / 350.0), 335.0), - Arguments.of(SquarePowerModel(350.0, 200 / 350.0), 321.5), - Arguments.of(CubicPowerModel(350.0, 200 / 350.0), 309.35), - Arguments.of(SqrtPowerModel(350.0, 200 / 350.0), 342.302), + Arguments.of(LinearPowerModel(350.0, 200.0), 335.0), + Arguments.of(SquarePowerModel(350.0, 200.0), 321.5), + Arguments.of(CubicPowerModel(350.0, 200.0), 309.35), + Arguments.of(SqrtPowerModel(350.0, 200.0), 342.302), ) } } |
