From 6171ab09f1df2ab3475a7b28ece383a9f87a77c5 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Thu, 22 Sep 2022 10:28:37 +0200 Subject: refactor(sim/compute): Extract Random dependency from interference model This change moves the Random dependency outside the interference model, to allow the interference model to be completely immutable and passable between different simulations. --- .../kotlin/org/opendc/compute/simulator/SimHost.kt | 3 +- .../org/opendc/compute/simulator/SimHostTest.kt | 8 +++- .../compute/workload/ComputeServiceHelper.kt | 24 +++++++----- .../experiments/capelin/CapelinBenchmarks.kt | 5 ++- .../opendc/experiments/capelin/CapelinRunner.kt | 5 ++- .../experiments/capelin/CapelinIntegrationTest.kt | 37 +++++++++++-------- .../simulator/compute/SimMachineBenchmarks.kt | 10 +++-- .../compute/kernel/SimAbstractHypervisor.kt | 17 ++++++--- .../compute/kernel/SimFairShareHypervisor.kt | 7 +++- .../kernel/SimFairShareHypervisorProvider.kt | 4 +- .../compute/kernel/SimHypervisorProvider.kt | 3 +- .../compute/kernel/SimSpaceSharedHypervisor.kt | 8 +++- .../kernel/SimSpaceSharedHypervisorProvider.kt | 4 +- .../kernel/interference/VmInterferenceMember.kt | 5 ++- .../kernel/interference/VmInterferenceModel.kt | 43 +++++++--------------- .../compute/kernel/SimFairShareHypervisorTest.kt | 13 +++++-- .../compute/kernel/SimSpaceSharedHypervisorTest.kt | 19 +++++++--- .../kotlin/org/opendc/web/runner/OpenDCRunner.kt | 3 +- .../opendc/workflow/service/WorkflowServiceTest.kt | 1 + 19 files changed, 130 insertions(+), 89 deletions(-) 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 628f324b..ece3f752 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 @@ -62,6 +62,7 @@ public class SimHost( context: CoroutineContext, engine: FlowEngine, hypervisorProvider: SimHypervisorProvider, + random: SplittableRandom, scalingGovernor: ScalingGovernor = PerformanceScalingGovernor(), powerDriver: PowerDriver = SimplePowerDriver(ConstantPowerModel(0.0)), private val mapper: SimWorkloadMapper = SimMetaWorkloadMapper(), @@ -92,7 +93,7 @@ public class SimHost( * The hypervisor to run multiple workloads. */ private val hypervisor: SimHypervisor = hypervisorProvider - .create(engine, scalingGovernor = scalingGovernor) + .create(engine, random, scalingGovernor = scalingGovernor) /** * The virtual machines running on the hypervisor. diff --git a/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimHostTest.kt b/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimHostTest.kt index 5ba4a667..0b2285e5 100644 --- a/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimHostTest.kt +++ b/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimHostTest.kt @@ -67,6 +67,7 @@ internal class SimHostTest { fun testOvercommitted() = runBlockingSimulation { val duration = 5 * 60L val engine = FlowEngine(coroutineContext, clock) + val random = SplittableRandom(1) val host = SimHost( uid = UUID.randomUUID(), name = "test", @@ -74,7 +75,8 @@ internal class SimHostTest { meta = emptyMap(), coroutineContext, engine, - SimFairShareHypervisorProvider() + SimFairShareHypervisorProvider(), + random, ) val vmImageA = MockImage( UUID.randomUUID(), @@ -149,6 +151,7 @@ internal class SimHostTest { fun testFailure() = runBlockingSimulation { val duration = 5 * 60L val engine = FlowEngine(coroutineContext, clock) + val random = SplittableRandom(1) val host = SimHost( uid = UUID.randomUUID(), name = "test", @@ -156,7 +159,8 @@ internal class SimHostTest { meta = emptyMap(), coroutineContext, engine, - SimFairShareHypervisorProvider() + SimFairShareHypervisorProvider(), + random ) val image = MockImage( UUID.randomUUID(), diff --git a/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/ComputeServiceHelper.kt b/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/ComputeServiceHelper.kt index fddb4890..879ef072 100644 --- a/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/ComputeServiceHelper.kt +++ b/opendc-compute/opendc-compute-workload/src/main/kotlin/org/opendc/compute/workload/ComputeServiceHelper.kt @@ -54,6 +54,7 @@ public class ComputeServiceHelper( private val context: CoroutineContext, private val clock: Clock, scheduler: ComputeScheduler, + seed: Long, private val failureModel: FailureModel? = null, private val interferenceModel: VmInterferenceModel? = null, schedulingQuantum: Duration = Duration.ofMinutes(5) @@ -66,12 +67,17 @@ public class ComputeServiceHelper( /** * The [FlowEngine] to simulate the hosts. */ - private val _engine = FlowEngine(context, clock) + private val engine = FlowEngine(context, clock) /** * The hosts that belong to this class. */ - private val _hosts = mutableSetOf() + private val hosts = mutableSetOf() + + /** + * The source of randomness. + */ + private val random = SplittableRandom(seed) init { val service = createService(scheduler, schedulingQuantum) @@ -82,18 +88,15 @@ public class ComputeServiceHelper( * Run a simulation of the [ComputeService] by replaying the workload trace given by [trace]. * * @param trace The trace to simulate. - * @param seed The seed for the simulation. * @param servers A list to which the created servers is added. * @param submitImmediately A flag to indicate that the servers are scheduled immediately (so not at their start time). */ public suspend fun run( trace: List, - seed: Long, servers: MutableList? = null, submitImmediately: Boolean = false ) { - val random = Random(seed) - val injector = failureModel?.createInjector(context, clock, service, random) + val injector = failureModel?.createInjector(context, clock, service, Random(random.nextLong())) val client = service.newClient() // Create new image for the virtual machine @@ -170,14 +173,15 @@ public class ComputeServiceHelper( spec.model, spec.meta, context, - _engine, + engine, spec.hypervisor, + random, powerDriver = spec.powerDriver, interferenceDomain = interferenceModel?.newDomain(), optimize = optimize ) - require(_hosts.add(host)) { "Host with uid ${spec.uid} already exists" } + require(hosts.add(host)) { "Host with uid ${spec.uid} already exists" } service.addHost(host) return host @@ -186,11 +190,11 @@ public class ComputeServiceHelper( override fun close() { service.close() - for (host in _hosts) { + for (host in hosts) { host.close() } - _hosts.clear() + hosts.clear() } /** diff --git a/opendc-experiments/opendc-experiments-capelin/src/jmh/kotlin/org/opendc/experiments/capelin/CapelinBenchmarks.kt b/opendc-experiments/opendc-experiments-capelin/src/jmh/kotlin/org/opendc/experiments/capelin/CapelinBenchmarks.kt index fd2c26f0..074ffc3e 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/jmh/kotlin/org/opendc/experiments/capelin/CapelinBenchmarks.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/jmh/kotlin/org/opendc/experiments/capelin/CapelinBenchmarks.kt @@ -68,12 +68,13 @@ class CapelinBenchmarks { val runner = ComputeServiceHelper( coroutineContext, clock, - computeScheduler + computeScheduler, + seed = 0L, ) try { runner.apply(topology, isOptimized) - runner.run(vms, 0) + runner.run(vms) } finally { runner.close() } diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinRunner.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinRunner.kt index cca5b6cf..2f417172 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinRunner.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/CapelinRunner.kt @@ -73,8 +73,9 @@ public class CapelinRunner( coroutineContext, clock, computeScheduler, + seed, failureModel, - interferenceModel?.withSeed(seed) + interferenceModel?.takeIf { operationalPhenomena.hasInterference } ) val topology = clusterTopology(File(envPath, "${scenario.topology.name}.txt")) @@ -104,7 +105,7 @@ public class CapelinRunner( runner.apply(topology, optimize = true) // Run the workload trace - runner.run(vms, seeder.nextLong(), servers) + runner.run(vms, servers) // Stop the metric collection exporter?.close() diff --git a/opendc-experiments/opendc-experiments-capelin/src/test/kotlin/org/opendc/experiments/capelin/CapelinIntegrationTest.kt b/opendc-experiments/opendc-experiments-capelin/src/test/kotlin/org/opendc/experiments/capelin/CapelinIntegrationTest.kt index 368b0086..ff9faef7 100644 --- a/opendc-experiments/opendc-experiments-capelin/src/test/kotlin/org/opendc/experiments/capelin/CapelinIntegrationTest.kt +++ b/opendc-experiments/opendc-experiments-capelin/src/test/kotlin/org/opendc/experiments/capelin/CapelinIntegrationTest.kt @@ -81,11 +81,13 @@ class CapelinIntegrationTest { */ @Test fun testLarge() = runBlockingSimulation { - val (workload, _) = createTestWorkload(1.0) + val seed = 0L + val (workload, _) = createTestWorkload(1.0, seed) val runner = ComputeServiceHelper( coroutineContext, clock, - computeScheduler + computeScheduler, + seed, ) val topology = createTopology() @@ -94,7 +96,7 @@ class CapelinIntegrationTest { try { runner.apply(topology) - runner.run(workload, 0, servers) + runner.run(workload, servers) val serviceMetrics = runner.service.getSchedulerStats() println( @@ -129,12 +131,13 @@ class CapelinIntegrationTest { */ @Test fun testSmall() = runBlockingSimulation { - val seed = 1 + val seed = 1L val (workload, _) = createTestWorkload(0.25, seed) val runner = ComputeServiceHelper( coroutineContext, clock, - computeScheduler + computeScheduler, + seed, ) val topology = createTopology("single") val servers = mutableListOf() @@ -142,7 +145,7 @@ class CapelinIntegrationTest { try { runner.apply(topology) - runner.run(workload, seed.toLong(), servers) + runner.run(workload, servers) val serviceMetrics = runner.service.getSchedulerStats() println( @@ -173,14 +176,15 @@ class CapelinIntegrationTest { */ @Test fun testInterference() = runBlockingSimulation { - val seed = 0 + val seed = 0L val (workload, interferenceModel) = createTestWorkload(1.0, seed) val simulator = ComputeServiceHelper( coroutineContext, clock, computeScheduler, - interferenceModel = interferenceModel?.withSeed(seed.toLong()) + seed, + interferenceModel = interferenceModel ) val topology = createTopology("single") val servers = mutableListOf() @@ -188,7 +192,7 @@ class CapelinIntegrationTest { try { simulator.apply(topology) - simulator.run(workload, seed.toLong(), servers) + simulator.run(workload, servers) val serviceMetrics = simulator.service.getSchedulerStats() println( @@ -218,11 +222,12 @@ class CapelinIntegrationTest { */ @Test fun testFailures() = runBlockingSimulation { - val seed = 1 + val seed = 0L val simulator = ComputeServiceHelper( coroutineContext, clock, computeScheduler, + seed, grid5000(Duration.ofDays(7)) ) val topology = createTopology("single") @@ -232,7 +237,7 @@ class CapelinIntegrationTest { try { simulator.apply(topology) - simulator.run(workload, seed.toLong(), servers) + simulator.run(workload, servers) val serviceMetrics = simulator.service.getSchedulerStats() println( @@ -250,20 +255,20 @@ class CapelinIntegrationTest { // Note that these values have been verified beforehand assertAll( - { assertEquals(10867345, monitor.idleTime) { "Idle time incorrect" } }, - { assertEquals(9607095, monitor.activeTime) { "Active time incorrect" } }, + { assertEquals(10982026, monitor.idleTime) { "Idle time incorrect" } }, + { assertEquals(9740058, monitor.activeTime) { "Active time incorrect" } }, { assertEquals(0, monitor.stealTime) { "Steal time incorrect" } }, { assertEquals(0, monitor.lostTime) { "Lost time incorrect" } }, - { assertEquals(2559305056, monitor.uptime) { "Uptime incorrect" } } + { assertEquals(2590260605, monitor.uptime) { "Uptime incorrect" } }, ) } /** * Obtain the trace reader for the test. */ - private fun createTestWorkload(fraction: Double, seed: Int = 0): ComputeWorkload.Resolved { + private fun createTestWorkload(fraction: Double, seed: Long): ComputeWorkload.Resolved { val source = trace("bitbrains-small").sampleByLoad(fraction) - return source.resolve(workloadLoader, Random(seed.toLong())) + return source.resolve(workloadLoader, Random(seed)) } /** 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 02b48fa7..c3332d66 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 @@ -37,6 +37,7 @@ import org.opendc.simulator.compute.workload.SimTraceWorkload import org.opendc.simulator.core.runBlockingSimulation import org.opendc.simulator.flow.FlowEngine import org.openjdk.jmh.annotations.* +import java.util.SplittableRandom import java.util.concurrent.ThreadLocalRandom import java.util.concurrent.TimeUnit @@ -85,7 +86,8 @@ class SimMachineBenchmarks { val machine = SimBareMetalMachine( engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0)) ) - val hypervisor = SimSpaceSharedHypervisor(engine, null) + val random = SplittableRandom(1) + val hypervisor = SimSpaceSharedHypervisor(engine, null, random) launch { machine.runWorkload(hypervisor) } @@ -107,7 +109,8 @@ class SimMachineBenchmarks { val machine = SimBareMetalMachine( engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0)) ) - val hypervisor = SimFairShareHypervisor(engine, null) + val random = SplittableRandom(1) + val hypervisor = SimFairShareHypervisor(engine, null, random) launch { machine.runWorkload(hypervisor) } @@ -129,7 +132,8 @@ class SimMachineBenchmarks { val machine = SimBareMetalMachine( engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0)) ) - val hypervisor = SimFairShareHypervisor(engine, null) + val random = SplittableRandom(1) + val hypervisor = SimFairShareHypervisor(engine, null, random) 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 98dab28f..2cabeece 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 @@ -31,6 +31,7 @@ import org.opendc.simulator.compute.model.ProcessingUnit import org.opendc.simulator.compute.workload.SimWorkload import org.opendc.simulator.flow.* import org.opendc.simulator.flow.mux.FlowMultiplexer +import java.util.SplittableRandom import kotlin.math.roundToLong /** @@ -38,10 +39,12 @@ import kotlin.math.roundToLong * * @param engine The [FlowEngine] to drive the simulation. * @param scalingGovernor The scaling governor to use for scaling the CPU frequency of the underlying hardware. + * @param random A randomness generator for the interference calculations. */ public abstract class SimAbstractHypervisor( protected val engine: FlowEngine, - private val scalingGovernor: ScalingGovernor? + private val scalingGovernor: ScalingGovernor?, + private val random: SplittableRandom ) : SimHypervisor, FlowConvergenceListener { /** * The machine on which the hypervisor runs. @@ -142,8 +145,12 @@ public abstract class SimAbstractHypervisor( if (delta > 0) { _counters.record() + val mux = mux + val load = mux.rate / mux.capacity.coerceAtLeast(1.0) + val random = random + for (vm in _vms) { - vm._counters.record() + vm._counters.record(random, load) } } @@ -351,7 +358,7 @@ public abstract class SimAbstractHypervisor( /** * Record the CPU time of the hypervisor. */ - fun record() { + fun record(random: SplittableRandom, load: Double) { val cpuTime = _cpuTime val previous = _previous @@ -383,9 +390,7 @@ public abstract class SimAbstractHypervisor( // Compute the performance penalty due to flow interference val key = key if (key != null) { - val mux = mux - val load = mux.rate / mux.capacity.coerceAtLeast(1.0) - val penalty = 1 - key.apply(load) + val penalty = 1 - key.apply(random, 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 66453835..4435a422 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 @@ -29,6 +29,7 @@ import org.opendc.simulator.compute.workload.SimWorkload import org.opendc.simulator.flow.FlowEngine import org.opendc.simulator.flow.mux.FlowMultiplexer import org.opendc.simulator.flow.mux.MaxMinFlowMultiplexer +import java.util.SplittableRandom /** * A [SimHypervisor] that distributes the computing requirements of multiple [SimWorkload]s on a single [SimMachine] @@ -36,11 +37,13 @@ import org.opendc.simulator.flow.mux.MaxMinFlowMultiplexer * * @param engine The [FlowEngine] to manage the machine's resources. * @param scalingGovernor The CPU frequency scaling governor to use for the hypervisor. + * @param random A randomness generator for the interference calculations. */ public class SimFairShareHypervisor( engine: FlowEngine, - scalingGovernor: ScalingGovernor? -) : SimAbstractHypervisor(engine, scalingGovernor) { + scalingGovernor: ScalingGovernor?, + random: SplittableRandom +) : SimAbstractHypervisor(engine, scalingGovernor, random) { /** * 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 ad8177d3..c7008652 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 @@ -24,6 +24,7 @@ package org.opendc.simulator.compute.kernel import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernor import org.opendc.simulator.flow.FlowEngine +import java.util.* /** * A [SimHypervisorProvider] for the [SimFairShareHypervisor] implementation. @@ -33,6 +34,7 @@ public class SimFairShareHypervisorProvider : SimHypervisorProvider { override fun create( engine: FlowEngine, + random: SplittableRandom, scalingGovernor: ScalingGovernor?, - ): SimHypervisor = SimFairShareHypervisor(engine, scalingGovernor) + ): SimHypervisor = SimFairShareHypervisor(engine, scalingGovernor, random) } 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 6ee523fd..020a2a60 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 @@ -24,6 +24,7 @@ package org.opendc.simulator.compute.kernel import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernor import org.opendc.simulator.flow.FlowEngine +import java.util.SplittableRandom /** * A service provider interface for constructing a [SimHypervisor]. @@ -40,5 +41,5 @@ public interface SimHypervisorProvider { /** * Create a new [SimHypervisor] instance. */ - public fun create(engine: FlowEngine, scalingGovernor: ScalingGovernor? = null): SimHypervisor + public fun create(engine: FlowEngine, random: SplittableRandom, scalingGovernor: ScalingGovernor? = null): SimHypervisor } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisor.kt index 7976077c..51bf4ce5 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisor.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisor.kt @@ -27,14 +27,20 @@ import org.opendc.simulator.compute.model.MachineModel import org.opendc.simulator.flow.FlowEngine import org.opendc.simulator.flow.mux.FlowMultiplexer import org.opendc.simulator.flow.mux.ForwardingFlowMultiplexer +import java.util.SplittableRandom /** * A [SimHypervisor] that allocates its sub-resources exclusively for the virtual machine that it hosts. + * + * @param engine The [FlowEngine] to manage the machine's resources. + * @param scalingGovernor The CPU frequency scaling governor to use for the hypervisor. + * @param random A randomness generator for the interference calculations. */ public class SimSpaceSharedHypervisor( engine: FlowEngine, scalingGovernor: ScalingGovernor?, -) : SimAbstractHypervisor(engine, scalingGovernor) { + random: SplittableRandom +) : SimAbstractHypervisor(engine, scalingGovernor, random) { override val mux: FlowMultiplexer = ForwardingFlowMultiplexer(engine, this) override fun canFit(model: MachineModel): Boolean { 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 f7456797..05c54528 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 @@ -24,6 +24,7 @@ package org.opendc.simulator.compute.kernel import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernor import org.opendc.simulator.flow.FlowEngine +import java.util.* /** * A [SimHypervisorProvider] for the [SimSpaceSharedHypervisor] implementation. @@ -33,6 +34,7 @@ public class SimSpaceSharedHypervisorProvider : SimHypervisorProvider { override fun create( engine: FlowEngine, + random: SplittableRandom, scalingGovernor: ScalingGovernor?, - ): SimHypervisor = SimSpaceSharedHypervisor(engine, scalingGovernor) + ): SimHypervisor = SimSpaceSharedHypervisor(engine, scalingGovernor, random) } 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 index 2f3dd74b..04203c63 100644 --- 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 @@ -22,6 +22,8 @@ package org.opendc.simulator.compute.kernel.interference +import java.util.* + /** * A participant of an interference domain. */ @@ -39,9 +41,10 @@ public interface VmInterferenceMember { /** * Compute the performance score of the member in this interference domain. * + * @param random The source of randomness to apply when computing the performance score. * @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 + public fun apply(random: SplittableRandom, 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 b9eee536..3ea869d4 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 @@ -32,7 +32,6 @@ import java.util.* * @param members The members belonging to each group. * @param membership The identifier of each key. * @param size The number of groups. - * @param seed The seed to use for randomly selecting the virtual machines that are affected. */ public class VmInterferenceModel private constructor( private val idMapping: Map, @@ -40,25 +39,12 @@ public class VmInterferenceModel private constructor( private val membership: Array, private val targets: DoubleArray, private val scores: DoubleArray, - private val size: Int, - seed: Long, + private val size: Int ) { - /** - * A [SplittableRandom] used for selecting the virtual machines that are affected. - */ - private val random = SplittableRandom(seed) - /** * Construct a new [VmInterferenceDomain]. */ - 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(idMapping, members, membership, targets, scores, size, seed) - } + public fun newDomain(): VmInterferenceDomain = InterferenceDomainImpl(idMapping, members, membership, targets, scores) public companion object { /** @@ -72,11 +58,6 @@ public class VmInterferenceModel private constructor( * Builder class for a [VmInterferenceModel] */ public class Builder internal constructor() { - /** - * The initial capacity of the builder. - */ - private val INITIAL_CAPACITY = 256 - /** * The target load of each group. */ @@ -125,7 +106,7 @@ public class VmInterferenceModel private constructor( /** * Build the [VmInterferenceModel]. */ - public fun build(seed: Long = 0): VmInterferenceModel { + public fun build(): VmInterferenceModel { val size = size val targets = _targets val scores = _scores @@ -176,8 +157,7 @@ public class VmInterferenceModel private constructor( membership.map { it.value.toIntArray() }.toTypedArray(), newTargets, newScores, - size, - seed + size ) } @@ -191,6 +171,13 @@ public class VmInterferenceModel private constructor( _targets = _targets.copyOf(newSize) _scores = _scores.copyOf(newSize) } + + private companion object { + /** + * The initial capacity of the builder. + */ + const val INITIAL_CAPACITY = 256 + } } /** @@ -202,7 +189,6 @@ public class VmInterferenceModel private constructor( private val membership: Array, private val targets: DoubleArray, private val scores: DoubleArray, - private val random: SplittableRandom ) : VmInterferenceDomain { /** * Keys registered with this domain. @@ -221,7 +207,7 @@ public class VmInterferenceModel private constructor( override fun getMember(id: String): VmInterferenceMember? { val intId = idMapping[id] ?: return null - return keys.computeIfAbsent(intId) { InterferenceMemberImpl(it, this, membership[it], members, targets, scores, random) } + return keys.computeIfAbsent(intId) { InterferenceMemberImpl(it, this, membership[it], members, targets, scores) } } override fun toString(): String = "VmInterferenceDomain" @@ -307,8 +293,7 @@ public class VmInterferenceModel private constructor( @JvmField val membership: IntArray, private val members: Array, private val targets: DoubleArray, - private val scores: DoubleArray, - private val random: SplittableRandom + private val scores: DoubleArray ) : VmInterferenceMember, Comparable { /** * The active groups to which the key belongs. @@ -333,7 +318,7 @@ public class VmInterferenceModel private constructor( } } - override fun apply(load: Double): Double { + override fun apply(random: SplittableRandom, load: Double): Double { val groupsSize = groupsSize if (groupsSize == 0) { 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 15d32002..23d832e8 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 @@ -43,6 +43,7 @@ import org.opendc.simulator.compute.workload.SimTraceFragment import org.opendc.simulator.compute.workload.SimTraceWorkload import org.opendc.simulator.core.runBlockingSimulation import org.opendc.simulator.flow.FlowEngine +import java.util.* /** * Test suite for the [SimHypervisor] class. @@ -77,7 +78,8 @@ internal class SimFairShareHypervisorTest { val platform = FlowEngine(coroutineContext, clock) val machine = SimBareMetalMachine(platform, model, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimFairShareHypervisor(platform, PerformanceScalingGovernor()) + val random = SplittableRandom(1) + val hypervisor = SimFairShareHypervisor(platform, PerformanceScalingGovernor(), random) launch { machine.runWorkload(hypervisor) @@ -128,7 +130,8 @@ internal class SimFairShareHypervisorTest { val machine = SimBareMetalMachine( platform, model, SimplePowerDriver(ConstantPowerModel(0.0)) ) - val hypervisor = SimFairShareHypervisor(platform, null) + val random = SplittableRandom(1) + val hypervisor = SimFairShareHypervisor(platform, null, random) launch { machine.runWorkload(hypervisor) @@ -167,7 +170,8 @@ internal class SimFairShareHypervisorTest { val platform = FlowEngine(coroutineContext, clock) val machine = SimBareMetalMachine(platform, model, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimFairShareHypervisor(platform, null) + val random = SplittableRandom(1) + val hypervisor = SimFairShareHypervisor(platform, null, random) assertDoesNotThrow { launch { @@ -197,7 +201,8 @@ internal class SimFairShareHypervisorTest { val machine = SimBareMetalMachine( platform, model, SimplePowerDriver(ConstantPowerModel(0.0)) ) - val hypervisor = SimFairShareHypervisor(platform, null) + val random = SplittableRandom(1) + val hypervisor = SimFairShareHypervisor(platform, null, random) val duration = 5 * 60L val workloadA = diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt index 0f533130..9471f548 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt @@ -40,6 +40,7 @@ import org.opendc.simulator.compute.runWorkload import org.opendc.simulator.compute.workload.* import org.opendc.simulator.core.runBlockingSimulation import org.opendc.simulator.flow.FlowEngine +import java.util.* /** * A test suite for the [SimSpaceSharedHypervisor]. @@ -75,7 +76,8 @@ internal class SimSpaceSharedHypervisorTest { val engine = FlowEngine(coroutineContext, clock) val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimSpaceSharedHypervisor(engine, null) + val random = SplittableRandom(1) + val hypervisor = SimSpaceSharedHypervisor(engine, null, random) launch { machine.runWorkload(hypervisor) } val vm = hypervisor.newMachine(machineModel) @@ -97,7 +99,8 @@ internal class SimSpaceSharedHypervisorTest { val workload = SimRuntimeWorkload(duration) val engine = FlowEngine(coroutineContext, clock) val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimSpaceSharedHypervisor(engine, null) + val random = SplittableRandom(1) + val hypervisor = SimSpaceSharedHypervisor(engine, null, random) launch { machine.runWorkload(hypervisor) } yield() @@ -121,7 +124,8 @@ internal class SimSpaceSharedHypervisorTest { val machine = SimBareMetalMachine( engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0)) ) - val hypervisor = SimSpaceSharedHypervisor(engine, null) + val random = SplittableRandom(1) + val hypervisor = SimSpaceSharedHypervisor(engine, null, random) launch { machine.runWorkload(hypervisor) } yield() @@ -142,7 +146,8 @@ internal class SimSpaceSharedHypervisorTest { val machine = SimBareMetalMachine( engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0)) ) - val hypervisor = SimSpaceSharedHypervisor(engine, null) + val random = SplittableRandom(1) + val hypervisor = SimSpaceSharedHypervisor(engine, null, random) launch { machine.runWorkload(hypervisor) } yield() @@ -169,7 +174,8 @@ internal class SimSpaceSharedHypervisorTest { fun testConcurrentWorkloadFails() = runBlockingSimulation { val engine = FlowEngine(coroutineContext, clock) val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimSpaceSharedHypervisor(engine, null) + val random = SplittableRandom(1) + val hypervisor = SimSpaceSharedHypervisor(engine, null, random) launch { machine.runWorkload(hypervisor) } yield() @@ -193,7 +199,8 @@ internal class SimSpaceSharedHypervisorTest { val machine = SimBareMetalMachine( interpreter, machineModel, SimplePowerDriver(ConstantPowerModel(0.0)) ) - val hypervisor = SimSpaceSharedHypervisor(interpreter, null) + val random = SplittableRandom(1) + val hypervisor = SimSpaceSharedHypervisor(interpreter, null, random) launch { machine.runWorkload(hypervisor) } yield() diff --git a/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/OpenDCRunner.kt b/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/OpenDCRunner.kt index c958bdb2..d5dbed1c 100644 --- a/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/OpenDCRunner.kt +++ b/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/OpenDCRunner.kt @@ -220,6 +220,7 @@ public class OpenDCRunner( coroutineContext, clock, computeScheduler, + seed = 0L, failureModel, interferenceModel.takeIf { phenomena.interference } ) @@ -230,7 +231,7 @@ public class OpenDCRunner( // Instantiate the topology onto the simulator simulator.apply(topology) // Run workload trace - simulator.run(vms, seeder.nextLong(), servers) + simulator.run(vms, servers) val serviceMetrics = simulator.service.getSchedulerStats() logger.debug { diff --git a/opendc-workflow/opendc-workflow-service/src/test/kotlin/org/opendc/workflow/service/WorkflowServiceTest.kt b/opendc-workflow/opendc-workflow-service/src/test/kotlin/org/opendc/workflow/service/WorkflowServiceTest.kt index 73d1b23b..f6fa2134 100644 --- a/opendc-workflow/opendc-workflow-service/src/test/kotlin/org/opendc/workflow/service/WorkflowServiceTest.kt +++ b/opendc-workflow/opendc-workflow-service/src/test/kotlin/org/opendc/workflow/service/WorkflowServiceTest.kt @@ -73,6 +73,7 @@ internal class WorkflowServiceTest { coroutineContext, clock, computeScheduler, + seed = 0, schedulingQuantum = Duration.ofSeconds(1) ) -- cgit v1.2.3