summaryrefslogtreecommitdiff
path: root/opendc/opendc-compute/src/main
diff options
context:
space:
mode:
authorGeorgios Andreadis <g.andreadis@student.tudelft.nl>2020-03-11 12:16:10 +0100
committerGeorgios Andreadis <g.andreadis@student.tudelft.nl>2020-03-11 12:16:10 +0100
commit1ccfcb28bb91c9dc456a1f324a0be6300086eb28 (patch)
treeb03bbf6dc69cc2d2c7fcfda1ad0b081c91443312 /opendc/opendc-compute/src/main
parent2dd2bfe87bcbe368f46ce8e975ccccbfac2c0560 (diff)
parent1200816b7bf5c76b6bbb91b7e4555e9f04ea1af9 (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')
-rw-r--r--opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/BareMetalDriver.kt18
-rw-r--r--opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriver.kt41
-rw-r--r--opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/power/PowerModels.kt45
-rw-r--r--opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorVirtDriver.kt8
-rw-r--r--opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/monitor/HypervisorMonitor.kt2
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,