From 35ec0060fb73149e687655851a682f12486f0086 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Mon, 5 Sep 2022 20:23:45 +0200 Subject: refactor(sim/compute): Move interference logic into VmInterferenceMember This change updates the design of the VM interference model, where we move more of the logic into the `VmInterferenceMember` interface. This removes the dependency on the `VmInterferenceModel` for the hypervisor interface. --- .../kotlin/org/opendc/compute/simulator/SimHost.kt | 4 +- .../simulator/compute/SimMachineBenchmarks.kt | 4 +- .../compute/kernel/SimAbstractHypervisor.kt | 48 ++--- .../compute/kernel/SimFairShareHypervisor.kt | 8 +- .../kernel/SimFairShareHypervisorProvider.kt | 4 +- .../simulator/compute/kernel/SimHypervisor.kt | 4 +- .../compute/kernel/SimHypervisorProvider.kt | 7 +- .../kernel/SimSpaceSharedHypervisorProvider.kt | 2 - .../kernel/interference/VmInterferenceDomain.kt | 37 +--- .../kernel/interference/VmInterferenceKey.kt | 28 --- .../kernel/interference/VmInterferenceMember.kt | 47 +++++ .../kernel/interference/VmInterferenceModel.kt | 197 ++++++++++----------- .../compute/kernel/SimFairShareHypervisorTest.kt | 12 +- 13 files changed, 170 insertions(+), 232 deletions(-) delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceKey.kt create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceMember.kt diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt index 908353f0..628f324b 100644 --- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt +++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt @@ -92,7 +92,7 @@ public class SimHost( * The hypervisor to run multiple workloads. */ private val hypervisor: SimHypervisor = hypervisorProvider - .create(engine, scalingGovernor = scalingGovernor, interferenceDomain = interferenceDomain) + .create(engine, scalingGovernor = scalingGovernor) /** * The virtual machines running on the hypervisor. @@ -144,7 +144,7 @@ public class SimHost( val guest = guests.computeIfAbsent(server) { key -> require(canFit(key)) { "Server does not fit" } - val interferenceKey = interferenceDomain?.createKey(key.name) + val interferenceKey = interferenceDomain?.getMember(key.name) val machine = hypervisor.newMachine(key.flavor.toMachineModel(), interferenceKey) val newGuest = Guest( scope.coroutineContext, diff --git a/opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt b/opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt index fbcc99c3..02b48fa7 100644 --- a/opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt +++ b/opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt @@ -107,7 +107,7 @@ class SimMachineBenchmarks { val machine = SimBareMetalMachine( engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0)) ) - val hypervisor = SimFairShareHypervisor(engine, null, null) + val hypervisor = SimFairShareHypervisor(engine, null) launch { machine.runWorkload(hypervisor) } @@ -129,7 +129,7 @@ class SimMachineBenchmarks { val machine = SimBareMetalMachine( engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0)) ) - val hypervisor = SimFairShareHypervisor(engine, null, null) + val hypervisor = SimFairShareHypervisor(engine, null) launch { machine.runWorkload(hypervisor) } 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 b3898004..98dab28f 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 @@ -25,8 +25,7 @@ package org.opendc.simulator.compute.kernel import org.opendc.simulator.compute.* import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernor import org.opendc.simulator.compute.kernel.cpufreq.ScalingPolicy -import org.opendc.simulator.compute.kernel.interference.VmInterferenceDomain -import org.opendc.simulator.compute.kernel.interference.VmInterferenceKey +import org.opendc.simulator.compute.kernel.interference.VmInterferenceMember import org.opendc.simulator.compute.model.MachineModel import org.opendc.simulator.compute.model.ProcessingUnit import org.opendc.simulator.compute.workload.SimWorkload @@ -42,8 +41,7 @@ import kotlin.math.roundToLong */ public abstract class SimAbstractHypervisor( protected val engine: FlowEngine, - private val scalingGovernor: ScalingGovernor?, - protected val interferenceDomain: VmInterferenceDomain? = null + private val scalingGovernor: ScalingGovernor? ) : SimHypervisor, FlowConvergenceListener { /** * The machine on which the hypervisor runs. @@ -93,7 +91,7 @@ public abstract class SimAbstractHypervisor( private val governors = mutableListOf() /* SimHypervisor */ - override fun newMachine(model: MachineModel, interferenceKey: VmInterferenceKey?): SimVirtualMachine { + override fun newMachine(model: MachineModel, interferenceKey: VmInterferenceMember?): SimVirtualMachine { require(canFit(model)) { "Machine does not fit" } val vm = VirtualMachine(model, interferenceKey) _vms.add(vm) @@ -163,7 +161,7 @@ public abstract class SimAbstractHypervisor( */ private inner class VirtualMachine( model: MachineModel, - private val interferenceKey: VmInterferenceKey? = null + private val interferenceKey: VmInterferenceMember? = null ) : SimAbstractMachine(engine, model), SimVirtualMachine, AutoCloseable { /** * A flag to indicate that the machine is closed. @@ -180,7 +178,7 @@ public abstract class SimAbstractHypervisor( */ override val counters: SimHypervisorCounters get() = _counters - @JvmField val _counters = VmCountersImpl(cpus, interferenceDomain, interferenceKey) + @JvmField val _counters = VmCountersImpl(cpus, interferenceKey) /** * The CPU capacity of the hypervisor in MHz. @@ -206,17 +204,18 @@ public abstract class SimAbstractHypervisor( return super.startWorkload( object : SimWorkload { override fun onStart(ctx: SimMachineContext) { + val interferenceKey = interferenceKey try { - joinInterferenceDomain() + interferenceKey?.activate() workload.onStart(ctx) } catch (cause: Throwable) { - leaveInterferenceDomain() + interferenceKey?.deactivate() throw cause } } override fun onStop(ctx: SimMachineContext) { - leaveInterferenceDomain() + interferenceKey?.deactivate() workload.onStop(ctx) } }, @@ -236,26 +235,6 @@ public abstract class SimAbstractHypervisor( cpu.close() } } - - /** - * Join the interference domain of the hypervisor. - */ - private fun joinInterferenceDomain() { - val interferenceKey = interferenceKey - if (interferenceKey != null) { - interferenceDomain?.join(interferenceKey) - } - } - - /** - * Leave the interference domain of the hypervisor. - */ - private fun leaveInterferenceDomain() { - val interferenceKey = interferenceKey - if (interferenceKey != null) { - interferenceDomain?.leave(interferenceKey) - } - } } /** @@ -353,8 +332,7 @@ public abstract class SimAbstractHypervisor( */ private inner class VmCountersImpl( private val cpus: List, - private val interferenceDomain: VmInterferenceDomain?, - private val key: VmInterferenceKey? + private val key: VmInterferenceMember? ) : SimHypervisorCounters { private val d = cpus.size / cpus.sumOf { it.model.frequency } * 1000 @@ -403,11 +381,11 @@ public abstract class SimAbstractHypervisor( cpuTime[2] += ((demandDelta - actualDelta) * d).roundToLong() // Compute the performance penalty due to flow interference - val interferenceDomain = interferenceDomain - if (interferenceDomain != null) { + val key = key + if (key != null) { val mux = mux val load = mux.rate / mux.capacity.coerceAtLeast(1.0) - val penalty = 1 - interferenceDomain.apply(key, load) + val penalty = 1 - key.apply(load) val interference = (actualDelta * d * penalty).roundToLong() if (interference > 0) { 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 a12c5517..66453835 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 @@ -24,7 +24,6 @@ package org.opendc.simulator.compute.kernel import org.opendc.simulator.compute.SimMachine import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernor -import org.opendc.simulator.compute.kernel.interference.VmInterferenceDomain import org.opendc.simulator.compute.model.MachineModel import org.opendc.simulator.compute.workload.SimWorkload import org.opendc.simulator.flow.FlowEngine @@ -36,15 +35,12 @@ import org.opendc.simulator.flow.mux.MaxMinFlowMultiplexer * concurrently using weighted fair sharing. * * @param engine The [FlowEngine] to manage the machine's resources. - * @param listener The listener for the convergence of the system. * @param scalingGovernor The CPU frequency scaling governor to use for the hypervisor. - * @param interferenceDomain The resource interference domain to which the hypervisor belongs. */ public class SimFairShareHypervisor( engine: FlowEngine, - scalingGovernor: ScalingGovernor?, - interferenceDomain: VmInterferenceDomain?, -) : SimAbstractHypervisor(engine, scalingGovernor, interferenceDomain) { + scalingGovernor: ScalingGovernor? +) : SimAbstractHypervisor(engine, scalingGovernor) { /** * The multiplexer that distributes the computing capacity. */ diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorProvider.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorProvider.kt index 204b4860..ad8177d3 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorProvider.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorProvider.kt @@ -23,7 +23,6 @@ package org.opendc.simulator.compute.kernel import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernor -import org.opendc.simulator.compute.kernel.interference.VmInterferenceDomain import org.opendc.simulator.flow.FlowEngine /** @@ -35,6 +34,5 @@ public class SimFairShareHypervisorProvider : SimHypervisorProvider { override fun create( engine: FlowEngine, scalingGovernor: ScalingGovernor?, - interferenceDomain: VmInterferenceDomain?, - ): SimHypervisor = SimFairShareHypervisor(engine, scalingGovernor, interferenceDomain) + ): SimHypervisor = SimFairShareHypervisor(engine, scalingGovernor) } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisor.kt index 229e569c..f53d0c5d 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisor.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisor.kt @@ -23,7 +23,7 @@ package org.opendc.simulator.compute.kernel import org.opendc.simulator.compute.SimMachine -import org.opendc.simulator.compute.kernel.interference.VmInterferenceKey +import org.opendc.simulator.compute.kernel.interference.VmInterferenceMember import org.opendc.simulator.compute.model.MachineModel import org.opendc.simulator.compute.workload.SimWorkload @@ -68,7 +68,7 @@ public interface SimHypervisor : SimWorkload { * @param model The machine to create. * @param interferenceKey The key of the machine in the interference model. */ - public fun newMachine(model: MachineModel, interferenceKey: VmInterferenceKey? = null): SimVirtualMachine + public fun newMachine(model: MachineModel, interferenceKey: VmInterferenceMember? = null): SimVirtualMachine /** * Remove the specified [machine] from the hypervisor. diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisorProvider.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisorProvider.kt index b7e8760a..6ee523fd 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisorProvider.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisorProvider.kt @@ -23,7 +23,6 @@ package org.opendc.simulator.compute.kernel import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernor -import org.opendc.simulator.compute.kernel.interference.VmInterferenceDomain import org.opendc.simulator.flow.FlowEngine /** @@ -41,9 +40,5 @@ public interface SimHypervisorProvider { /** * Create a new [SimHypervisor] instance. */ - public fun create( - engine: FlowEngine, - scalingGovernor: ScalingGovernor? = null, - interferenceDomain: VmInterferenceDomain? = null, - ): SimHypervisor + public fun create(engine: FlowEngine, scalingGovernor: ScalingGovernor? = null): SimHypervisor } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorProvider.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorProvider.kt index 96b73e69..f7456797 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorProvider.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorProvider.kt @@ -23,7 +23,6 @@ package org.opendc.simulator.compute.kernel import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernor -import org.opendc.simulator.compute.kernel.interference.VmInterferenceDomain import org.opendc.simulator.flow.FlowEngine /** @@ -35,6 +34,5 @@ public class SimSpaceSharedHypervisorProvider : SimHypervisorProvider { override fun create( engine: FlowEngine, scalingGovernor: ScalingGovernor?, - interferenceDomain: VmInterferenceDomain?, ): SimHypervisor = SimSpaceSharedHypervisor(engine, scalingGovernor) } 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 5220fa2d..3b355f1e 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 @@ -27,40 +27,11 @@ package org.opendc.simulator.compute.kernel.interference */ 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 [VmInterferenceKey] for the specified [id]. + * Return the [VmInterferenceMember] associated with 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): VmInterferenceKey? - - /** - * Remove the specified [key] from this domain. - */ - 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: 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. + * @return A [VmInterferenceMember] representing the virtual machine as part of the interference domain. `null` if + * the virtual machine does not participate in the domain. */ - public fun leave(key: VmInterferenceKey) + public fun getMember(id: String): VmInterferenceMember? } 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 deleted file mode 100644 index 8d720ea9..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceKey.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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/VmInterferenceMember.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceMember.kt new file mode 100644 index 00000000..2f3dd74b --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceMember.kt @@ -0,0 +1,47 @@ +/* + * 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 participant of an interference domain. + */ +public interface VmInterferenceMember { + /** + * Mark this member as active in this interference domain. + */ + public fun activate() + + /** + * Mark this member as inactive in this interference domain. + */ + public fun deactivate() + + /** + * Compute the performance score of the member in this interference domain. + * + * @param load The overall load on the interference domain. + * @return A score representing the performance score to be applied to the member, with 1 + * meaning no influence, <1 means that performance degrades, and >1 means that performance improves. + */ + public fun apply(load: Double): Double +} 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 7cc545c8..bfda3121 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 @@ -35,11 +35,11 @@ import java.util.* * @param seed The seed to use for randomly selecting the virtual machines that are affected. */ public class VmInterferenceModel private constructor( - private val targets: DoubleArray, - private val scores: DoubleArray, private val idMapping: Map, private val members: Array, private val membership: Array, + private val targets: DoubleArray, + private val scores: DoubleArray, private val size: Int, seed: Long, ) { @@ -51,13 +51,13 @@ public class VmInterferenceModel private constructor( /** * Construct a new [VmInterferenceDomain]. */ - public fun newDomain(): VmInterferenceDomain = InterferenceDomainImpl(targets, scores, idMapping, members, membership, random) + public fun newDomain(): VmInterferenceDomain = InterferenceDomainImpl(idMapping, members, membership, targets, scores, random) /** * Create a copy of this model with a different seed. */ public fun withSeed(seed: Long): VmInterferenceModel { - return VmInterferenceModel(targets, scores, idMapping, members, membership, size, seed) + return VmInterferenceModel(idMapping, members, membership, targets, scores, size, seed) } public companion object { @@ -171,11 +171,11 @@ public class VmInterferenceModel private constructor( @Suppress("UNCHECKED_CAST") return VmInterferenceModel( - newTargets, - newScores, idMapping, newMembers as Array, membership.map { it.value.toIntArray() }.toTypedArray(), + newTargets, + newScores, size, seed ) @@ -197,127 +197,62 @@ public class VmInterferenceModel private constructor( * Internal implementation of [VmInterferenceDomain]. */ private class InterferenceDomainImpl( - private val targets: DoubleArray, - private val scores: DoubleArray, private val idMapping: Map, private val members: Array, private val membership: Array, + private val targets: DoubleArray, + private val scores: DoubleArray, private val random: SplittableRandom ) : VmInterferenceDomain { /** * Keys registered with this domain. */ - private val keys = HashMap() + private val keys = HashMap() /** * The set of keys active in this domain. */ - private val activeKeys = ArrayList() - - override fun createKey(id: String): VmInterferenceKey? { - val intId = idMapping[id] ?: return null - return keys.computeIfAbsent(intId) { InterferenceKeyImpl(intId) } - } - - override fun removeKey(key: VmInterferenceKey) { - if (key !is InterferenceKeyImpl) { - return - } + private val activeKeys = ArrayList() - if (activeKeys.remove(key)) { - computeActiveGroups(key.id) - } + /** + * Queue of participants that will be removed or added to the active groups. + */ + private val participants = ArrayDeque() - keys.remove(key.id) + override fun getMember(id: String): VmInterferenceMember? { + val intId = idMapping[id] ?: return null + return keys.computeIfAbsent(intId) { InterferenceMemberImpl(it, this, members, targets, scores, random) } } - override fun join(key: VmInterferenceKey) { - if (key !is InterferenceKeyImpl) { - return - } + override fun toString(): String = "VmInterferenceDomain" - if (key.acquire()) { - val pos = activeKeys.binarySearch(key) - if (pos < 0) { - activeKeys.add(-pos - 1, key) - } - computeActiveGroups(key.id) + fun join(key: InterferenceMemberImpl) { + val activeKeys = activeKeys + val pos = activeKeys.binarySearch(key) + if (pos < 0) { + activeKeys.add(-pos - 1, key) } - } - override fun leave(key: VmInterferenceKey) { - if (key is InterferenceKeyImpl && key.release()) { - activeKeys.remove(key) - computeActiveGroups(key.id) - } + computeActiveGroups(activeKeys, key.id) } - override fun apply(key: VmInterferenceKey?, load: Double): Double { - if (key == null || key !is InterferenceKeyImpl) { - return 1.0 - } - - val groups = key.groups - val groupSize = groups.size - - if (groupSize == 0) { - return 1.0 - } - - val targets = targets - val scores = scores - var low = 0 - var high = groups.size - 1 - - var group = -1 - var score = 1.0 - - // Perform binary search over the groups based on target load - while (low <= high) { - val mid = low + high ushr 1 - val midGroup = groups[mid] - val target = targets[midGroup] - - if (target < load) { - low = mid + 1 - group = midGroup - score = scores[midGroup] - } else if (target > load) { - high = mid - 1 - } else { - group = midGroup - score = scores[midGroup] - break - } - } - - return if (group >= 0 && random.nextInt(members[group].size) == 0) { - score - } else { - 1.0 - } + fun leave(key: InterferenceMemberImpl) { + val activeKeys = activeKeys + activeKeys.remove(key) + computeActiveGroups(activeKeys, key.id) } - override fun toString(): String = "VmInterferenceDomain" - - /** - * Queue of participants that will be removed or added to the active groups. - */ - private val _participants = ArrayDeque() - /** - * (Re-)Compute the active groups. + * (Re-)compute the active groups. */ - private fun computeActiveGroups(id: Int) { - val activeKeys = activeKeys - val groups = membership[id] - + private fun computeActiveGroups(activeKeys: ArrayList, id: Int) { if (activeKeys.isEmpty()) { return } + val groups = membership[id] val members = members - val participants = _participants + val participants = participants for (group in groups) { val groupMembers = members[group] @@ -366,7 +301,14 @@ public class VmInterferenceModel private constructor( * * @param id The identifier of the member. */ - private class InterferenceKeyImpl(@JvmField val id: Int) : VmInterferenceKey, Comparable { + private class InterferenceMemberImpl( + @JvmField val id: Int, + private val domain: InterferenceDomainImpl, + private val members: Array, + private val targets: DoubleArray, + private val scores: DoubleArray, + private val random: SplittableRandom + ) : VmInterferenceMember, Comparable { /** * The active groups to which the key belongs. */ @@ -377,16 +319,57 @@ public class VmInterferenceModel private constructor( */ private var refCount: Int = 0 - /** - * Join the domain. - */ - fun acquire(): Boolean = refCount++ <= 0 + override fun activate() { + if (refCount++ <= 0) { + domain.join(this) + } + } - /** - * Leave the domain. - */ - fun release(): Boolean = --refCount <= 0 + override fun deactivate() { + if (--refCount <= 0) { + domain.leave(this) + } + } + + override fun apply(load: Double): Double { + val groups = groups + val groupSize = groups.size + + if (groupSize == 0) { + return 1.0 + } + + val targets = targets + val scores = scores + var low = 0 + var high = groups.size - 1 + + var group = -1 + + // Perform binary search over the groups based on target load + while (low <= high) { + val mid = low + high ushr 1 + val midGroup = groups[mid] + val target = targets[midGroup] + + if (target < load) { + low = mid + 1 + group = midGroup + } else if (target > load) { + high = mid - 1 + } else { + group = midGroup + break + } + } + + return if (group >= 0 && random.nextInt(members[group].size) == 0) { + scores[group] + } else { + 1.0 + } + } - override fun compareTo(other: InterferenceKeyImpl): Int = id.compareTo(other.id) + override fun compareTo(other: InterferenceMemberImpl): Int = id.compareTo(other.id) } } 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 ab2a6d76..15d32002 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 @@ -77,7 +77,7 @@ internal class SimFairShareHypervisorTest { val platform = FlowEngine(coroutineContext, clock) val machine = SimBareMetalMachine(platform, model, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimFairShareHypervisor(platform, PerformanceScalingGovernor(), null) + val hypervisor = SimFairShareHypervisor(platform, PerformanceScalingGovernor()) launch { machine.runWorkload(hypervisor) @@ -128,7 +128,7 @@ internal class SimFairShareHypervisorTest { val machine = SimBareMetalMachine( platform, model, SimplePowerDriver(ConstantPowerModel(0.0)) ) - val hypervisor = SimFairShareHypervisor(platform, null, null) + val hypervisor = SimFairShareHypervisor(platform, null) launch { machine.runWorkload(hypervisor) @@ -167,7 +167,7 @@ internal class SimFairShareHypervisorTest { val platform = FlowEngine(coroutineContext, clock) val machine = SimBareMetalMachine(platform, model, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimFairShareHypervisor(platform, null, null) + val hypervisor = SimFairShareHypervisor(platform, null) assertDoesNotThrow { launch { @@ -197,7 +197,7 @@ internal class SimFairShareHypervisorTest { val machine = SimBareMetalMachine( platform, model, SimplePowerDriver(ConstantPowerModel(0.0)) ) - val hypervisor = SimFairShareHypervisor(platform, null, interferenceDomain) + val hypervisor = SimFairShareHypervisor(platform, null) val duration = 5 * 60L val workloadA = @@ -225,11 +225,11 @@ internal class SimFairShareHypervisorTest { coroutineScope { launch { - val vm = hypervisor.newMachine(model, interferenceDomain.createKey("a")) + val vm = hypervisor.newMachine(model, interferenceDomain.getMember("a")) vm.runWorkload(workloadA) hypervisor.removeMachine(vm) } - val vm = hypervisor.newMachine(model, interferenceDomain.createKey("b")) + val vm = hypervisor.newMachine(model, interferenceDomain.getMember("b")) vm.runWorkload(workloadB) hypervisor.removeMachine(vm) } -- cgit v1.2.3