diff options
| author | Georgios Andreadis <g.andreadis@student.tudelft.nl> | 2020-03-11 12:16:10 +0100 |
|---|---|---|
| committer | Georgios Andreadis <g.andreadis@student.tudelft.nl> | 2020-03-11 12:16:10 +0100 |
| commit | 1ccfcb28bb91c9dc456a1f324a0be6300086eb28 (patch) | |
| tree | b03bbf6dc69cc2d2c7fcfda1ad0b081c91443312 /opendc/opendc-compute/src/main | |
| parent | 2dd2bfe87bcbe368f46ce8e975ccccbfac2c0560 (diff) | |
| parent | 1200816b7bf5c76b6bbb91b7e4555e9f04ea1af9 (diff) | |
Merge branch 'feat/2.x-power-model' into '2.x'
Implement basic power usage model
See merge request opendc/opendc-simulator!36
Diffstat (limited to 'opendc/opendc-compute/src/main')
5 files changed, 105 insertions, 9 deletions
diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/BareMetalDriver.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/BareMetalDriver.kt index 1330158e..1214dd36 100644 --- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/BareMetalDriver.kt +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/BareMetalDriver.kt @@ -24,15 +24,26 @@ package com.atlarge.opendc.compute.metal.driver +import com.atlarge.opendc.compute.core.Server import com.atlarge.opendc.compute.core.image.Image import com.atlarge.opendc.compute.core.monitor.ServerMonitor import com.atlarge.opendc.compute.metal.Node import com.atlarge.opendc.compute.metal.PowerState +import com.atlarge.opendc.core.power.Powerable +import com.atlarge.opendc.core.services.AbstractServiceKey +import kotlinx.coroutines.flow.Flow +import java.util.UUID /** * A driver interface for the management interface of a bare-metal compute node. */ -public interface BareMetalDriver { +public interface BareMetalDriver : Powerable { + /** + * The amount of work done by the machine in percentage with respect to the total amount of processing power + * available. + */ + public val usage: Flow<Double> + /** * Initialize the driver. */ @@ -55,4 +66,9 @@ public interface BareMetalDriver { * Obtain the state of the compute node. */ public suspend fun refresh(): Node + + /** + * A key that allows access to the [BareMetalDriver] instance from a [Server] that runs on the bare-metal machine. + */ + companion object Key : AbstractServiceKey<BareMetalDriver>(UUID.randomUUID(), "bare-metal:driver") } diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriver.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriver.kt index fcdc9363..cd3e9a48 100644 --- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriver.kt +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriver.kt @@ -25,6 +25,7 @@ package com.atlarge.opendc.compute.metal.driver import com.atlarge.odcsim.Domain +import com.atlarge.odcsim.signal.Signal import com.atlarge.odcsim.simulationContext import com.atlarge.opendc.compute.core.ProcessingUnit import com.atlarge.opendc.compute.core.Server @@ -37,9 +38,12 @@ import com.atlarge.opendc.compute.core.image.Image import com.atlarge.opendc.compute.core.monitor.ServerMonitor import com.atlarge.opendc.compute.metal.Node import com.atlarge.opendc.compute.metal.PowerState +import com.atlarge.opendc.compute.metal.power.ConstantPowerModel +import com.atlarge.opendc.core.power.PowerModel import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Job import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch import java.util.UUID import kotlin.math.ceil @@ -50,18 +54,20 @@ import kotlinx.coroutines.withContext /** * A basic implementation of the [BareMetalDriver] that simulates an [Image] running on a bare-metal machine. * + * @param domain The simulation domain the driver runs in. * @param uid The unique identifier of the machine. * @param name An optional name of the machine. * @param cpus The CPUs available to the bare metal machine. * @param memoryUnits The memory units in this machine. - * @param domain The simulation domain the driver runs in. + * @param powerModel The power model of this machine. */ public class SimpleBareMetalDriver( + private val domain: Domain, uid: UUID, name: String, val cpus: List<ProcessingUnit>, val memoryUnits: List<MemoryUnit>, - private val domain: Domain + powerModel: PowerModel<SimpleBareMetalDriver> = ConstantPowerModel(0.0) ) : BareMetalDriver { /** * The monitor to use. @@ -83,6 +89,15 @@ public class SimpleBareMetalDriver( */ private var job: Job? = null + /** + * The signal containing the load of the server. + */ + private val usageSignal = Signal(0.0) + + override val usage: Flow<Double> = usageSignal + + override val powerDraw: Flow<Double> = powerModel(this) + override suspend fun init(monitor: ServerMonitor): Node = withContext(domain.coroutineContext) { this@SimpleBareMetalDriver.monitor = monitor return@withContext node @@ -104,6 +119,7 @@ public class SimpleBareMetalDriver( PowerState.POWER_ON to PowerState.POWER_ON -> node.server else -> throw IllegalStateException() } + server?.serviceRegistry?.set(BareMetalDriver.Key, this@SimpleBareMetalDriver) node = node.copy(powerState = powerState, server = server) if (powerState != previousPowerState && server != null) { @@ -167,11 +183,18 @@ public class SimpleBareMetalDriver( domain.launch { monitor.onUpdate(server, previousState) } } + private var flush: Job? = null + override suspend fun run(burst: LongArray, limit: DoubleArray, deadline: Long) { require(burst.size == limit.size) { "Array dimensions do not match" } + // If run is called in at the same timestamp as the previous call, cancel the load flush + flush?.cancel() + flush = null + val start = simulationContext.clock.millis() var duration = max(0, deadline - start) + var totalUsage = 0.0 // Determine the duration of the first CPU to finish for (i in 0 until min(cpus.size, burst.size)) { @@ -179,19 +202,31 @@ public class SimpleBareMetalDriver( val usage = min(limit[i], cpu.frequency) * 1_000_000 // Usage from MHz to Hz val cpuDuration = ceil(burst[i] / usage * 1000).toLong() // Convert from seconds to milliseconds + totalUsage += usage / (cpu.frequency * 1_000_000) + if (cpuDuration != 0L) { // We only wait for processor cores with a non-zero burst duration = min(duration, cpuDuration) } } + usageSignal.value = totalUsage / cpus.size + try { delay(duration) } catch (_: CancellationException) { // On cancellation, we compute and return the remaining burst } - val end = simulationContext.clock.millis() + // Flush the load if the do not receive a new run call for the same timestamp + flush = domain.launch { + delay(1) + usageSignal.value = 0.0 + } + flush!!.invokeOnCompletion { + flush = null + } + // Write back the remaining burst time for (i in 0 until min(cpus.size, burst.size)) { val usage = min(limit[i], cpus[i].frequency) * 1_000_000 diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/power/PowerModels.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/power/PowerModels.kt new file mode 100644 index 00000000..9ddbe08e --- /dev/null +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/power/PowerModels.kt @@ -0,0 +1,45 @@ +/* + * MIT License + * + * Copyright (c) 2020 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.opendc.compute.metal.power + +import com.atlarge.opendc.compute.metal.driver.BareMetalDriver +import com.atlarge.opendc.core.power.PowerModel +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map + +/** + * A power model which emits a single value. + */ +public fun ConstantPowerModel(value: Double): PowerModel<BareMetalDriver> = { _ -> flowOf(value) } + +/** + * A power model that assumes a naive linear relation between power usage and host CPU utilization. + * + * @param idle The power draw in Watts on idle. + * @param max The maximum power draw in Watts of the server. + */ +public fun LinearLoadPowerModel(idle: Double, max: Double): PowerModel<BareMetalDriver> = { driver -> + driver.usage.map { load -> (max - idle) * load + idle } +} diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorVirtDriver.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorVirtDriver.kt index 6fe11c28..05e1ab90 100644 --- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorVirtDriver.kt +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorVirtDriver.kt @@ -155,10 +155,10 @@ class HypervisorVirtDriver( } } - val granted = burst.clone() + val remainder = burst.clone() // We run the total burst on the host processor. Note that this call may be cancelled at any moment in // time, so not all of the burst may be executed. - hostContext.run(granted, usage, deadline) + hostContext.run(remainder, usage, deadline) val end = simulationContext.clock.millis() // No work was performed @@ -178,7 +178,7 @@ class HypervisorVirtDriver( val fraction = actualUsage / usage[i] // Compute the burst time that the VM was actually granted - val grantedBurst = max(0, actualBurst - ceil(burst[i] * fraction).toLong()) + val grantedBurst = max(0, actualBurst - ceil(remainder[i] * fraction).toLong()) // Compute remaining burst time to be executed for the request vm.requestedBurst[i] = max(0, vm.requestedBurst[i] - grantedBurst) @@ -194,7 +194,7 @@ class HypervisorVirtDriver( monitor.onSliceFinish( end, burst[i], - granted[i], + remainder[i], vms.size, hostContext.server ) diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/monitor/HypervisorMonitor.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/monitor/HypervisorMonitor.kt index e259a3c0..1e3981f6 100644 --- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/monitor/HypervisorMonitor.kt +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/monitor/HypervisorMonitor.kt @@ -15,7 +15,7 @@ interface HypervisorMonitor { * @param numberOfDeployedImages The number of images deployed on this hypervisor. * @param hostServer The server hosting this hypervisor. */ - fun onSliceFinish( + suspend fun onSliceFinish( time: Long, requestedBurst: Long, grantedBurst: Long, |
