From ea601024d9ec1f03e9bc081de91874b11181548d Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Tue, 10 Mar 2020 23:19:57 +0100 Subject: feat: Represent machine load as flow --- .../opendc/compute/metal/driver/BareMetalDriver.kt | 6 ++++ .../compute/metal/driver/SimpleBareMetalDriver.kt | 40 +++++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) 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..57c62c31 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 @@ -28,11 +28,17 @@ 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 kotlinx.coroutines.flow.Flow /** * A driver interface for the management interface of a bare-metal compute node. */ public interface BareMetalDriver { + /** + * The load of the machine. + */ + public val load: Flow + /** * Initialize the 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..90080b91 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 @@ -38,8 +38,14 @@ import com.atlarge.opendc.compute.core.monitor.ServerMonitor import com.atlarge.opendc.compute.metal.Node import com.atlarge.opendc.compute.metal.PowerState import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.BroadcastChannel +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.launch import java.util.UUID import kotlin.math.ceil @@ -83,6 +89,19 @@ public class SimpleBareMetalDriver( */ private var job: Job? = null + /** + * The channel containing the load of the server. + */ + @UseExperimental(ExperimentalCoroutinesApi::class) + private val loadChannel = BroadcastChannel(Channel.CONFLATED) + + @UseExperimental(FlowPreview::class) + override val load: Flow = loadChannel.asFlow() + + init { + loadChannel.offer(0.0) + } + override suspend fun init(monitor: ServerMonitor): Node = withContext(domain.coroutineContext) { this@SimpleBareMetalDriver.monitor = monitor return@withContext node @@ -167,11 +186,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 load = 0.0 // Determine the duration of the first CPU to finish for (i in 0 until min(cpus.size, burst.size)) { @@ -179,19 +205,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 + load += usage / cpu.frequency + if (cpuDuration != 0L) { // We only wait for processor cores with a non-zero burst duration = min(duration, cpuDuration) } } + loadChannel.offer(load) + 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) + loadChannel.offer(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 -- cgit v1.2.3 From 4e127b7cef0bbc186766af9631f538992ff7dfaf Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Tue, 10 Mar 2020 23:36:25 +0100 Subject: feat: Implement basic power model --- .../opendc/compute/metal/driver/BareMetalDriver.kt | 3 +- .../compute/metal/driver/SimpleBareMetalDriver.kt | 11 ++++-- .../opendc/compute/metal/power/PowerModels.kt | 42 ++++++++++++++++++++++ .../metal/driver/SimpleBareMetalDriverTest.kt | 2 +- .../metal/service/SimpleProvisioningServiceTest.kt | 4 +-- .../virt/driver/hypervisor/HypervisorTest.kt | 2 +- .../com/atlarge/opendc/core/power/PowerModel.kt | 32 +++++++++++++++++ .../com/atlarge/opendc/core/power/Powerable.kt | 37 +++++++++++++++++++ .../environment/sc18/Sc18EnvironmentReader.kt | 3 +- .../environment/sc20/Sc20EnvironmentReader.kt | 2 +- 10 files changed, 128 insertions(+), 10 deletions(-) create mode 100644 opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/power/PowerModels.kt create mode 100644 opendc/opendc-core/src/main/kotlin/com/atlarge/opendc/core/power/PowerModel.kt create mode 100644 opendc/opendc-core/src/main/kotlin/com/atlarge/opendc/core/power/Powerable.kt 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 57c62c31..330eecfd 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 @@ -28,12 +28,13 @@ 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 kotlinx.coroutines.flow.Flow /** * A driver interface for the management interface of a bare-metal compute node. */ -public interface BareMetalDriver { +public interface BareMetalDriver : Powerable { /** * The load of the machine. */ 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 90080b91..a3738a1d 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 @@ -37,6 +37,8 @@ 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.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview @@ -56,18 +58,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, val memoryUnits: List, - private val domain: Domain + powerModel: PowerModel = ConstantPowerModel(0.0) ) : BareMetalDriver { /** * The monitor to use. @@ -98,8 +102,11 @@ public class SimpleBareMetalDriver( @UseExperimental(FlowPreview::class) override val load: Flow = loadChannel.asFlow() + override val powerDraw: Flow + init { loadChannel.offer(0.0) + powerDraw = powerModel(this) } override suspend fun init(monitor: ServerMonitor): Node = withContext(domain.coroutineContext) { 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..a4f400ca --- /dev/null +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/power/PowerModels.kt @@ -0,0 +1,42 @@ +/* + * 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 = { _ -> flowOf(value) } + +/** + * A power model that assumes a naive linear relation between power usage and host CPU utilization. + */ +public fun LinearLoadPowerModel(base: Double, multiplier: Double): PowerModel = { driver -> + driver.load.map { load -> base + multiplier * load } +} diff --git a/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriverTest.kt b/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriverTest.kt index 84b16b68..f3796028 100644 --- a/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriverTest.kt +++ b/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriverTest.kt @@ -55,7 +55,7 @@ internal class SimpleBareMetalDriverTest { val dom = root.newDomain(name = "driver") val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 4) val cpus = List(4) { ProcessingUnit(cpuNode, it, 2400.0) } - val driver = SimpleBareMetalDriver(UUID.randomUUID(), "test", cpus, emptyList(), dom) + val driver = SimpleBareMetalDriver(dom, UUID.randomUUID(), "test", cpus, emptyList()) val monitor = object : ServerMonitor { override suspend fun onUpdate(server: Server, previousState: ServerState) { diff --git a/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/metal/service/SimpleProvisioningServiceTest.kt b/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/metal/service/SimpleProvisioningServiceTest.kt index d5366552..a837130d 100644 --- a/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/metal/service/SimpleProvisioningServiceTest.kt +++ b/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/metal/service/SimpleProvisioningServiceTest.kt @@ -62,8 +62,8 @@ internal class SimpleProvisioningServiceTest { val dom = root.newDomain("provisioner") val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 4) - val cpus = List(5) { ProcessingUnit(cpuNode, it, 2400.0) } - val driver = SimpleBareMetalDriver(UUID.randomUUID(), "test", cpus, emptyList(), dom) + val cpus = List(4) { ProcessingUnit(cpuNode, it, 2400.0) } + val driver = SimpleBareMetalDriver(dom, UUID.randomUUID(), "test", cpus, emptyList()) val provisioner = SimpleProvisioningService(dom) provisioner.create(driver) diff --git a/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorTest.kt b/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorTest.kt index 6cfb2317..c711ebcd 100644 --- a/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorTest.kt +++ b/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorTest.kt @@ -81,7 +81,7 @@ internal class HypervisorTest { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 4) val cpus = List(4) { ProcessingUnit(cpuNode, it, 2000.0) } - val metalDriver = SimpleBareMetalDriver(UUID.randomUUID(), "test", cpus, emptyList(), driverDom) + val metalDriver = SimpleBareMetalDriver(driverDom, UUID.randomUUID(), "test", cpus, emptyList()) metalDriver.init(monitor) metalDriver.setImage(vmm) diff --git a/opendc/opendc-core/src/main/kotlin/com/atlarge/opendc/core/power/PowerModel.kt b/opendc/opendc-core/src/main/kotlin/com/atlarge/opendc/core/power/PowerModel.kt new file mode 100644 index 00000000..51c9f379 --- /dev/null +++ b/opendc/opendc-core/src/main/kotlin/com/atlarge/opendc/core/power/PowerModel.kt @@ -0,0 +1,32 @@ +/* + * 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.core.power + +import kotlinx.coroutines.flow.Flow + +/** + * A model for computing the power draw based on some value. + */ +public typealias PowerModel = (T) -> Flow diff --git a/opendc/opendc-core/src/main/kotlin/com/atlarge/opendc/core/power/Powerable.kt b/opendc/opendc-core/src/main/kotlin/com/atlarge/opendc/core/power/Powerable.kt new file mode 100644 index 00000000..203bf520 --- /dev/null +++ b/opendc/opendc-core/src/main/kotlin/com/atlarge/opendc/core/power/Powerable.kt @@ -0,0 +1,37 @@ +/* + * 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.core.power + +import kotlinx.coroutines.flow.Flow + +/** + * An entity that is uses power from some power source. + */ +public interface Powerable { + /** + * The power draw of the device. + */ + val powerDraw: Flow +} diff --git a/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc18/Sc18EnvironmentReader.kt b/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc18/Sc18EnvironmentReader.kt index 942a8c72..0d4bd125 100644 --- a/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc18/Sc18EnvironmentReader.kt +++ b/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc18/Sc18EnvironmentReader.kt @@ -77,8 +77,7 @@ class Sc18EnvironmentReader(input: InputStream, mapper: ObjectMapper = jacksonOb else -> throw IllegalArgumentException("The cpu id $id is not recognized") } } - SimpleBareMetalDriver(UUID.randomUUID(), "node-${counter++}", cores, listOf(MemoryUnit("", "", 2300.0, 16000)), - dom.newDomain("node-$counter")) + SimpleBareMetalDriver(dom.newDomain("node-$counter"), UUID.randomUUID(), "node-${counter++}", cores, listOf(MemoryUnit("", "", 2300.0, 16000))) } } } diff --git a/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc20/Sc20EnvironmentReader.kt b/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc20/Sc20EnvironmentReader.kt index b33a5e93..7739a47f 100644 --- a/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc20/Sc20EnvironmentReader.kt +++ b/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc20/Sc20EnvironmentReader.kt @@ -80,7 +80,7 @@ class Sc20EnvironmentReader(input: InputStream, mapper: ObjectMapper = jacksonOb else -> throw IllegalArgumentException("The cpu id $id is not recognized") } } - SimpleBareMetalDriver(UUID.randomUUID(), "node-${counter++}", cores, memories, dom.newDomain("node-$counter")) + SimpleBareMetalDriver(dom.newDomain("node-$counter"), UUID.randomUUID(), "node-${counter++}", cores, memories) } } } -- cgit v1.2.3 From 2b71e8639a03d07b1c79a46628f72b1f49d021bb Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Wed, 11 Mar 2020 09:48:43 +0100 Subject: feat: Provide access to BareMetalDriver in Server --- .../atlarge/opendc/compute/metal/driver/BareMetalDriver.kt | 13 +++++++++++-- .../opendc/compute/metal/driver/SimpleBareMetalDriver.kt | 3 ++- .../com/atlarge/opendc/compute/metal/power/PowerModels.kt | 2 +- 3 files changed, 14 insertions(+), 4 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 330eecfd..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,21 +24,25 @@ 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 : Powerable { /** - * The load of the machine. + * The amount of work done by the machine in percentage with respect to the total amount of processing power + * available. */ - public val load: Flow + public val usage: Flow /** * Initialize the driver. @@ -62,4 +66,9 @@ public interface BareMetalDriver : Powerable { * 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(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 a3738a1d..dfd3c3f2 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 @@ -100,7 +100,7 @@ public class SimpleBareMetalDriver( private val loadChannel = BroadcastChannel(Channel.CONFLATED) @UseExperimental(FlowPreview::class) - override val load: Flow = loadChannel.asFlow() + override val usage: Flow = loadChannel.asFlow() override val powerDraw: Flow @@ -130,6 +130,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) { 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 index a4f400ca..10390cd8 100644 --- 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 @@ -38,5 +38,5 @@ public fun ConstantPowerModel(value: Double): PowerModel = { _ * A power model that assumes a naive linear relation between power usage and host CPU utilization. */ public fun LinearLoadPowerModel(base: Double, multiplier: Double): PowerModel = { driver -> - driver.load.map { load -> base + multiplier * load } + driver.usage.map { load -> base + multiplier * load } } -- cgit v1.2.3 From 62160b0e84fb56bf6adc2b18f5df73afb378e749 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Wed, 11 Mar 2020 09:51:44 +0100 Subject: docs: Clarify unit of power draw --- .../src/main/kotlin/com/atlarge/opendc/core/power/Powerable.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendc/opendc-core/src/main/kotlin/com/atlarge/opendc/core/power/Powerable.kt b/opendc/opendc-core/src/main/kotlin/com/atlarge/opendc/core/power/Powerable.kt index 203bf520..4473a571 100644 --- a/opendc/opendc-core/src/main/kotlin/com/atlarge/opendc/core/power/Powerable.kt +++ b/opendc/opendc-core/src/main/kotlin/com/atlarge/opendc/core/power/Powerable.kt @@ -31,7 +31,7 @@ import kotlinx.coroutines.flow.Flow */ public interface Powerable { /** - * The power draw of the device. + * The power draw at the device's power supply in watts (W).w */ val powerDraw: Flow } -- cgit v1.2.3 From e357ad22dde4c3c046beded29c0eb9325e199ebb Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Wed, 11 Mar 2020 10:26:35 +0100 Subject: bug: Propagate remaining burst correctly --- .../opendc/compute/virt/driver/hypervisor/HypervisorVirtDriver.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 ) -- cgit v1.2.3 From 592557d0efc139f9d3b14bad61f8e5b2d93b3a8d Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Wed, 11 Mar 2020 10:27:18 +0100 Subject: feat: Measure host CPU usage and power consumption --- .../opendc/compute/metal/driver/SimpleBareMetalDriver.kt | 2 +- .../opendc/compute/virt/monitor/HypervisorMonitor.kt | 2 +- .../opendc/compute/virt/driver/hypervisor/HypervisorTest.kt | 2 +- .../opendc/experiments/sc20/Sc20HypervisorMonitor.kt | 13 ++++++++++--- .../com/atlarge/opendc/experiments/sc20/TestExperiment.kt | 4 ---- 5 files changed, 13 insertions(+), 10 deletions(-) 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 dfd3c3f2..e9317aff 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 @@ -213,7 +213,7 @@ 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 - load += usage / cpu.frequency + load += usage / (cpu.frequency * 1_000_000) if (cpuDuration != 0L) { // We only wait for processor cores with a non-zero burst duration = min(duration, cpuDuration) 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, diff --git a/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorTest.kt b/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorTest.kt index c711ebcd..2a841711 100644 --- a/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorTest.kt +++ b/opendc/opendc-compute/src/test/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorTest.kt @@ -59,7 +59,7 @@ internal class HypervisorTest { root.launch { val vmm = HypervisorImage(object : HypervisorMonitor { - override fun onSliceFinish( + override suspend fun onSliceFinish( time: Long, requestedBurst: Long, grantedBurst: Long, diff --git a/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/Sc20HypervisorMonitor.kt b/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/Sc20HypervisorMonitor.kt index e095d300..36db25bc 100644 --- a/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/Sc20HypervisorMonitor.kt +++ b/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/Sc20HypervisorMonitor.kt @@ -1,7 +1,9 @@ package com.atlarge.opendc.experiments.sc20 import com.atlarge.opendc.compute.core.Server +import com.atlarge.opendc.compute.metal.driver.BareMetalDriver import com.atlarge.opendc.compute.virt.monitor.HypervisorMonitor +import kotlinx.coroutines.flow.first import java.io.BufferedWriter import java.io.Closeable import java.io.FileWriter @@ -10,17 +12,22 @@ class Sc20HypervisorMonitor : HypervisorMonitor, Closeable { private val outputFile = BufferedWriter(FileWriter("sc20-experiment-results.csv")) init { - outputFile.write("time,requestedBurst,grantedBurst,numberOfDeployedImages,server\n") + outputFile.write("time,requestedBurst,grantedBurst,numberOfDeployedImages,server,hostUsage,powerDraw\n") } - override fun onSliceFinish( + override suspend fun onSliceFinish( time: Long, requestedBurst: Long, grantedBurst: Long, numberOfDeployedImages: Int, hostServer: Server ) { - outputFile.write("$time,$requestedBurst,$grantedBurst,$numberOfDeployedImages,$numberOfDeployedImages,${hostServer.uid}\n") + // Assume for now that the host is not virtualized and measure the current power draw + val driver = hostServer.serviceRegistry[BareMetalDriver.Key] + val usage = driver.usage.first() + val powerDraw = driver.powerDraw.first() + + outputFile.write("$time,$requestedBurst,$grantedBurst,$numberOfDeployedImages,$numberOfDeployedImages,${hostServer.uid},$usage,$powerDraw\n") } override fun close() { diff --git a/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/TestExperiment.kt b/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/TestExperiment.kt index f4be75fa..1d12df29 100644 --- a/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/TestExperiment.kt +++ b/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/TestExperiment.kt @@ -36,7 +36,6 @@ import com.atlarge.opendc.compute.virt.service.allocation.AvailableMemoryAllocat import com.atlarge.opendc.format.environment.sc20.Sc20EnvironmentReader import com.atlarge.opendc.format.trace.sc20.Sc20PerformanceInterferenceReader import com.atlarge.opendc.format.trace.vm.VmTraceReader -import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking @@ -52,7 +51,6 @@ fun main(args: Array) { println("error: Please provide path to directory containing VM trace files") return } - val token = Channel() val monitor = object : ServerMonitor { override suspend fun onUpdate(server: Server, previousState: ServerState) { @@ -89,8 +87,6 @@ fun main(args: Array) { scheduler.deploy(workload.image, monitor, Flavor(workload.image.cores, workload.image.requiredMemory)) } - token.receive() - println(simulationContext.clock.instant()) } -- cgit v1.2.3 From d10ce8c10a6239711307d1cee6f7be29fddeadcb Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Wed, 11 Mar 2020 11:00:19 +0100 Subject: chore: Update dependencies --- buildSrc/build.gradle.kts | 6 +++--- buildSrc/src/main/kotlin/library.kt | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index c56e7e95..6ed95c71 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -40,7 +40,7 @@ repositories { } dependencies { - implementation(kotlin("gradle-plugin", "1.3.61")) - implementation("org.jlleitschuh.gradle:ktlint-gradle:9.1.1") - implementation("org.jetbrains.dokka:dokka-gradle-plugin:0.10.0") + implementation(kotlin("gradle-plugin", version = "1.3.70")) + implementation("org.jlleitschuh.gradle:ktlint-gradle:9.2.1") + implementation("org.jetbrains.dokka:dokka-gradle-plugin:0.10.1") } diff --git a/buildSrc/src/main/kotlin/library.kt b/buildSrc/src/main/kotlin/library.kt index 9532122d..6333e351 100644 --- a/buildSrc/src/main/kotlin/library.kt +++ b/buildSrc/src/main/kotlin/library.kt @@ -30,20 +30,20 @@ object Library { /** * The library for testing the projects. */ - val JUNIT_JUPITER = "5.5.2" + val JUNIT_JUPITER = "5.6.0" /** * The library for hosting the tests. */ - val JUNIT_PLATFORM = "1.5.2" + val JUNIT_PLATFORM = "1.6.0" /** * Logging facade. */ - val SLF4J = "1.7.29" + val SLF4J = "1.7.30" /** * Kotlin coroutines support */ - val KOTLINX_COROUTINES = "1.3.3" + val KOTLINX_COROUTINES = "1.3.4" } -- cgit v1.2.3 From fb8303b734af0f02a3f798e57cde53ab92f30e0a Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Wed, 11 Mar 2020 11:21:40 +0100 Subject: refactor: Extract Signal Flow into odcsim --- .../kotlin/kotlin-library-convention.gradle.kts | 1 + .../kotlin/com/atlarge/odcsim/signal/Signal.kt | 90 ++++++++++++++++++++++ .../compute/metal/driver/SimpleBareMetalDriver.kt | 29 +++---- 3 files changed, 100 insertions(+), 20 deletions(-) create mode 100644 odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/signal/Signal.kt diff --git a/buildSrc/src/main/kotlin/kotlin-library-convention.gradle.kts b/buildSrc/src/main/kotlin/kotlin-library-convention.gradle.kts index 8b71ad91..c0c37a09 100644 --- a/buildSrc/src/main/kotlin/kotlin-library-convention.gradle.kts +++ b/buildSrc/src/main/kotlin/kotlin-library-convention.gradle.kts @@ -42,6 +42,7 @@ java { tasks.withType().configureEach { kotlinOptions.jvmTarget = "1.8" + kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn" } tasks.test { diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/signal/Signal.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/signal/Signal.kt new file mode 100644 index 00000000..da6298a3 --- /dev/null +++ b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/signal/Signal.kt @@ -0,0 +1,90 @@ +/* + * 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.odcsim.signal + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.InternalCoroutinesApi +import kotlinx.coroutines.channels.BroadcastChannel +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.flow.asFlow + +/** + * A [Flow] that contains a single value that changes over time. + * + * This class exists to implement the DataFlow/StateFlow functionality that will be implemented in `kotlinx-coroutines` + * in the future, but is not available yet. + * See: https://github.com/Kotlin/kotlinx.coroutines/pull/1354 + */ +public interface Signal : Flow { + /** + * The current value of this signal. + * + * Setting a value that is [equal][Any.equals] to the previous one does nothing. + */ + public var value: T +} + +/** + * Creates a [Signal] with a given initial [value]. + */ +@Suppress("FunctionName") +public fun Signal(value: T): Signal = SignalImpl(value) + +/** + * Internal implementation of the [Signal] interface. + */ +private class SignalImpl(initialValue: T) : Signal { + /** + * The [BroadcastChannel] to back this signal. + */ + @OptIn(ExperimentalCoroutinesApi::class) + private val chan = BroadcastChannel(Channel.CONFLATED) + + /** + * The internal [Flow] backing this signal. + */ + @OptIn(FlowPreview::class) + private val flow = chan.asFlow() + + init { + @OptIn(ExperimentalCoroutinesApi::class) + chan.offer(initialValue) + } + + @OptIn(ExperimentalCoroutinesApi::class) + public override var value: T = initialValue + set(value) { + if (field != value) { + chan.offer(value) + field = value + } + } + + @InternalCoroutinesApi + override suspend fun collect(collector: FlowCollector) = flow.collect(collector) +} 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 e9317aff..637432db 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 @@ -40,14 +41,9 @@ 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.ExperimentalCoroutinesApi -import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.Job -import kotlinx.coroutines.channels.BroadcastChannel -import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.launch import java.util.UUID import kotlin.math.ceil @@ -94,20 +90,13 @@ public class SimpleBareMetalDriver( private var job: Job? = null /** - * The channel containing the load of the server. + * The signal containing the load of the server. */ - @UseExperimental(ExperimentalCoroutinesApi::class) - private val loadChannel = BroadcastChannel(Channel.CONFLATED) + private val usageSignal = Signal(0.0) - @UseExperimental(FlowPreview::class) - override val usage: Flow = loadChannel.asFlow() + override val usage: Flow = usageSignal - override val powerDraw: Flow - - init { - loadChannel.offer(0.0) - powerDraw = powerModel(this) - } + override val powerDraw: Flow = powerModel(this) override suspend fun init(monitor: ServerMonitor): Node = withContext(domain.coroutineContext) { this@SimpleBareMetalDriver.monitor = monitor @@ -205,7 +194,7 @@ public class SimpleBareMetalDriver( val start = simulationContext.clock.millis() var duration = max(0, deadline - start) - var load = 0.0 + var totalUsage = 0.0 // Determine the duration of the first CPU to finish for (i in 0 until min(cpus.size, burst.size)) { @@ -213,14 +202,14 @@ 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 - load += usage / (cpu.frequency * 1_000_000) + 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) } } - loadChannel.offer(load) + usageSignal.value = totalUsage try { delay(duration) @@ -232,7 +221,7 @@ public class SimpleBareMetalDriver( // Flush the load if the do not receive a new run call for the same timestamp flush = domain.launch { delay(1) - loadChannel.offer(0.0) + usageSignal.value = 0.0 } flush!!.invokeOnCompletion { flush = null -- cgit v1.2.3 From 1200816b7bf5c76b6bbb91b7e4555e9f04ea1af9 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Wed, 11 Mar 2020 11:54:03 +0100 Subject: feat: Use linear power model for SC20 experiments --- .../opendc/compute/metal/driver/SimpleBareMetalDriver.kt | 2 +- .../com/atlarge/opendc/compute/metal/power/PowerModels.kt | 7 +++++-- .../opendc/experiments/sc20/Sc20HypervisorMonitor.kt | 4 +++- .../com/atlarge/opendc/experiments/sc20/TestExperiment.kt | 6 +++++- .../opendc/format/environment/sc20/Sc20EnvironmentReader.kt | 13 ++++++++++++- 5 files changed, 26 insertions(+), 6 deletions(-) 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 637432db..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 @@ -209,7 +209,7 @@ public class SimpleBareMetalDriver( } } - usageSignal.value = totalUsage + usageSignal.value = totalUsage / cpus.size try { delay(duration) 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 index 10390cd8..9ddbe08e 100644 --- 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 @@ -36,7 +36,10 @@ public fun ConstantPowerModel(value: Double): PowerModel = { _ /** * 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(base: Double, multiplier: Double): PowerModel = { driver -> - driver.usage.map { load -> base + multiplier * load } +public fun LinearLoadPowerModel(idle: Double, max: Double): PowerModel = { driver -> + driver.usage.map { load -> (max - idle) * load + idle } } diff --git a/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/Sc20HypervisorMonitor.kt b/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/Sc20HypervisorMonitor.kt index 36db25bc..55a0ce75 100644 --- a/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/Sc20HypervisorMonitor.kt +++ b/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/Sc20HypervisorMonitor.kt @@ -27,10 +27,12 @@ class Sc20HypervisorMonitor : HypervisorMonitor, Closeable { val usage = driver.usage.first() val powerDraw = driver.powerDraw.first() - outputFile.write("$time,$requestedBurst,$grantedBurst,$numberOfDeployedImages,$numberOfDeployedImages,${hostServer.uid},$usage,$powerDraw\n") + outputFile.write("$time,$requestedBurst,$grantedBurst,$numberOfDeployedImages,$numberOfDeployedImages,${hostServer.uid},$usage,$powerDraw") + outputFile.newLine() } override fun close() { + outputFile.flush() outputFile.close() } } diff --git a/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/TestExperiment.kt b/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/TestExperiment.kt index 1d12df29..76f7b600 100644 --- a/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/TestExperiment.kt +++ b/opendc/opendc-experiments-sc20/src/main/kotlin/com/atlarge/opendc/experiments/sc20/TestExperiment.kt @@ -52,6 +52,7 @@ fun main(args: Array) { return } + val hypervisorMonitor = Sc20HypervisorMonitor() val monitor = object : ServerMonitor { override suspend fun onUpdate(server: Server, previousState: ServerState) { println(server) @@ -76,7 +77,7 @@ fun main(args: Array) { AvailableMemoryAllocationPolicy(), simulationContext, environment.platforms[0].zones[0].services[ProvisioningService.Key], - Sc20HypervisorMonitor() + hypervisorMonitor ) val reader = VmTraceReader(File(args[0]), performanceInterferenceModel) @@ -94,4 +95,7 @@ fun main(args: Array) { system.run() system.terminate() } + + // Explicitly close the monitor to flush its buffer + hypervisorMonitor.close() } diff --git a/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc20/Sc20EnvironmentReader.kt b/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc20/Sc20EnvironmentReader.kt index 7739a47f..a954a308 100644 --- a/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc20/Sc20EnvironmentReader.kt +++ b/opendc/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/sc20/Sc20EnvironmentReader.kt @@ -29,6 +29,7 @@ import com.atlarge.opendc.compute.core.MemoryUnit import com.atlarge.opendc.compute.core.ProcessingNode import com.atlarge.opendc.compute.core.ProcessingUnit import com.atlarge.opendc.compute.metal.driver.SimpleBareMetalDriver +import com.atlarge.opendc.compute.metal.power.LinearLoadPowerModel import com.atlarge.opendc.compute.metal.service.ProvisioningService import com.atlarge.opendc.compute.metal.service.SimpleProvisioningService import com.atlarge.opendc.core.Environment @@ -80,7 +81,17 @@ class Sc20EnvironmentReader(input: InputStream, mapper: ObjectMapper = jacksonOb else -> throw IllegalArgumentException("The cpu id $id is not recognized") } } - SimpleBareMetalDriver(dom.newDomain("node-$counter"), UUID.randomUUID(), "node-${counter++}", cores, memories) + SimpleBareMetalDriver( + dom.newDomain("node-$counter"), + UUID.randomUUID(), + "node-${counter++}", + cores, + memories, + // For now we assume a simple linear load model with an idle draw of ~200W and a maximum + // power draw of 350W. + // Source: https://stackoverflow.com/questions/6128960 + LinearLoadPowerModel(200.0, 350.0) + ) } } } -- cgit v1.2.3