diff options
Diffstat (limited to 'opendc-simulator/opendc-simulator-compute')
6 files changed, 120 insertions, 33 deletions
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimAbstractHypervisor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimAbstractHypervisor.kt index 8e925bdf..9495095e 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimAbstractHypervisor.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimAbstractHypervisor.kt @@ -30,7 +30,7 @@ import org.opendc.simulator.compute.model.MachineModel import org.opendc.simulator.compute.model.ProcessingUnit import org.opendc.simulator.compute.workload.SimWorkload import org.opendc.simulator.flow.* -import org.opendc.simulator.flow.interference.InterferenceKey +import org.opendc.simulator.compute.kernel.interference.VmInterferenceKey import org.opendc.simulator.flow.mux.FlowMultiplexer import kotlin.math.roundToLong @@ -144,6 +144,10 @@ public abstract class SimAbstractHypervisor( if (delta > 0) { _counters.record() + + for (vm in _vms) { + vm._counters.record() + } } val load = cpuDemand / cpuCapacity @@ -171,19 +175,19 @@ public abstract class SimAbstractHypervisor( /** * The interference key of this virtual machine. */ - private val interferenceKey: InterferenceKey? = interferenceId?.let { interferenceDomain?.createKey(it) } + private val interferenceKey: VmInterferenceKey? = interferenceId?.let { interferenceDomain?.createKey(it) } /** * The vCPUs of the machine. */ - override val cpus = model.cpus.map { cpu -> VCpu(mux, mux.newInput(cpu.frequency, interferenceKey), cpu) } + override val cpus = model.cpus.map { cpu -> VCpu(mux, mux.newInput(cpu.frequency), cpu) } /** * The resource counters associated with the hypervisor. */ override val counters: SimHypervisorCounters get() = _counters - private val _counters = VmCountersImpl(cpus) + @JvmField val _counters = VmCountersImpl(cpus, interferenceDomain, interferenceKey) /** * The CPU capacity of the hypervisor in MHz. @@ -317,8 +321,8 @@ public abstract class SimAbstractHypervisor( override val cpuLostTime: Long get() = _cpuTime[3] - private val _cpuTime = LongArray(4) - private val _previous = DoubleArray(4) + val _cpuTime = LongArray(4) + private val _previous = DoubleArray(3) /** * Record the CPU time of the hypervisor. @@ -331,22 +335,18 @@ public abstract class SimAbstractHypervisor( val demand = counters.demand val actual = counters.actual val remaining = counters.remaining - val interference = counters.interference val demandDelta = demand - previous[0] val actualDelta = actual - previous[1] val remainingDelta = remaining - previous[2] - val interferenceDelta = interference - previous[3] previous[0] = demand previous[1] = actual previous[2] = remaining - previous[3] = interference cpuTime[0] += (actualDelta * d).roundToLong() cpuTime[1] += (remainingDelta * d).roundToLong() cpuTime[2] += ((demandDelta - actualDelta) * d).roundToLong() - cpuTime[3] += (interferenceDelta * d).roundToLong() } override fun flush() { @@ -358,17 +358,71 @@ public abstract class SimAbstractHypervisor( /** * A [SimHypervisorCounters] implementation for a virtual machine. */ - private class VmCountersImpl(private val cpus: List<VCpu>) : SimHypervisorCounters { + private inner class VmCountersImpl( + private val cpus: List<VCpu>, + private val interferenceDomain: VmInterferenceDomain?, + private val key: VmInterferenceKey? + ) : SimHypervisorCounters { private val d = cpus.size / cpus.sumOf { it.model.frequency } * 1000 override val cpuActiveTime: Long - get() = (cpus.sumOf { it.counters.actual } * d).roundToLong() + get() = _cpuTime[0] override val cpuIdleTime: Long - get() = (cpus.sumOf { it.counters.remaining } * d).roundToLong() + get() = _cpuTime[1] override val cpuStealTime: Long - get() = (cpus.sumOf { it.counters.demand - it.counters.actual } * d).roundToLong() + get() = _cpuTime[2] override val cpuLostTime: Long - get() = (cpus.sumOf { it.counters.interference } * d).roundToLong() + get() = _cpuTime[3] + + private val _cpuTime = LongArray(4) + private val _previous = DoubleArray(3) + + /** + * Record the CPU time of the hypervisor. + */ + fun record() { + val cpuTime = _cpuTime + val previous = _previous + + var demand = 0.0 + var actual = 0.0 + var remaining = 0.0 + + for (cpu in cpus) { + val counters = cpu.counters + + actual += counters.actual + demand += counters.demand + remaining += counters.remaining + } + + val demandDelta = demand - previous[0] + val actualDelta = actual - previous[1] + val remainingDelta = remaining - previous[2] + + previous[0] = demand + previous[1] = actual + previous[2] = remaining + + val d = d + cpuTime[0] += (actualDelta * d).roundToLong() + cpuTime[1] += (remainingDelta * d).roundToLong() + cpuTime[2] += ((demandDelta - actualDelta) * d).roundToLong() + + // Compute the performance penalty due to flow interference + val interferenceDomain = interferenceDomain + if (interferenceDomain != null) { + val mux = mux + val load = mux.rate / mux.capacity.coerceAtLeast(1.0) + val penalty = 1 - interferenceDomain.apply(key, load) + val interference = (actualDelta * d * penalty).roundToLong() + + if (interference > 0) { + cpuTime[3] += interference + _counters._cpuTime[3] += interference + } + } + } override fun flush() { for (cpu in cpus) { diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisor.kt index 36f76650..f6a700b9 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisor.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisor.kt @@ -50,7 +50,7 @@ public class SimFairShareHypervisor( /** * The multiplexer that distributes the computing capacity. */ - override val mux: FlowMultiplexer = MaxMinFlowMultiplexer(engine, this, interferenceDomain) + override val mux: FlowMultiplexer = MaxMinFlowMultiplexer(engine, this) override fun canFit(model: MachineModel): Boolean = true } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.kt index 09b03306..5220fa2d 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.kt @@ -22,38 +22,45 @@ package org.opendc.simulator.compute.kernel.interference -import org.opendc.simulator.flow.interference.InterferenceDomain -import org.opendc.simulator.flow.interference.InterferenceKey - /** * The interference domain of a hypervisor. */ -public interface VmInterferenceDomain : InterferenceDomain { +public interface VmInterferenceDomain { + /** + * Compute the performance score of a participant in this interference domain. + * + * @param key The participant to obtain the score of or `null` if the participant has no key. + * @param load The overall load on the interference domain. + * @return A score representing the performance score to be applied to the resource consumer, with 1 + * meaning no influence, <1 means that performance degrades, and >1 means that performance improves. + */ + public fun apply(key: VmInterferenceKey?, load: Double): Double + /** - * Construct an [InterferenceKey] for the specified [id]. + * Construct an [VmInterferenceKey] for the specified [id]. * * @param id The identifier of the virtual machine. * @return A key identifying the virtual machine as part of the interference domain. `null` if the virtual machine * does not participate in the domain. */ - public fun createKey(id: String): InterferenceKey? + public fun createKey(id: String): VmInterferenceKey? /** * Remove the specified [key] from this domain. */ - public fun removeKey(key: InterferenceKey) + public fun removeKey(key: VmInterferenceKey) /** * Mark the specified [key] as active in this interference domain. * * @param key The key to join the interference domain with. */ - public fun join(key: InterferenceKey) + public fun join(key: VmInterferenceKey) /** * Mark the specified [key] as inactive in this interference domain. * * @param key The key of the virtual machine that wants to leave the domain. */ - public fun leave(key: InterferenceKey) + public fun leave(key: VmInterferenceKey) } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceKey.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceKey.kt new file mode 100644 index 00000000..8d720ea9 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceKey.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 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 org.opendc.simulator.compute.kernel.interference + +/** + * A key that uniquely identifies a participant of an interference domain. + */ +public interface VmInterferenceKey diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.kt index 977292be..7cc545c8 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.kt @@ -22,7 +22,6 @@ package org.opendc.simulator.compute.kernel.interference -import org.opendc.simulator.flow.interference.InterferenceKey import java.util.* /** @@ -215,12 +214,12 @@ public class VmInterferenceModel private constructor( */ private val activeKeys = ArrayList<InterferenceKeyImpl>() - override fun createKey(id: String): InterferenceKey? { + override fun createKey(id: String): VmInterferenceKey? { val intId = idMapping[id] ?: return null return keys.computeIfAbsent(intId) { InterferenceKeyImpl(intId) } } - override fun removeKey(key: InterferenceKey) { + override fun removeKey(key: VmInterferenceKey) { if (key !is InterferenceKeyImpl) { return } @@ -232,7 +231,7 @@ public class VmInterferenceModel private constructor( keys.remove(key.id) } - override fun join(key: InterferenceKey) { + override fun join(key: VmInterferenceKey) { if (key !is InterferenceKeyImpl) { return } @@ -246,14 +245,14 @@ public class VmInterferenceModel private constructor( } } - override fun leave(key: InterferenceKey) { + override fun leave(key: VmInterferenceKey) { if (key is InterferenceKeyImpl && key.release()) { activeKeys.remove(key) computeActiveGroups(key.id) } } - override fun apply(key: InterferenceKey?, load: Double): Double { + override fun apply(key: VmInterferenceKey?, load: Double): Double { if (key == null || key !is InterferenceKeyImpl) { return 1.0 } @@ -367,7 +366,7 @@ public class VmInterferenceModel private constructor( * * @param id The identifier of the member. */ - private class InterferenceKeyImpl(@JvmField val id: Int) : InterferenceKey, Comparable<InterferenceKeyImpl> { + private class InterferenceKeyImpl(@JvmField val id: Int) : VmInterferenceKey, Comparable<InterferenceKeyImpl> { /** * The active groups to which the key belongs. */ diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt index 91855e8d..dd5bfc33 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt @@ -47,7 +47,6 @@ import org.opendc.simulator.flow.FlowEngine /** * Test suite for the [SimHypervisor] class. */ -@OptIn(ExperimentalCoroutinesApi::class) internal class SimFairShareHypervisorTest { private lateinit var model: MachineModel |
