From 44215bd668c5fa3efe2f57fc577824478b00af57 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Thu, 1 Sep 2022 14:38:34 +0200 Subject: refactor(sim/compute): Re-implement using flow2 This change re-implements the OpenDC compute simulator framework using the new flow2 framework for modelling multi-edge flow networks. The re-implementation is written in Java and focusses on performance and clean API surface. --- .../simulator/compute/SimMachineBenchmarks.kt | 53 +- .../simulator/compute/SimAbstractMachine.java | 327 ++++++++ .../simulator/compute/SimBareMetalMachine.java | 298 +++++++ .../org/opendc/simulator/compute/SimMachine.java | 59 ++ .../simulator/compute/SimMachineContext.java | 74 ++ .../org/opendc/simulator/compute/SimMemory.java | 42 + .../simulator/compute/SimNetworkInterface.java | 51 ++ .../simulator/compute/SimProcessingUnit.java | 62 ++ .../java/org/opendc/simulator/compute/SimPsu.java | 71 ++ .../opendc/simulator/compute/SimPsuFactories.java | 214 +++++ .../opendc/simulator/compute/SimPsuFactory.java | 38 + .../simulator/compute/SimStorageInterface.java | 50 ++ .../compute/device/SimNetworkAdapter.java | 36 + .../simulator/compute/device/SimPeripheral.java | 33 + .../simulator/compute/kernel/SimHypervisor.java | 911 +++++++++++++++++++++ .../compute/kernel/SimHypervisorCounters.java | 53 ++ .../compute/kernel/SimVirtualMachine.java | 50 ++ .../compute/kernel/cpufreq/ScalingGovernor.java | 46 ++ .../kernel/cpufreq/ScalingGovernorFactory.java | 33 + .../compute/kernel/cpufreq/ScalingGovernors.java | 190 +++++ .../compute/kernel/cpufreq/ScalingPolicy.java | 56 ++ .../kernel/interference/VmInterferenceDomain.java | 136 +++ .../kernel/interference/VmInterferenceMember.java | 177 ++++ .../kernel/interference/VmInterferenceModel.java | 185 +++++ .../kernel/interference/VmInterferenceProfile.java | 60 ++ .../simulator/compute/model/MachineModel.java | 149 ++++ .../opendc/simulator/compute/model/MemoryUnit.java | 100 +++ .../simulator/compute/model/NetworkAdapter.java | 88 ++ .../simulator/compute/model/ProcessingNode.java | 100 +++ .../simulator/compute/model/ProcessingUnit.java | 86 ++ .../simulator/compute/model/StorageDevice.java | 112 +++ .../simulator/compute/power/CpuPowerModel.java | 38 + .../simulator/compute/power/CpuPowerModels.java | 330 ++++++++ .../compute/workload/SimFlopsWorkload.java | 141 ++++ .../compute/workload/SimRuntimeWorkload.java | 131 +++ .../simulator/compute/workload/SimTrace.java | 303 +++++++ .../compute/workload/SimTraceFragment.java | 94 +++ .../simulator/compute/workload/SimWorkload.java | 48 ++ .../compute/workload/SimWorkloadLifecycle.java | 60 ++ .../opendc/simulator/compute/SimAbstractMachine.kt | 239 ------ .../simulator/compute/SimBareMetalMachine.kt | 130 --- .../org/opendc/simulator/compute/SimMachine.kt | 59 -- .../opendc/simulator/compute/SimMachineContext.kt | 60 -- .../org/opendc/simulator/compute/SimMemory.kt | 36 - .../simulator/compute/SimNetworkInterface.kt | 51 -- .../opendc/simulator/compute/SimProcessingUnit.kt | 41 - .../simulator/compute/SimStorageInterface.kt | 50 -- .../simulator/compute/device/SimNetworkAdapter.kt | 36 - .../simulator/compute/device/SimPeripheral.kt | 33 - .../org/opendc/simulator/compute/device/SimPsu.kt | 114 --- .../simulator/compute/kernel/SimHypervisor.kt | 442 ---------- .../compute/kernel/SimHypervisorCounters.kt | 53 -- .../simulator/compute/kernel/SimVirtualMachine.kt | 50 -- .../kernel/cpufreq/ConservativeScalingGovernor.kt | 66 -- .../kernel/cpufreq/OnDemandScalingGovernor.kt | 50 -- .../kernel/cpufreq/PerformanceScalingGovernor.kt | 36 - .../kernel/cpufreq/PowerSaveScalingGovernor.kt | 36 - .../compute/kernel/cpufreq/ScalingGovernor.kt | 56 -- .../compute/kernel/cpufreq/ScalingPolicy.kt | 51 -- .../kernel/interference/VmInterferenceDomain.kt | 131 --- .../kernel/interference/VmInterferenceMember.kt | 163 ---- .../kernel/interference/VmInterferenceModel.kt | 191 ----- .../kernel/interference/VmInterferenceProfile.kt | 51 -- .../opendc/simulator/compute/model/MachineModel.kt | 54 -- .../opendc/simulator/compute/model/MemoryUnit.kt | 38 - .../simulator/compute/model/NetworkAdapter.kt | 36 - .../simulator/compute/model/ProcessingNode.kt | 38 - .../simulator/compute/model/ProcessingUnit.kt | 36 - .../simulator/compute/model/StorageDevice.kt | 40 - .../compute/power/AsymptoticPowerModel.kt | 54 -- .../simulator/compute/power/ConstantPowerModel.kt | 32 - .../simulator/compute/power/CubicPowerModel.kt | 41 - .../compute/power/InterpolationPowerModel.kt | 64 -- .../simulator/compute/power/LinearPowerModel.kt | 42 - .../simulator/compute/power/MsePowerModel.kt | 49 -- .../simulator/compute/power/PStatePowerDriver.kt | 60 -- .../opendc/simulator/compute/power/PowerDriver.kt | 48 -- .../opendc/simulator/compute/power/PowerModel.kt | 38 - .../simulator/compute/power/SimplePowerDriver.kt | 48 -- .../simulator/compute/power/SqrtPowerModel.kt | 41 - .../simulator/compute/power/SquarePowerModel.kt | 41 - .../compute/power/ZeroIdlePowerDecorator.kt | 40 - .../simulator/compute/workload/SimFlopsWorkload.kt | 54 -- .../compute/workload/SimRuntimeWorkload.kt | 54 -- .../opendc/simulator/compute/workload/SimTrace.kt | 218 ----- .../simulator/compute/workload/SimTraceFragment.kt | 38 - .../simulator/compute/workload/SimTraceWorkload.kt | 46 -- .../simulator/compute/workload/SimWorkload.kt | 47 -- .../compute/workload/SimWorkloadLifecycle.kt | 82 -- .../org/opendc/simulator/compute/SimMachineTest.kt | 224 +++-- .../opendc/simulator/compute/device/SimPsuTest.kt | 102 --- .../compute/kernel/SimFairShareHypervisorTest.kt | 146 ++-- .../compute/kernel/SimSpaceSharedHypervisorTest.kt | 86 +- .../cpufreq/ConservativeScalingGovernorTest.kt | 14 +- .../kernel/cpufreq/OnDemandScalingGovernorTest.kt | 13 +- .../cpufreq/PerformanceScalingGovernorTest.kt | 2 +- .../kernel/cpufreq/PowerSaveScalingGovernorTest.kt | 4 +- .../compute/power/PStatePowerDriverTest.kt | 123 --- .../simulator/compute/power/PowerModelTest.kt | 27 +- .../compute/workload/SimFlopsWorkloadTest.kt | 2 +- .../compute/workload/SimTraceWorkloadTest.kt | 72 +- .../src/test/resources/spec_machines.yml | 29 - 102 files changed, 5369 insertions(+), 4164 deletions(-) create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimAbstractMachine.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimBareMetalMachine.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachine.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachineContext.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMemory.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimNetworkInterface.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimProcessingUnit.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsu.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactories.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactory.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimStorageInterface.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimNetworkAdapter.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimPeripheral.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisor.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisorCounters.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimVirtualMachine.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernor.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernorFactory.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernors.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingPolicy.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceMember.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceProfile.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/MachineModel.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/MemoryUnit.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/NetworkAdapter.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/ProcessingNode.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/ProcessingUnit.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/StorageDevice.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CpuPowerModel.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CpuPowerModels.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimFlopsWorkload.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimRuntimeWorkload.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTrace.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTraceFragment.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkload.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkloadLifecycle.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractMachine.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachine.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachineContext.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMemory.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimNetworkInterface.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimProcessingUnit.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimStorageInterface.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimNetworkAdapter.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimPeripheral.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimPsu.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisor.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisorCounters.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimVirtualMachine.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernor.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernor.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernor.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernor.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernor.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ScalingPolicy.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceMember.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceProfile.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/MachineModel.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/MemoryUnit.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/NetworkAdapter.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingNode.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingUnit.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/StorageDevice.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/AsymptoticPowerModel.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ConstantPowerModel.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/CubicPowerModel.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/InterpolationPowerModel.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/LinearPowerModel.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/MsePowerModel.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PStatePowerDriver.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerDriver.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerModel.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SimplePowerDriver.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SqrtPowerModel.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SquarePowerModel.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ZeroIdlePowerDecorator.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkload.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimRuntimeWorkload.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTrace.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceFragment.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkload.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkloadLifecycle.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/device/SimPsuTest.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PStatePowerDriverTest.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/test/resources/spec_machines.yml (limited to 'opendc-simulator/opendc-simulator-compute/src') 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 220b97cc..ec032070 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 @@ -29,12 +29,9 @@ import org.opendc.simulator.compute.model.MachineModel import org.opendc.simulator.compute.model.MemoryUnit import org.opendc.simulator.compute.model.ProcessingNode import org.opendc.simulator.compute.model.ProcessingUnit -import org.opendc.simulator.compute.power.ConstantPowerModel -import org.opendc.simulator.compute.power.SimplePowerDriver import org.opendc.simulator.compute.workload.SimTrace -import org.opendc.simulator.compute.workload.SimTraceWorkload -import org.opendc.simulator.flow.FlowEngine -import org.opendc.simulator.flow.mux.FlowMultiplexerFactory +import org.opendc.simulator.flow2.FlowEngine +import org.opendc.simulator.flow2.mux.FlowMultiplexerFactory import org.opendc.simulator.kotlin.runSimulation import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Fork @@ -60,14 +57,14 @@ class SimMachineBenchmarks { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) machineModel = MachineModel( - cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, - memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, + /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } ) val random = ThreadLocalRandom.current() val builder = SimTrace.builder() - repeat(10000) { - val timestamp = it.toLong() + repeat(1000000) { + val timestamp = it.toLong() * 1000 val deadline = timestamp + 1000 builder.add(deadline, random.nextDouble(0.0, 4500.0), 1) } @@ -77,29 +74,27 @@ class SimMachineBenchmarks { @Benchmark fun benchmarkBareMetal() { return runSimulation { - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine( - engine, - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) - ) - return@runSimulation machine.runWorkload(SimTraceWorkload(trace)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + val machine = SimBareMetalMachine.create(graph, machineModel) + return@runSimulation machine.runWorkload(trace.createWorkload(0)) } } @Benchmark fun benchmarkSpaceSharedHypervisor() { return runSimulation { - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(1), null) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(1)) launch { machine.runWorkload(hypervisor) } val vm = hypervisor.newMachine(machineModel) try { - return@runSimulation vm.runWorkload(SimTraceWorkload(trace)) + return@runSimulation vm.runWorkload(trace.createWorkload(0)) } finally { vm.cancel() machine.cancel() @@ -110,16 +105,17 @@ class SimMachineBenchmarks { @Benchmark fun benchmarkFairShareHypervisorSingle() { return runSimulation { - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1), null) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1)) launch { machine.runWorkload(hypervisor) } val vm = hypervisor.newMachine(machineModel) try { - return@runSimulation vm.runWorkload(SimTraceWorkload(trace)) + return@runSimulation vm.runWorkload(trace.createWorkload(0)) } finally { vm.cancel() machine.cancel() @@ -130,9 +126,10 @@ class SimMachineBenchmarks { @Benchmark fun benchmarkFairShareHypervisorDouble() { return runSimulation { - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1), null) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1)) launch { machine.runWorkload(hypervisor) } @@ -142,7 +139,7 @@ class SimMachineBenchmarks { launch { try { - vm.runWorkload(SimTraceWorkload(trace)) + vm.runWorkload(trace.createWorkload(0)) } finally { machine.cancel() } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimAbstractMachine.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimAbstractMachine.java new file mode 100644 index 00000000..cf5aed03 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimAbstractMachine.java @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2022 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; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.opendc.simulator.compute.device.SimNetworkAdapter; +import org.opendc.simulator.compute.model.MachineModel; +import org.opendc.simulator.compute.model.MemoryUnit; +import org.opendc.simulator.compute.workload.SimWorkload; +import org.opendc.simulator.flow2.FlowGraph; +import org.opendc.simulator.flow2.Inlet; +import org.opendc.simulator.flow2.Outlet; +import org.opendc.simulator.flow2.sink.SimpleFlowSink; +import org.opendc.simulator.flow2.util.FlowTransformer; +import org.opendc.simulator.flow2.util.FlowTransforms; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Abstract implementation of the {@link SimMachine} interface. + */ +public abstract class SimAbstractMachine implements SimMachine { + private static final Logger LOGGER = LoggerFactory.getLogger(SimAbstractMachine.class); + private final MachineModel model; + + private Context activeContext; + + /** + * Construct a {@link SimAbstractMachine} instance. + * + * @param model The model of the machine. + */ + public SimAbstractMachine(MachineModel model) { + this.model = model; + } + + @Override + public final MachineModel getModel() { + return model; + } + + @Override + public final SimMachineContext startWorkload(SimWorkload workload, Map meta) { + if (activeContext != null) { + throw new IllegalStateException("A machine cannot run multiple workloads concurrently"); + } + + final Context ctx = createContext(workload, new HashMap<>(meta)); + ctx.start(); + return ctx; + } + + @Override + public final void cancel() { + final Context context = activeContext; + if (context != null) { + context.shutdown(); + } + } + + /** + * Construct a new {@link Context} instance representing the active execution. + * + * @param workload The workload to start on the machine. + * @param meta The metadata to pass to the workload. + */ + protected abstract Context createContext(SimWorkload workload, Map meta); + + /** + * Return the active {@link Context} instance (if any). + */ + protected Context getActiveContext() { + return activeContext; + } + + /** + * The execution context in which the workload runs. + */ + public abstract static class Context implements SimMachineContext { + private final SimAbstractMachine machine; + private final SimWorkload workload; + private final Map meta; + private boolean isClosed; + + /** + * Construct a new {@link Context} instance. + * + * @param machine The {@link SimAbstractMachine} to which the context belongs. + * @param workload The {@link SimWorkload} to which the context belongs. + * @param meta The metadata passed to the context. + */ + public Context(SimAbstractMachine machine, SimWorkload workload, Map meta) { + this.machine = machine; + this.workload = workload; + this.meta = meta; + } + + @Override + public final Map getMeta() { + return meta; + } + + @Override + public final void shutdown() { + if (isClosed) { + return; + } + + isClosed = true; + final SimAbstractMachine machine = this.machine; + assert machine.activeContext == this : "Invariant violation: multiple contexts active for a single machine"; + machine.activeContext = null; + + // Cancel all the resources associated with the machine + doCancel(); + + try { + workload.onStop(this); + } catch (Exception cause) { + LOGGER.warn("Workload failed during onStop callback", cause); + } + } + + /** + * Start this context. + */ + final void start() { + try { + machine.activeContext = this; + workload.onStart(this); + } catch (Exception cause) { + LOGGER.warn("Workload failed during onStart callback", cause); + shutdown(); + } + } + + /** + * Run the stop procedures for the resources associated with the machine. + */ + protected void doCancel() { + final FlowGraph graph = getMemory().getInput().getGraph(); + + for (SimProcessingUnit cpu : getCpus()) { + final Inlet inlet = cpu.getInput(); + graph.disconnect(inlet); + } + + graph.disconnect(getMemory().getInput()); + + for (SimNetworkInterface ifx : getNetworkInterfaces()) { + ((NetworkAdapter) ifx).disconnect(); + } + + for (SimStorageInterface storage : getStorageInterfaces()) { + StorageDevice impl = (StorageDevice) storage; + graph.disconnect(impl.getRead()); + graph.disconnect(impl.getWrite()); + } + } + + @Override + public String toString() { + return "SimAbstractMachine.Context"; + } + } + + /** + * The [SimMemory] implementation for a machine. + */ + public static final class Memory implements SimMemory { + private final SimpleFlowSink sink; + private final List models; + + public Memory(FlowGraph graph, List models) { + long memorySize = 0; + for (MemoryUnit mem : models) { + memorySize += mem.getSize(); + } + + this.sink = new SimpleFlowSink(graph, (float) memorySize); + this.models = models; + } + + @Override + public double getCapacity() { + return sink.getCapacity(); + } + + @Override + public List getModels() { + return models; + } + + @Override + public Inlet getInput() { + return sink.getInput(); + } + + @Override + public String toString() { + return "SimAbstractMachine.Memory"; + } + } + + /** + * A {@link SimNetworkAdapter} implementation for a machine. + */ + public static class NetworkAdapter extends SimNetworkAdapter implements SimNetworkInterface { + private final org.opendc.simulator.compute.model.NetworkAdapter model; + private final FlowTransformer tx; + private final FlowTransformer rx; + private final String name; + + /** + * Construct a {@link NetworkAdapter}. + */ + public NetworkAdapter(FlowGraph graph, org.opendc.simulator.compute.model.NetworkAdapter model, int index) { + this.model = model; + this.tx = new FlowTransformer(graph, FlowTransforms.noop()); + this.rx = new FlowTransformer(graph, FlowTransforms.noop()); + this.name = "eth" + index; + } + + @Override + public String getName() { + return name; + } + + @Override + public Inlet getTx() { + return tx.getInput(); + } + + @Override + public Outlet getRx() { + return rx.getOutput(); + } + + @Override + public double getBandwidth() { + return model.getBandwidth(); + } + + @Override + protected Outlet getOutlet() { + return tx.getOutput(); + } + + @Override + protected Inlet getInlet() { + return rx.getInput(); + } + + @Override + public String toString() { + return "SimAbstractMachine.NetworkAdapterImpl[name=" + name + ", bandwidth=" + model.getBandwidth() + + "Mbps]"; + } + } + + /** + * A {@link SimStorageInterface} implementation for a machine. + */ + public static class StorageDevice implements SimStorageInterface { + private final org.opendc.simulator.compute.model.StorageDevice model; + private final SimpleFlowSink read; + private final SimpleFlowSink write; + private final String name; + + /** + * Construct a {@link StorageDevice}. + */ + public StorageDevice(FlowGraph graph, org.opendc.simulator.compute.model.StorageDevice model, int index) { + this.model = model; + this.read = new SimpleFlowSink(graph, (float) model.getReadBandwidth()); + this.write = new SimpleFlowSink(graph, (float) model.getWriteBandwidth()); + this.name = "disk" + index; + } + + @Override + public String getName() { + return name; + } + + @Override + public Inlet getRead() { + return read.getInput(); + } + + @Override + public Inlet getWrite() { + return write.getInput(); + } + + @Override + public double getCapacity() { + return model.getCapacity(); + } + + @Override + public String toString() { + return "SimAbstractMachine.StorageDeviceImpl[name=" + name + ", capacity=" + model.getCapacity() + "MB]"; + } + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimBareMetalMachine.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimBareMetalMachine.java new file mode 100644 index 00000000..aa7502d6 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimBareMetalMachine.java @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2022 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; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.opendc.simulator.compute.device.SimPeripheral; +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.flow2.FlowGraph; +import org.opendc.simulator.flow2.InPort; +import org.opendc.simulator.flow2.Inlet; + +/** + * A simulated bare-metal machine that is able to run a single workload. + * + *

+ * A {@link SimBareMetalMachine} is a stateful object, and you should be careful when operating this object concurrently. For + * example, the class expects only a single concurrent call to {@link #startWorkload(SimWorkload, Map)}. + */ +public final class SimBareMetalMachine extends SimAbstractMachine { + /** + * The {@link FlowGraph} in which the simulation takes places. + */ + private final FlowGraph graph; + + /** + * The {@link SimPsu} of this bare metal machine. + */ + private final SimPsu psu; + + /** + * The resources of this machine. + */ + private final List cpus; + + private final Memory memory; + private final List net; + private final List disk; + + /** + * Construct a {@link SimBareMetalMachine} instance. + * + * @param graph The {@link FlowGraph} to which the machine belongs. + * @param model The machine model to simulate. + * @param psuFactory The {@link SimPsuFactory} to construct the power supply of the machine. + */ + private SimBareMetalMachine(FlowGraph graph, MachineModel model, SimPsuFactory psuFactory) { + super(model); + + this.graph = graph; + this.psu = psuFactory.newPsu(this, graph); + + int cpuIndex = 0; + final ArrayList cpus = new ArrayList<>(); + this.cpus = cpus; + for (ProcessingUnit cpu : model.getCpus()) { + cpus.add(new Cpu(psu, cpu, cpuIndex++)); + } + + this.memory = new Memory(graph, model.getMemory()); + + int netIndex = 0; + final ArrayList net = new ArrayList<>(); + this.net = net; + for (org.opendc.simulator.compute.model.NetworkAdapter adapter : model.getNetwork()) { + net.add(new NetworkAdapter(graph, adapter, netIndex++)); + } + + int diskIndex = 0; + final ArrayList disk = new ArrayList<>(); + this.disk = disk; + for (org.opendc.simulator.compute.model.StorageDevice device : model.getStorage()) { + disk.add(new StorageDevice(graph, device, diskIndex++)); + } + } + + /** + * Create a {@link SimBareMetalMachine} instance. + * + * @param graph The {@link FlowGraph} to which the machine belongs. + * @param model The machine model to simulate. + * @param psuFactory The {@link SimPsuFactory} to construct the power supply of the machine. + */ + public static SimBareMetalMachine create(FlowGraph graph, MachineModel model, SimPsuFactory psuFactory) { + return new SimBareMetalMachine(graph, model, psuFactory); + } + + /** + * Create a {@link SimBareMetalMachine} instance with a no-op PSU. + * + * @param graph The {@link FlowGraph} to which the machine belongs. + * @param model The machine model to simulate. + */ + public static SimBareMetalMachine create(FlowGraph graph, MachineModel model) { + return new SimBareMetalMachine(graph, model, SimPsuFactories.noop()); + } + + /** + * Return the {@link SimPsu} belonging to this bare metal machine. + */ + public SimPsu getPsu() { + return psu; + } + + /** + * Return the list of peripherals attached to this bare metal machine. + */ + @Override + public List getPeripherals() { + return Collections.unmodifiableList(net); + } + + /** + * Return the CPU capacity of the machine in MHz. + */ + public double getCpuCapacity() { + final Context context = (Context) getActiveContext(); + + if (context == null) { + return 0.0; + } + + float capacity = 0.f; + + for (SimProcessingUnit cpu : context.cpus) { + capacity += cpu.getFrequency(); + } + + return capacity; + } + + /** + * The CPU demand of the machine in MHz. + */ + public double getCpuDemand() { + final Context context = (Context) getActiveContext(); + + if (context == null) { + return 0.0; + } + + float demand = 0.f; + + for (SimProcessingUnit cpu : context.cpus) { + demand += cpu.getDemand(); + } + + return demand; + } + + /** + * The CPU usage of the machine in MHz. + */ + public double getCpuUsage() { + final Context context = (Context) getActiveContext(); + + if (context == null) { + return 0.0; + } + + float rate = 0.f; + + for (SimProcessingUnit cpu : context.cpus) { + rate += cpu.getSpeed(); + } + + return rate; + } + + @Override + protected SimAbstractMachine.Context createContext(SimWorkload workload, Map meta) { + return new Context(this, workload, meta); + } + + /** + * The execution context for a {@link SimBareMetalMachine}. + */ + private static final class Context extends SimAbstractMachine.Context { + private final FlowGraph graph; + private final List cpus; + private final Memory memory; + private final List net; + private final List disk; + + private Context(SimBareMetalMachine machine, SimWorkload workload, Map meta) { + super(machine, workload, meta); + + this.graph = machine.graph; + this.cpus = machine.cpus; + this.memory = machine.memory; + this.net = machine.net; + this.disk = machine.disk; + } + + @Override + public FlowGraph getGraph() { + return graph; + } + + @Override + public List getCpus() { + return cpus; + } + + @Override + public SimMemory getMemory() { + return memory; + } + + @Override + public List getNetworkInterfaces() { + return net; + } + + @Override + public List getStorageInterfaces() { + return disk; + } + } + + /** + * A {@link SimProcessingUnit} of a bare-metal machine. + */ + private static final class Cpu implements SimProcessingUnit { + private final SimPsu psu; + private final ProcessingUnit model; + private final InPort port; + + private Cpu(SimPsu psu, ProcessingUnit model, int id) { + this.psu = psu; + this.model = model; + this.port = psu.getCpuPower(id, model); + + this.port.pull((float) model.getFrequency()); + } + + @Override + public double getFrequency() { + return port.getCapacity(); + } + + @Override + public void setFrequency(double frequency) { + // Clamp the capacity of the CPU between [0.0, maxFreq] + frequency = Math.max(0, Math.min(model.getFrequency(), frequency)); + psu.setCpuFrequency(port, frequency); + } + + @Override + public double getDemand() { + return port.getDemand(); + } + + @Override + public double getSpeed() { + return port.getRate(); + } + + @Override + public ProcessingUnit getModel() { + return model; + } + + @Override + public Inlet getInput() { + return port; + } + + @Override + public String toString() { + return "SimBareMetalMachine.Cpu[model=" + model + "]"; + } + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachine.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachine.java new file mode 100644 index 00000000..59599875 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachine.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022 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; + +import java.util.List; +import java.util.Map; +import org.opendc.simulator.compute.device.SimPeripheral; +import org.opendc.simulator.compute.model.MachineModel; +import org.opendc.simulator.compute.workload.SimWorkload; + +/** + * A generic machine that is able to execute {@link SimWorkload} objects. + */ +public interface SimMachine { + /** + * Return the model of the machine containing its specifications. + */ + MachineModel getModel(); + + /** + * Return the peripherals attached to the machine. + */ + List getPeripherals(); + + /** + * Start the specified {@link SimWorkload} on this machine. + * + * @param workload The workload to start on the machine. + * @param meta The metadata to pass to the workload. + * @return A {@link SimMachineContext} that represents the execution context for the workload. + * @throws IllegalStateException if a workload is already active on the machine or if the machine is closed. + */ + SimMachineContext startWorkload(SimWorkload workload, Map meta); + + /** + * Cancel the active workload on this machine (if any). + */ + void cancel(); +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachineContext.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachineContext.java new file mode 100644 index 00000000..f6a3bd38 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachineContext.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2022 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; + +import java.util.List; +import java.util.Map; +import org.opendc.simulator.compute.workload.SimWorkload; +import org.opendc.simulator.flow2.FlowGraph; + +/** + * A simulated execution context in which a bootable image runs. + * + *

+ * This interface represents the interface between the running image (e.g. operating system) and the physical + * or virtual firmware on which the image runs. + */ +public interface SimMachineContext { + /** + * Return the {@link FlowGraph} in which the workload executes. + */ + FlowGraph getGraph(); + + /** + * Return the metadata associated with the context. + *

+ * Users can pass this metadata to the workload via {@link SimMachine#startWorkload(SimWorkload, Map)}. + */ + Map getMeta(); + + /** + * Return the CPUs available on the machine. + */ + List getCpus(); + + /** + * Return the memory interface of the machine. + */ + SimMemory getMemory(); + + /** + * Return the network interfaces available to the workload. + */ + List getNetworkInterfaces(); + + /** + * Return the storage devices available to the workload. + */ + List getStorageInterfaces(); + + /** + * Shutdown the workload. + */ + void shutdown(); +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMemory.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMemory.java new file mode 100644 index 00000000..4fcc64ab --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMemory.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 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; + +import java.util.List; +import org.opendc.simulator.compute.model.MemoryUnit; +import org.opendc.simulator.flow2.sink.FlowSink; + +/** + * An interface to control the memory usage of simulated workloads. + */ +public interface SimMemory extends FlowSink { + /** + * Return the total capacity of the memory (in MBs). + */ + double getCapacity(); + + /** + * Return the models representing the static information of the memory units supporting this interface. + */ + List getModels(); +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimNetworkInterface.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimNetworkInterface.java new file mode 100644 index 00000000..4b623e59 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimNetworkInterface.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 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; + +import org.opendc.simulator.flow2.Inlet; +import org.opendc.simulator.flow2.Outlet; + +/** + * A firmware interface to a network adapter. + */ +public interface SimNetworkInterface { + /** + * Return the name of the network interface. + */ + String getName(); + + /** + * Return the unidirectional bandwidth of the network interface in Mbps. + */ + double getBandwidth(); + + /** + * Return the inlet for the "transmit" channel of the network interface. + */ + Inlet getTx(); + + /** + * Return the outlet for the "receive" channel of the network interface. + */ + Outlet getRx(); +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimProcessingUnit.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimProcessingUnit.java new file mode 100644 index 00000000..3dbd3656 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimProcessingUnit.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022 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; + +import org.opendc.simulator.compute.model.ProcessingUnit; +import org.opendc.simulator.flow2.sink.FlowSink; + +/** + * A simulated processing unit. + */ +public interface SimProcessingUnit extends FlowSink { + /** + * Return the base clock frequency of the processing unit (in MHz). + */ + double getFrequency(); + + /** + * Adjust the base clock frequency of the processing unit. + * + *

+ * The CPU may or may not round the new frequency to one of its pre-defined frequency steps. + * + * @param frequency The new frequency to set the clock of the processing unit to. + * @throws UnsupportedOperationException if the base clock cannot be adjusted. + */ + void setFrequency(double frequency); + + /** + * The demand on the processing unit. + */ + double getDemand(); + + /** + * The speed of the processing unit. + */ + double getSpeed(); + + /** + * The model representing the static properties of the processing unit. + */ + ProcessingUnit getModel(); +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsu.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsu.java new file mode 100644 index 00000000..7f1f97a0 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsu.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022 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; + +import org.opendc.simulator.compute.model.ProcessingUnit; +import org.opendc.simulator.flow2.InPort; +import org.opendc.simulator.power.SimPowerInlet; + +/** + * A power supply unit in a {@link SimBareMetalMachine}. + * + *

+ * This class manages the computation of power usage for a {@link SimBareMetalMachine} based on the resource usage. + */ +public abstract class SimPsu extends SimPowerInlet { + /** + * Return the power demand of the machine (in W) measured in the PSU. + *

+ * This method provides access to the power consumption of the machine before PSU losses are applied. + */ + public abstract double getPowerDemand(); + + /** + * Return the instantaneous power usage of the machine (in W) measured at the inlet of the power supply. + */ + public abstract double getPowerUsage(); + + /** + * Return the cumulated energy usage of the machine (in J) measured at the inlet of the powers supply. + */ + public abstract double getEnergyUsage(); + + /** + * Return an {@link InPort} that converts processing demand (in MHz) into energy demand (J) for the specified CPU + * model. + * + * @param id The unique identifier of the CPU for this machine. + * @param model The details of the processing unit. + */ + abstract InPort getCpuPower(int id, ProcessingUnit model); + + /** + * This method is invoked when the CPU frequency is changed for the specified port. + * + * @param port The {@link InPort} for which the capacity is changed. + * @param capacity The capacity to change to. + */ + void setCpuFrequency(InPort port, double capacity) { + port.pull((float) capacity); + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactories.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactories.java new file mode 100644 index 00000000..52d04052 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactories.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2022 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; + +import java.time.Clock; +import org.jetbrains.annotations.NotNull; +import org.opendc.simulator.compute.model.ProcessingUnit; +import org.opendc.simulator.compute.power.CpuPowerModel; +import org.opendc.simulator.flow2.FlowGraph; +import org.opendc.simulator.flow2.FlowStage; +import org.opendc.simulator.flow2.FlowStageLogic; +import org.opendc.simulator.flow2.InHandler; +import org.opendc.simulator.flow2.InPort; +import org.opendc.simulator.flow2.OutPort; +import org.opendc.simulator.flow2.Outlet; + +/** + * A collection {@link SimPsu} implementations. + */ +public class SimPsuFactories { + private SimPsuFactories() {} + + /** + * Return a {@link SimPsuFactory} of {@link SimPsu} implementations that do not measure any power consumption. + * + *

+ * This implementation has the lowest performance impact and users are advised to use this factory if they do not + * consider power consumption in their experiments. + */ + public static SimPsuFactory noop() { + return NoopPsu.FACTORY; + } + + /** + * Return a {@link SimPsuFactory} of {@link SimPsu} implementations that use a {@link CpuPowerModel} to estimate the + * power consumption of a machine based on its CPU utilization. + * + * @param model The power model to estimate the power consumption based on the CPU usage. + */ + public static SimPsuFactory simple(CpuPowerModel model) { + return (machine, graph) -> new SimplePsu(graph, model); + } + + /** + * A {@link SimPsu} implementation that does not attempt to measure power consumption. + */ + private static final class NoopPsu extends SimPsu implements FlowStageLogic { + private static final SimPsuFactory FACTORY = (machine, graph) -> new NoopPsu(graph); + + private final FlowStage stage; + private final OutPort out; + + NoopPsu(FlowGraph graph) { + stage = graph.newStage(this); + out = stage.getOutlet("out"); + out.setMask(true); + } + + @Override + public double getPowerDemand() { + return 0; + } + + @Override + public double getPowerUsage() { + return 0; + } + + @Override + public double getEnergyUsage() { + return 0; + } + + @Override + InPort getCpuPower(int id, ProcessingUnit model) { + final InPort port = stage.getInlet("cpu" + id); + port.setMask(true); + return port; + } + + @Override + public long onUpdate(FlowStage ctx, long now) { + return Long.MAX_VALUE; + } + + @NotNull + @Override + public Outlet getFlowOutlet() { + return out; + } + } + + /** + * A {@link SimPsu} implementation that estimates the power consumption based on CPU usage. + */ + private static final class SimplePsu extends SimPsu implements FlowStageLogic { + private final FlowStage stage; + private final OutPort out; + private final CpuPowerModel model; + private final Clock clock; + + private double targetFreq; + private double totalUsage; + private long lastUpdate; + + private double powerUsage; + private double energyUsage; + + private final InHandler handler = new InHandler() { + @Override + public void onPush(InPort port, float demand) { + totalUsage += -port.getDemand() + demand; + } + + @Override + public void onUpstreamFinish(InPort port, Throwable cause) { + totalUsage -= port.getDemand(); + } + }; + + SimplePsu(FlowGraph graph, CpuPowerModel model) { + this.stage = graph.newStage(this); + this.model = model; + this.clock = graph.getEngine().getClock(); + this.out = stage.getOutlet("out"); + this.out.setMask(true); + + lastUpdate = graph.getEngine().getClock().millis(); + } + + @Override + public double getPowerDemand() { + return totalUsage; + } + + @Override + public double getPowerUsage() { + return powerUsage; + } + + @Override + public double getEnergyUsage() { + updateEnergyUsage(clock.millis()); + return energyUsage; + } + + @Override + InPort getCpuPower(int id, ProcessingUnit model) { + targetFreq += model.getFrequency(); + + final InPort port = stage.getInlet("cpu" + id); + port.setHandler(handler); + return port; + } + + @Override + void setCpuFrequency(InPort port, double capacity) { + targetFreq += -port.getCapacity() + capacity; + + super.setCpuFrequency(port, capacity); + } + + @Override + public long onUpdate(FlowStage ctx, long now) { + updateEnergyUsage(now); + + double usage = model.computePower(totalUsage / targetFreq); + out.push((float) usage); + powerUsage = usage; + + return Long.MAX_VALUE; + } + + @NotNull + @Override + public Outlet getFlowOutlet() { + return out; + } + + /** + * Calculate the energy usage up until now. + */ + private void updateEnergyUsage(long now) { + long lastUpdate = this.lastUpdate; + this.lastUpdate = now; + + long duration = now - lastUpdate; + if (duration > 0) { + // Compute the energy usage of the machine + energyUsage += powerUsage * duration * 0.001; + } + } + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactory.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactory.java new file mode 100644 index 00000000..872e7016 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactory.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 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; + +import org.opendc.simulator.flow2.FlowGraph; + +/** + * A factory interface for {@link SimPsu} implementations. + */ +public interface SimPsuFactory { + /** + * Construct a new {@link SimPsu} for the specified machine. + * + * @param machine The machine to construct the power supply for. + * @param graph The {@link FlowGraph} used for the simulation. + */ + SimPsu newPsu(SimMachine machine, FlowGraph graph); +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimStorageInterface.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimStorageInterface.java new file mode 100644 index 00000000..341122dc --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimStorageInterface.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022 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; + +import org.opendc.simulator.flow2.Inlet; + +/** + * A firmware interface to a storage device. + */ +public interface SimStorageInterface { + /** + * Return the name of the network interface. + */ + String getName(); + + /** + * Return the capacity of the storage device in MBs. + */ + double getCapacity(); + + /** + * Return the inlet for the read operations of the storage device. + */ + Inlet getRead(); + + /** + * Return the inlet for the write operation of the storage device. + */ + Inlet getWrite(); +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimNetworkAdapter.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimNetworkAdapter.java new file mode 100644 index 00000000..1c16ceff --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimNetworkAdapter.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 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.device; + +import org.opendc.simulator.compute.SimMachine; +import org.opendc.simulator.network.SimNetworkPort; + +/** + * A simulated network interface card (NIC or network adapter) that can be attached to a {@link SimMachine}. + */ +public abstract class SimNetworkAdapter extends SimNetworkPort implements SimPeripheral { + /** + * Return the unidirectional bandwidth of the network adapter (in Mbps). + */ + public abstract double getBandwidth(); +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimPeripheral.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimPeripheral.java new file mode 100644 index 00000000..40bd268b --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimPeripheral.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 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.device; + +import org.opendc.simulator.compute.SimMachine; + +/** + * A component that can be attached to a {@link SimMachine}. + *

+ * This interface represents the physical view of the peripheral and should be used to configure the physical properties + * of the peripheral. + */ +public interface SimPeripheral {} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisor.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisor.java new file mode 100644 index 00000000..6e295837 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisor.java @@ -0,0 +1,911 @@ +/* + * Copyright (c) 2022 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; + +import java.time.Clock; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.SplittableRandom; +import java.util.stream.Collectors; +import org.opendc.simulator.compute.SimAbstractMachine; +import org.opendc.simulator.compute.SimMachine; +import org.opendc.simulator.compute.SimMachineContext; +import org.opendc.simulator.compute.SimMemory; +import org.opendc.simulator.compute.SimNetworkInterface; +import org.opendc.simulator.compute.SimProcessingUnit; +import org.opendc.simulator.compute.SimStorageInterface; +import org.opendc.simulator.compute.device.SimPeripheral; +import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernor; +import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernorFactory; +import org.opendc.simulator.compute.kernel.cpufreq.ScalingPolicy; +import org.opendc.simulator.compute.kernel.interference.VmInterferenceDomain; +import org.opendc.simulator.compute.kernel.interference.VmInterferenceMember; +import org.opendc.simulator.compute.kernel.interference.VmInterferenceProfile; +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.flow2.FlowGraph; +import org.opendc.simulator.flow2.FlowStage; +import org.opendc.simulator.flow2.FlowStageLogic; +import org.opendc.simulator.flow2.InHandler; +import org.opendc.simulator.flow2.InPort; +import org.opendc.simulator.flow2.Inlet; +import org.opendc.simulator.flow2.OutHandler; +import org.opendc.simulator.flow2.OutPort; +import org.opendc.simulator.flow2.mux.FlowMultiplexer; +import org.opendc.simulator.flow2.mux.FlowMultiplexerFactory; + +/** + * A SimHypervisor facilitates the execution of multiple concurrent {@link SimWorkload}s, while acting as a single + * workload to another {@link SimMachine}. + */ +public final class SimHypervisor implements SimWorkload { + private final FlowMultiplexerFactory muxFactory; + private final SplittableRandom random; + private final ScalingGovernorFactory scalingGovernorFactory; + private final VmInterferenceDomain interferenceDomain; + + private Context activeContext; + private final ArrayList vms = new ArrayList<>(); + private final HvCounters counters = new HvCounters(); + + /** + * Construct a {@link SimHypervisor} instance. + * + * @param muxFactory The factory for the {@link FlowMultiplexer} to multiplex the workloads. + * @param random A randomness generator for the interference calculations. + * @param scalingGovernorFactory The factory for the scaling governor to use for scaling the CPU frequency. + * @param interferenceDomain The interference domain to which the hypervisor belongs. + */ + private SimHypervisor( + FlowMultiplexerFactory muxFactory, + SplittableRandom random, + ScalingGovernorFactory scalingGovernorFactory, + VmInterferenceDomain interferenceDomain) { + this.muxFactory = muxFactory; + this.random = random; + this.scalingGovernorFactory = scalingGovernorFactory; + this.interferenceDomain = interferenceDomain; + } + + /** + * Create a {@link SimHypervisor} instance. + * + * @param muxFactory The factory for the {@link FlowMultiplexer} to multiplex the workloads. + * @param random A randomness generator for the interference calculations. + * @param scalingGovernorFactory The factory for the scaling governor to use for scaling the CPU frequency. + * @param interferenceDomain The interference domain to which the hypervisor belongs. + */ + public static SimHypervisor create( + FlowMultiplexerFactory muxFactory, + SplittableRandom random, + ScalingGovernorFactory scalingGovernorFactory, + VmInterferenceDomain interferenceDomain) { + return new SimHypervisor(muxFactory, random, scalingGovernorFactory, interferenceDomain); + } + + /** + * Create a {@link SimHypervisor} instance with a default interference domain. + * + * @param muxFactory The factory for the {@link FlowMultiplexer} to multiplex the workloads. + * @param random A randomness generator for the interference calculations. + * @param scalingGovernorFactory The factory for the scaling governor to use for scaling the CPU frequency. + */ + public static SimHypervisor create( + FlowMultiplexerFactory muxFactory, SplittableRandom random, ScalingGovernorFactory scalingGovernorFactory) { + return create(muxFactory, random, scalingGovernorFactory, new VmInterferenceDomain()); + } + + /** + * Create a {@link SimHypervisor} instance with a default interference domain and scaling governor. + * + * @param muxFactory The factory for the {@link FlowMultiplexer} to multiplex the workloads. + * @param random A randomness generator for the interference calculations. + */ + public static SimHypervisor create(FlowMultiplexerFactory muxFactory, SplittableRandom random) { + return create(muxFactory, random, null); + } + + /** + * Return the performance counters of the hypervisor. + */ + public SimHypervisorCounters getCounters() { + return counters; + } + + /** + * Return the virtual machines running on this hypervisor. + */ + public List getVirtualMachines() { + return Collections.unmodifiableList(vms); + } + + /** + * Create a {@link SimVirtualMachine} instance on which users may run a [SimWorkload]. + * + * @param model The machine to create. + */ + public SimVirtualMachine newMachine(MachineModel model) { + if (!canFit(model)) { + throw new IllegalArgumentException("Machine does not fit"); + } + + VirtualMachine vm = new VirtualMachine(model); + vms.add(vm); + return vm; + } + + /** + * Remove the specified machine from the hypervisor. + * + * @param machine The machine to remove. + */ + public void removeMachine(SimVirtualMachine machine) { + if (vms.remove(machine)) { + // This cast must always succeed, since `_vms` only contains `VirtualMachine` types. + ((VirtualMachine) machine).close(); + } + } + + /** + * Return the CPU capacity of the hypervisor in MHz. + */ + public double getCpuCapacity() { + final Context context = activeContext; + + if (context == null) { + return 0.0; + } + + return context.previousCapacity; + } + + /** + * The CPU demand of the hypervisor in MHz. + */ + public double getCpuDemand() { + final Context context = activeContext; + + if (context == null) { + return 0.0; + } + + return context.previousDemand; + } + + /** + * The CPU usage of the hypervisor in MHz. + */ + public double getCpuUsage() { + final Context context = activeContext; + + if (context == null) { + return 0.0; + } + + return context.previousRate; + } + + /** + * Determine whether the specified machine characterized by model can fit on this hypervisor at this + * moment. + */ + public boolean canFit(MachineModel model) { + final Context context = activeContext; + if (context == null) { + return false; + } + + final FlowMultiplexer multiplexer = context.multiplexer; + return (multiplexer.getMaxInputs() - multiplexer.getInputCount()) + >= model.getCpus().size(); + } + + @Override + public void onStart(SimMachineContext ctx) { + final Context context = new Context(ctx, muxFactory, scalingGovernorFactory, counters); + context.start(); + activeContext = context; + } + + @Override + public void onStop(SimMachineContext ctx) { + final Context context = activeContext; + if (context != null) { + activeContext = null; + context.stop(); + } + } + + /** + * The context which carries the state when the hypervisor is running on a machine. + */ + private static final class Context implements FlowStageLogic { + private final SimMachineContext ctx; + private final FlowMultiplexer multiplexer; + private final FlowStage stage; + private final List scalingGovernors; + private final Clock clock; + private final HvCounters counters; + + private long lastCounterUpdate; + private final double d; + private float previousDemand; + private float previousRate; + private float previousCapacity; + + private Context( + SimMachineContext ctx, + FlowMultiplexerFactory muxFactory, + ScalingGovernorFactory scalingGovernorFactory, + HvCounters counters) { + + this.ctx = ctx; + this.counters = counters; + + final FlowGraph graph = ctx.getGraph(); + this.multiplexer = muxFactory.newMultiplexer(graph); + this.stage = graph.newStage(this); + this.clock = graph.getEngine().getClock(); + + this.lastCounterUpdate = clock.millis(); + + if (scalingGovernorFactory != null) { + this.scalingGovernors = ctx.getCpus().stream() + .map(cpu -> scalingGovernorFactory.newGovernor(new ScalingPolicyImpl(cpu))) + .collect(Collectors.toList()); + } else { + this.scalingGovernors = Collections.emptyList(); + } + + float cpuCapacity = 0.f; + final List cpus = ctx.getCpus(); + for (SimProcessingUnit cpu : cpus) { + cpuCapacity += cpu.getFrequency(); + } + this.d = cpus.size() / cpuCapacity; + } + + /** + * Start the hypervisor on a new machine. + */ + void start() { + final FlowGraph graph = ctx.getGraph(); + final FlowMultiplexer multiplexer = this.multiplexer; + + for (SimProcessingUnit cpu : ctx.getCpus()) { + graph.connect(multiplexer.newOutput(), cpu.getInput()); + } + + for (ScalingGovernor governor : scalingGovernors) { + governor.onStart(); + } + } + + /** + * Stop the hypervisor. + */ + void stop() { + // Synchronize the counters before stopping the hypervisor. Otherwise, the last report is missed. + updateCounters(clock.millis()); + + stage.close(); + } + + /** + * Invalidate the {@link FlowStage} of the hypervisor. + */ + void invalidate() { + stage.invalidate(); + } + + /** + * Update the performance counters of the hypervisor. + * + * @param now The timestamp at which to update the counter. + */ + void updateCounters(long now) { + long lastUpdate = this.lastCounterUpdate; + this.lastCounterUpdate = now; + long delta = now - lastUpdate; + + if (delta > 0) { + final HvCounters counters = this.counters; + + float demand = previousDemand; + float rate = previousRate; + float capacity = previousCapacity; + + final double factor = this.d * delta; + + counters.cpuActiveTime += Math.round(rate * factor); + counters.cpuIdleTime += Math.round((capacity - rate) * factor); + counters.cpuStealTime += Math.round((demand - rate) * factor); + } + } + + /** + * Update the performance counters of the hypervisor. + */ + void updateCounters() { + updateCounters(clock.millis()); + } + + @Override + public long onUpdate(FlowStage ctx, long now) { + updateCounters(now); + + final FlowMultiplexer multiplexer = this.multiplexer; + final List scalingGovernors = this.scalingGovernors; + + float demand = multiplexer.getDemand(); + float rate = multiplexer.getRate(); + float capacity = multiplexer.getCapacity(); + + this.previousDemand = demand; + this.previousRate = rate; + this.previousCapacity = capacity; + + double load = rate / Math.min(1.0, capacity); + + if (!scalingGovernors.isEmpty()) { + for (ScalingGovernor governor : scalingGovernors) { + governor.onLimit(load); + } + } + + return Long.MAX_VALUE; + } + } + + /** + * A {@link ScalingPolicy} for a physical CPU of the hypervisor. + */ + private static final class ScalingPolicyImpl implements ScalingPolicy { + private final SimProcessingUnit cpu; + + private ScalingPolicyImpl(SimProcessingUnit cpu) { + this.cpu = cpu; + } + + @Override + public SimProcessingUnit getCpu() { + return cpu; + } + + @Override + public double getTarget() { + return cpu.getFrequency(); + } + + @Override + public void setTarget(double target) { + cpu.setFrequency(target); + } + + @Override + public double getMin() { + return 0; + } + + @Override + public double getMax() { + return cpu.getModel().getFrequency(); + } + } + + /** + * A virtual machine running on the hypervisor. + */ + private class VirtualMachine extends SimAbstractMachine implements SimVirtualMachine { + private boolean isClosed; + private final VmCounters counters = new VmCounters(this); + + private VirtualMachine(MachineModel model) { + super(model); + } + + @Override + public SimHypervisorCounters getCounters() { + return counters; + } + + @Override + public double getCpuDemand() { + final VmContext context = (VmContext) getActiveContext(); + + if (context == null) { + return 0.0; + } + + return context.previousDemand; + } + + @Override + public double getCpuUsage() { + final VmContext context = (VmContext) getActiveContext(); + + if (context == null) { + return 0.0; + } + + return context.usage; + } + + @Override + public double getCpuCapacity() { + final VmContext context = (VmContext) getActiveContext(); + + if (context == null) { + return 0.0; + } + + return context.previousCapacity; + } + + @Override + public List getPeripherals() { + return Collections.emptyList(); + } + + @Override + protected Context createContext(SimWorkload workload, Map meta) { + if (isClosed) { + throw new IllegalStateException("Virtual machine does not exist anymore"); + } + + final SimHypervisor.Context context = activeContext; + if (context == null) { + throw new IllegalStateException("Hypervisor is inactive"); + } + + return new VmContext( + context, this, random, interferenceDomain, counters, SimHypervisor.this.counters, workload, meta); + } + + @Override + public Context getActiveContext() { + return super.getActiveContext(); + } + + void close() { + if (isClosed) { + return; + } + + isClosed = true; + cancel(); + } + } + + /** + * A {@link SimAbstractMachine.Context} for a virtual machine instance. + */ + private static final class VmContext extends SimAbstractMachine.Context implements FlowStageLogic { + private final Context context; + private final SplittableRandom random; + private final VmCounters vmCounters; + private final HvCounters hvCounters; + private final VmInterferenceMember interferenceMember; + private final FlowStage stage; + private final FlowMultiplexer multiplexer; + private final Clock clock; + + private final List cpus; + private final SimAbstractMachine.Memory memory; + private final List net; + private final List disk; + + private final Inlet[] muxInlets; + private long lastUpdate; + private long lastCounterUpdate; + private final double d; + + private float demand; + private float usage; + private float capacity; + + private float previousDemand; + private float previousCapacity; + + private VmContext( + Context context, + VirtualMachine machine, + SplittableRandom random, + VmInterferenceDomain interferenceDomain, + VmCounters vmCounters, + HvCounters hvCounters, + SimWorkload workload, + Map meta) { + super(machine, workload, meta); + + this.context = context; + this.random = random; + this.vmCounters = vmCounters; + this.hvCounters = hvCounters; + this.clock = context.clock; + + final VmInterferenceProfile interferenceProfile = (VmInterferenceProfile) meta.get("interference-profile"); + VmInterferenceMember interferenceMember = null; + if (interferenceDomain != null && interferenceProfile != null) { + interferenceMember = interferenceDomain.join(interferenceProfile); + interferenceMember.activate(); + } + this.interferenceMember = interferenceMember; + + final FlowGraph graph = context.ctx.getGraph(); + final FlowStage stage = graph.newStage(this); + this.stage = stage; + this.lastUpdate = clock.millis(); + this.lastCounterUpdate = clock.millis(); + + final FlowMultiplexer multiplexer = context.multiplexer; + this.multiplexer = multiplexer; + + final MachineModel model = machine.getModel(); + final List cpuModels = model.getCpus(); + final Inlet[] muxInlets = new Inlet[cpuModels.size()]; + final ArrayList cpus = new ArrayList<>(); + + this.muxInlets = muxInlets; + this.cpus = cpus; + + float capacity = 0.f; + + for (int i = 0; i < cpuModels.size(); i++) { + final Inlet muxInlet = multiplexer.newInput(); + muxInlets[i] = muxInlet; + + final InPort input = stage.getInlet("cpu" + i); + final OutPort output = stage.getOutlet("mux" + i); + + final Handler handler = new Handler(this, input, output); + input.setHandler(handler); + output.setHandler(handler); + + final ProcessingUnit cpuModel = cpuModels.get(i); + capacity += cpuModel.getFrequency(); + + final VCpu cpu = new VCpu(cpuModel, input); + cpus.add(cpu); + + graph.connect(output, muxInlet); + } + this.d = cpuModels.size() / capacity; + + this.memory = new SimAbstractMachine.Memory(graph, model.getMemory()); + + int netIndex = 0; + final ArrayList net = new ArrayList<>(); + this.net = net; + for (org.opendc.simulator.compute.model.NetworkAdapter adapter : model.getNetwork()) { + net.add(new SimAbstractMachine.NetworkAdapter(graph, adapter, netIndex++)); + } + + int diskIndex = 0; + final ArrayList disk = new ArrayList<>(); + this.disk = disk; + for (org.opendc.simulator.compute.model.StorageDevice device : model.getStorage()) { + disk.add(new SimAbstractMachine.StorageDevice(graph, device, diskIndex++)); + } + } + + /** + * Update the performance counters of the virtual machine. + * + * @param now The timestamp at which to update the counter. + */ + void updateCounters(long now) { + long lastUpdate = this.lastCounterUpdate; + this.lastCounterUpdate = now; + long delta = now - lastUpdate; + + if (delta > 0) { + final VmCounters counters = this.vmCounters; + + float demand = this.previousDemand; + float rate = this.usage; + float capacity = this.previousCapacity; + + final double factor = this.d * delta; + final double active = rate * factor; + + counters.cpuActiveTime += Math.round(active); + counters.cpuIdleTime += Math.round((capacity - rate) * factor); + counters.cpuStealTime += Math.round((demand - rate) * factor); + } + } + + /** + * Update the performance counters of the virtual machine. + */ + void updateCounters() { + updateCounters(clock.millis()); + } + + @Override + public FlowGraph getGraph() { + return stage.getGraph(); + } + + @Override + public List getCpus() { + return cpus; + } + + @Override + public SimMemory getMemory() { + return memory; + } + + @Override + public List getNetworkInterfaces() { + return net; + } + + @Override + public List getStorageInterfaces() { + return disk; + } + + @Override + public long onUpdate(FlowStage ctx, long now) { + float usage = 0.f; + for (Inlet inlet : muxInlets) { + usage += ((InPort) inlet).getRate(); + } + this.usage = usage; + this.previousDemand = demand; + this.previousCapacity = capacity; + + long lastUpdate = this.lastUpdate; + this.lastUpdate = now; + long delta = now - lastUpdate; + + if (delta > 0) { + final VmInterferenceMember interferenceMember = this.interferenceMember; + double penalty = 0.0; + + if (interferenceMember != null) { + final FlowMultiplexer multiplexer = this.multiplexer; + double load = multiplexer.getRate() / Math.min(1.0, multiplexer.getCapacity()); + penalty = 1 - interferenceMember.apply(random, load); + } + + final double factor = this.d * delta; + final long lostTime = Math.round(factor * usage * penalty); + + this.vmCounters.cpuLostTime += lostTime; + this.hvCounters.cpuLostTime += lostTime; + } + + // Invalidate the FlowStage of the hypervisor to update its counters (via onUpdate) + context.invalidate(); + + return Long.MAX_VALUE; + } + + @Override + protected void doCancel() { + super.doCancel(); + + // Synchronize the counters before stopping the hypervisor. Otherwise, the last report is missed. + updateCounters(clock.millis()); + + stage.close(); + + final FlowMultiplexer multiplexer = this.multiplexer; + for (Inlet muxInlet : muxInlets) { + multiplexer.releaseInput(muxInlet); + } + + final VmInterferenceMember interferenceMember = this.interferenceMember; + if (interferenceMember != null) { + interferenceMember.deactivate(); + } + } + } + + /** + * A {@link SimProcessingUnit} of a virtual machine. + */ + private static final class VCpu implements SimProcessingUnit { + private final ProcessingUnit model; + private final InPort input; + + private VCpu(ProcessingUnit model, InPort input) { + this.model = model; + this.input = input; + + input.pull((float) model.getFrequency()); + } + + @Override + public double getFrequency() { + return input.getCapacity(); + } + + @Override + public void setFrequency(double frequency) { + input.pull((float) frequency); + } + + @Override + public double getDemand() { + return input.getDemand(); + } + + @Override + public double getSpeed() { + return input.getRate(); + } + + @Override + public ProcessingUnit getModel() { + return model; + } + + @Override + public Inlet getInput() { + return input; + } + + @Override + public String toString() { + return "SimHypervisor.VCpu[model" + model + "]"; + } + } + + /** + * A handler for forwarding flow between an inlet and outlet. + */ + private static class Handler implements InHandler, OutHandler { + private final InPort input; + private final OutPort output; + private final VmContext context; + + private Handler(VmContext context, InPort input, OutPort output) { + this.context = context; + this.input = input; + this.output = output; + } + + @Override + public void onPush(InPort port, float demand) { + context.demand += -port.getDemand() + demand; + + output.push(demand); + } + + @Override + public void onUpstreamFinish(InPort port, Throwable cause) { + context.demand -= port.getDemand(); + + output.push(0.f); + } + + @Override + public float getRate(InPort port) { + return output.getRate(); + } + + @Override + public void onPull(OutPort port, float capacity) { + context.capacity += -port.getCapacity() + capacity; + + input.pull(capacity); + } + + @Override + public void onDownstreamFinish(OutPort port, Throwable cause) { + context.capacity -= port.getCapacity(); + + input.pull(0.f); + } + } + + /** + * Implementation of {@link SimHypervisorCounters} for the hypervisor. + */ + private class HvCounters implements SimHypervisorCounters { + private long cpuActiveTime; + private long cpuIdleTime; + private long cpuStealTime; + private long cpuLostTime; + + @Override + public long getCpuActiveTime() { + return cpuActiveTime; + } + + @Override + public long getCpuIdleTime() { + return cpuIdleTime; + } + + @Override + public long getCpuStealTime() { + return cpuStealTime; + } + + @Override + public long getCpuLostTime() { + return cpuLostTime; + } + + @Override + public void sync() { + final Context context = activeContext; + + if (context != null) { + context.updateCounters(); + } + } + } + + /** + * Implementation of {@link SimHypervisorCounters} for the virtual machine. + */ + private static class VmCounters implements SimHypervisorCounters { + private final VirtualMachine vm; + private long cpuActiveTime; + private long cpuIdleTime; + private long cpuStealTime; + private long cpuLostTime; + + private VmCounters(VirtualMachine vm) { + this.vm = vm; + } + + @Override + public long getCpuActiveTime() { + return cpuActiveTime; + } + + @Override + public long getCpuIdleTime() { + return cpuIdleTime; + } + + @Override + public long getCpuStealTime() { + return cpuStealTime; + } + + @Override + public long getCpuLostTime() { + return cpuLostTime; + } + + @Override + public void sync() { + final VmContext context = (VmContext) vm.getActiveContext(); + + if (context != null) { + context.updateCounters(); + } + } + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisorCounters.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisorCounters.java new file mode 100644 index 00000000..fc77e9d6 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisorCounters.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022 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; + +/** + * Performance counters of a {@link SimHypervisor}. + */ +public interface SimHypervisorCounters { + /** + * Return the amount of time (in milliseconds) the CPUs of the hypervisor were actively running. + */ + long getCpuActiveTime(); + + /** + * Return the amount of time (in milliseconds) the CPUs of the hypervisor were idle. + */ + long getCpuIdleTime(); + + /** + * Return the amount of CPU time (in milliseconds) that virtual machines were ready to run, but were not able to. + */ + long getCpuStealTime(); + + /** + * Return the amount of CPU time (in milliseconds) that was lost due to interference between virtual machines. + */ + long getCpuLostTime(); + + /** + * Synchronize the counter values. + */ + void sync(); +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimVirtualMachine.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimVirtualMachine.java new file mode 100644 index 00000000..fdf5e47f --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimVirtualMachine.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022 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; + +import org.opendc.simulator.compute.SimMachine; + +/** + * A virtual {@link SimMachine} running on top of another {@link SimMachine}. + */ +public interface SimVirtualMachine extends SimMachine { + /** + * Return the performance counters associated with the virtual machine. + */ + SimHypervisorCounters getCounters(); + + /** + * Return the CPU usage of the VM in MHz. + */ + double getCpuUsage(); + + /** + * Return the CPU usage of the VM in MHz. + */ + double getCpuDemand(); + + /** + * Return the CPU capacity of the VM in MHz. + */ + double getCpuCapacity(); +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernor.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernor.java new file mode 100644 index 00000000..69a371e1 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernor.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 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.cpufreq; + +/** + * A [ScalingGovernor] in the CPUFreq subsystem of OpenDC is responsible for scaling the frequency of simulated CPUs + * independent of the particular implementation of the CPU. + * + *

+ * Each of the scaling governors implements a single, possibly parametrized, performance scaling algorithm. + * + * @see documentation of the Linux CPUFreq subsystem. + */ +public interface ScalingGovernor { + /** + * This method is invoked when the governor is started. + */ + default void onStart() {} + + /** + * This method is invoked when the governor should re-decide the frequency limits. + * + * @param load The load of the system. + */ + default void onLimit(double load) {} +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernorFactory.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernorFactory.java new file mode 100644 index 00000000..97a49879 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernorFactory.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 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.cpufreq; + +/** + * Factory interface for a {@link ScalingGovernor}. + */ +public interface ScalingGovernorFactory { + /** + * Create the scaling logic for the specified {@link ScalingPolicy}. + */ + ScalingGovernor newGovernor(ScalingPolicy policy); +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernors.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernors.java new file mode 100644 index 00000000..2b10ae59 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernors.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2022 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.cpufreq; + +/** + * Collection of common {@link ScalingGovernor} implementations. + */ +public class ScalingGovernors { + private ScalingGovernors() {} + + /** + * Return a {@link ScalingGovernorFactory} for the performance scaling governor. + * + *

+ * This governor causes the highest possible frequency to be requested from the CPUs. + */ + public static ScalingGovernorFactory performance() { + return PerformanceScalingGovernor.FACTORY; + } + + /** + * Return a {@link ScalingGovernorFactory} for the powersave scaling governor. + * + *

+ * This governor causes the lowest possible frequency to be requested from the CPUs. + */ + public static ScalingGovernorFactory powerSave() { + return PowerSaveScalingGovernor.FACTORY; + } + + /** + * Return a {@link ScalingGovernorFactory} for the conservative scaling governor from the Linux kernel. + * + * @param threshold The threshold before scaling. + * @param stepSize The size of the frequency steps (use negative value for automatic). + */ + public static ScalingGovernorFactory conservative(double threshold, double stepSize) { + return (policy) -> new ConservativeScalingGovernor(policy, threshold, stepSize); + } + + /** + * Return a {@link ScalingGovernorFactory} for the conservative scaling governor from the Linux kernel. + * + * @param threshold The threshold before scaling. + */ + public static ScalingGovernorFactory conservative(double threshold) { + return conservative(threshold, -1.0); + } + + /** + * Return a {@link ScalingGovernorFactory} for the ondemand scaling governor from the Linux kernel. + * + * @param threshold The threshold before scaling. + */ + public static ScalingGovernorFactory ondemand(double threshold) { + return (policy) -> new OnDemandScalingGovernor(policy, threshold); + } + + private abstract static class AbstractScalingGovernor implements ScalingGovernor { + protected final ScalingPolicy policy; + + AbstractScalingGovernor(ScalingPolicy policy) { + this.policy = policy; + } + } + + private static class PerformanceScalingGovernor extends AbstractScalingGovernor { + static final ScalingGovernorFactory FACTORY = PerformanceScalingGovernor::new; + + private PerformanceScalingGovernor(ScalingPolicy policy) { + super(policy); + } + + @Override + public void onStart() { + policy.setTarget(policy.getMax()); + } + } + + private static class PowerSaveScalingGovernor extends AbstractScalingGovernor { + static final ScalingGovernorFactory FACTORY = PowerSaveScalingGovernor::new; + + private PowerSaveScalingGovernor(ScalingPolicy policy) { + super(policy); + } + + @Override + public void onStart() { + policy.setTarget(policy.getMin()); + } + } + + private static class ConservativeScalingGovernor extends AbstractScalingGovernor { + private final double threshold; + private final double stepSize; + private double previousLoad; + + private ConservativeScalingGovernor(ScalingPolicy policy, double threshold, double stepSize) { + super(policy); + + this.threshold = threshold; + this.previousLoad = threshold; + + if (stepSize < 0) { + // https://github.com/torvalds/linux/blob/master/drivers/cpufreq/cpufreq_conservative.c#L33 + this.stepSize = policy.getMax() * 0.05; + } else { + this.stepSize = Math.min(stepSize, policy.getMax()); + } + } + + @Override + public void onStart() { + policy.setTarget(policy.getMin()); + } + + @Override + public void onLimit(double load) { + final ScalingPolicy policy = this.policy; + double currentTarget = policy.getTarget(); + if (load > threshold) { + // Check for load increase (see: + // https://github.com/torvalds/linux/blob/master/drivers/cpufreq/cpufreq_conservative.c#L102) + double step = 0.0; + + if (load > previousLoad) { + step = stepSize; + } else if (load < previousLoad) { + step = -stepSize; + } + + double target = Math.min(Math.max(currentTarget + step, policy.getMin()), policy.getMax()); + policy.setTarget(target); + } + previousLoad = load; + } + } + + private static class OnDemandScalingGovernor extends AbstractScalingGovernor { + private final double threshold; + private final double multiplier; + + private OnDemandScalingGovernor(ScalingPolicy policy, double threshold) { + super(policy); + + this.threshold = threshold; + this.multiplier = (policy.getMax() - policy.getMin()) / 100; + } + + @Override + public void onStart() { + policy.setTarget(policy.getMin()); + } + + @Override + public void onLimit(double load) { + final ScalingPolicy policy = this.policy; + double target; + + if (load < threshold) { + /* Proportional scaling (see: https://github.com/torvalds/linux/blob/master/drivers/cpufreq/cpufreq_ondemand.c#L151). */ + target = policy.getMin() + load * multiplier; + } else { + target = policy.getMax(); + } + + policy.setTarget(target); + } + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingPolicy.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingPolicy.java new file mode 100644 index 00000000..0cdb7a0b --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingPolicy.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 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.cpufreq; + +import org.opendc.simulator.compute.SimProcessingUnit; + +/** + * An interface that holds the state managed by a {@link ScalingGovernor} and used by the underlying machine to control + * the CPU frequencies. + */ +public interface ScalingPolicy { + /** + * The processing unit that is associated with this policy. + */ + SimProcessingUnit getCpu(); + + /** + * Return the target frequency which the CPU should attempt to attain. + */ + double getTarget(); + + /** + * Set the target frequency which the CPU should attempt to attain. + */ + void setTarget(double target); + + /** + * Return the minimum frequency to which the CPU may scale. + */ + double getMin(); + + /** + * Return the maximum frequency to which the CPU may scale. + */ + double getMax(); +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.java new file mode 100644 index 00000000..cc671379 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2022 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; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collections; +import java.util.WeakHashMap; + +/** + * A domain where virtual machines may incur performance variability due to operating on the same resource and + * therefore causing interference. + */ +public final class VmInterferenceDomain { + /** + * A cache to maintain a mapping between the active profiles in this domain. + */ + private final WeakHashMap cache = new WeakHashMap<>(); + + /** + * The set of members active in this domain. + */ + private final ArrayList activeKeys = new ArrayList<>(); + + /** + * Queue of participants that will be removed or added to the active groups. + */ + private final ArrayDeque participants = new ArrayDeque<>(); + + /** + * Join this interference domain with the specified profile and return the {@link VmInterferenceMember} + * associated with the profile. If the member does not exist, it will be created. + */ + public VmInterferenceMember join(VmInterferenceProfile profile) { + return cache.computeIfAbsent(profile, (key) -> key.newMember(this)); + } + + /** + * Mark the specified member as active in this interference domain. + */ + void activate(VmInterferenceMember member) { + final ArrayList activeKeys = this.activeKeys; + int pos = Collections.binarySearch(activeKeys, member); + if (pos < 0) { + activeKeys.add(-pos - 1, member); + } + + computeActiveGroups(activeKeys, member); + } + + /** + * Mark the specified member as inactive in this interference domain. + */ + void deactivate(VmInterferenceMember member) { + final ArrayList activeKeys = this.activeKeys; + activeKeys.remove(member); + computeActiveGroups(activeKeys, member); + } + + /** + * (Re-)compute the active groups. + */ + private void computeActiveGroups(ArrayList activeKeys, VmInterferenceMember member) { + if (activeKeys.isEmpty()) { + return; + } + + final int[] groups = member.membership; + final int[][] members = member.members; + final ArrayDeque participants = this.participants; + + for (int group : groups) { + int[] groupMembers = members[group]; + + int i = 0; + int j = 0; + int intersection = 0; + + // Compute the intersection of the group members and the current active members + while (i < groupMembers.length && j < activeKeys.size()) { + int l = groupMembers[i]; + final VmInterferenceMember rightEntry = activeKeys.get(j); + int r = rightEntry.id; + + if (l < r) { + i++; + } else if (l > r) { + j++; + } else { + if (++intersection > 1) { + rightEntry.addGroup(group); + } else { + participants.add(rightEntry); + } + + i++; + j++; + } + } + + while (true) { + VmInterferenceMember participant = participants.poll(); + + if (participant == null) { + break; + } + + if (intersection <= 1) { + participant.removeGroup(group); + } else { + participant.addGroup(group); + } + } + } + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceMember.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceMember.java new file mode 100644 index 00000000..64cd5077 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceMember.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2022 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; + +import java.util.Arrays; +import java.util.SplittableRandom; +import org.jetbrains.annotations.NotNull; + +/** + * A participant of an interference domain. + */ +public final class VmInterferenceMember implements Comparable { + private final VmInterferenceDomain domain; + private final VmInterferenceModel model; + final int id; + final int[] membership; + final int[][] members; + private final double[] targets; + private final double[] scores; + + private int[] groups = new int[2]; + private int groupsSize = 0; + + private int refCount = 0; + + VmInterferenceMember( + VmInterferenceDomain domain, + VmInterferenceModel model, + int id, + int[] membership, + int[][] members, + double[] targets, + double[] scores) { + this.domain = domain; + this.model = model; + this.id = id; + this.membership = membership; + this.members = members; + this.targets = targets; + this.scores = scores; + } + + /** + * Mark this member as active in this interference domain. + */ + public void activate() { + if (refCount++ <= 0) { + domain.activate(this); + } + } + + /** + * Mark this member as inactive in this interference domain. + */ + public void deactivate() { + if (--refCount <= 0) { + domain.deactivate(this); + } + } + + /** + * 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 double apply(SplittableRandom random, double load) { + int groupsSize = this.groupsSize; + + if (groupsSize == 0) { + return 1.0; + } + + int[] groups = this.groups; + double[] targets = this.targets; + + int low = 0; + int high = groupsSize - 1; + int group = -1; + + // Perform binary search over the groups based on target load + while (low <= high) { + int mid = low + high >>> 1; + int midGroup = groups[mid]; + double target = targets[midGroup]; + + if (target < load) { + low = mid + 1; + group = midGroup; + } else if (target > load) { + high = mid - 1; + } else { + group = midGroup; + break; + } + } + + if (group >= 0 && random.nextInt(members[group].length) == 0) { + return scores[group]; + } + + return 1.0; + } + + /** + * Add an active group to this member. + */ + void addGroup(int group) { + int[] groups = this.groups; + int groupsSize = this.groupsSize; + int pos = Arrays.binarySearch(groups, 0, groupsSize, group); + + if (pos >= 0) { + return; + } + + int idx = -pos - 1; + + if (groups.length == groupsSize) { + int newSize = groupsSize + (groupsSize >> 1); + groups = Arrays.copyOf(groups, newSize); + this.groups = groups; + } + + System.arraycopy(groups, idx, groups, idx + 1, groupsSize - idx); + groups[idx] = group; + this.groupsSize += 1; + } + + /** + * Remove an active group from this member. + */ + void removeGroup(int group) { + int[] groups = this.groups; + int groupsSize = this.groupsSize; + int pos = Arrays.binarySearch(groups, 0, groupsSize, group); + + if (pos < 0) { + return; + } + + System.arraycopy(groups, pos + 1, groups, pos, groupsSize - pos - 1); + this.groupsSize -= 1; + } + + @Override + public int compareTo(@NotNull VmInterferenceMember member) { + int cmp = Integer.compare(model.hashCode(), member.model.hashCode()); + if (cmp != 0) { + return cmp; + } + + return Integer.compare(id, member.id); + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.java new file mode 100644 index 00000000..e2093266 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2022 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; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import org.jetbrains.annotations.Nullable; + +/** + * An interference model that models the resource interference between virtual machines on a host. + */ +public final class VmInterferenceModel { + private final Map idMapping; + private final int[][] members; + private final int[][] membership; + private final double[] targets; + private final double[] scores; + + private VmInterferenceModel( + Map idMapping, int[][] members, int[][] membership, double[] targets, double[] scores) { + this.idMapping = idMapping; + this.members = members; + this.membership = membership; + this.targets = targets; + this.scores = scores; + } + + /** + * Create a {@link Builder} for constructing a {@link VmInterferenceModel}. + */ + public static Builder builder() { + return new Builder(256); + } + + /** + * Return the {@link VmInterferenceProfile} associated with the specified id. + * + * @param id The identifier of the virtual machine. + * @return A {@link VmInterferenceProfile} representing the virtual machine as part of interference model or + * null if there is no profile for the virtual machine. + */ + @Nullable + public VmInterferenceProfile getProfile(String id) { + Integer intId = idMapping.get(id); + if (intId == null) { + return null; + } + return new VmInterferenceProfile(this, intId, membership[intId], members, targets, scores); + } + + /** + * Builder class for a {@link VmInterferenceModel}. + */ + public static final class Builder { + private double[] targets; + private double[] scores; + private final ArrayList> members; + private final TreeSet ids; + private int size; + + private Builder(int initialCapacity) { + this.targets = new double[initialCapacity]; + this.scores = new double[initialCapacity]; + this.members = new ArrayList<>(initialCapacity); + this.ids = new TreeSet<>(); + } + + /** + * Add the specified group to the model. + */ + public Builder addGroup(Set members, double targetLoad, double score) { + int size = this.size; + + if (size == targets.length) { + grow(); + } + + targets[size] = targetLoad; + scores[size] = score; + this.members.add(members); + ids.addAll(members); + + this.size++; + + return this; + } + + /** + * Build the {@link VmInterferenceModel}. + */ + public VmInterferenceModel build() { + int size = this.size; + double[] targets = this.targets; + double[] scores = this.scores; + ArrayList> members = this.members; + + Integer[] indices = new Integer[size]; + Arrays.setAll(indices, (i) -> i); + Arrays.sort( + indices, + Comparator.comparingDouble((Integer l) -> targets[l]) + .thenComparingDouble(l -> scores[l]) + .thenComparingInt(l -> l)); + + double[] newTargets = new double[size]; + double[] newScores = new double[size]; + int[][] newMembers = new int[size][]; + + int nextId = 0; + + Map idMapping = new HashMap<>(); + TreeMap> membership = new TreeMap<>(); + for (String id : ids) { + idMapping.put(id, nextId++); + membership.put(id, new ArrayList<>()); + } + + for (int group = 0; group < indices.length; group++) { + int j = indices[group]; + newTargets[group] = targets[j]; + newScores[group] = scores[j]; + + Set groupMembers = members.get(j); + int[] newGroupMembers = new int[groupMembers.size()]; + int k = 0; + + for (String groupMember : groupMembers) { + newGroupMembers[k++] = idMapping.get(groupMember); + } + + Arrays.sort(newGroupMembers); + newMembers[group] = newGroupMembers; + + for (String member : groupMembers) { + membership.get(member).add(group); + } + } + + int[][] newMembership = new int[membership.size()][]; + int k = 0; + for (ArrayList value : membership.values()) { + newMembership[k++] = value.stream().mapToInt(i -> i).toArray(); + } + + return new VmInterferenceModel(idMapping, newMembers, newMembership, newTargets, newScores); + } + + /** + * Helper function to grow the capacity of the internal arrays. + */ + private void grow() { + int oldSize = targets.length; + int newSize = oldSize + (oldSize >> 1); + + targets = Arrays.copyOf(targets, newSize); + scores = Arrays.copyOf(scores, newSize); + } + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceProfile.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceProfile.java new file mode 100644 index 00000000..3f0c0a88 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceProfile.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2022 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 profile of a particular virtual machine describing its interference pattern with other virtual machines. + */ +public final class VmInterferenceProfile { + private final VmInterferenceModel model; + private final int id; + private final int[] membership; + private final int[][] members; + private final double[] targets; + private final double[] scores; + + /** + * Construct a {@link VmInterferenceProfile}. + */ + VmInterferenceProfile( + VmInterferenceModel model, int id, int[] membership, int[][] members, double[] targets, double[] scores) { + this.model = model; + this.id = id; + this.membership = membership; + this.members = members; + this.targets = targets; + this.scores = scores; + } + + /** + * Create a new {@link VmInterferenceMember} based on this profile for the specified domain. + */ + VmInterferenceMember newMember(VmInterferenceDomain domain) { + return new VmInterferenceMember(domain, model, id, membership, members, targets, scores); + } + + @Override + public String toString() { + return "VmInterferenceProfile[id=" + id + "]"; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/MachineModel.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/MachineModel.java new file mode 100644 index 00000000..2c625fce --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/MachineModel.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2022 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.model; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * A description of the physical or virtual machine on which a bootable image runs. + */ +public final class MachineModel { + private final List cpus; + private final List memory; + private final List net; + private final List storage; + + /** + * Construct a {@link MachineModel} instance. + * + * @param cpus The list of processing units available to the image. + * @param memory The list of memory units available to the image. + * @param net A list of network adapters available to the machine. + * @param storage A list of storage devices available to the machine. + */ + public MachineModel( + Iterable cpus, + Iterable memory, + Iterable net, + Iterable storage) { + this.cpus = new ArrayList<>(); + cpus.forEach(this.cpus::add); + + this.memory = new ArrayList<>(); + memory.forEach(this.memory::add); + + this.net = new ArrayList<>(); + net.forEach(this.net::add); + + this.storage = new ArrayList<>(); + storage.forEach(this.storage::add); + } + + /** + * Construct a {@link MachineModel} instance. + * + * @param cpus The list of processing units available to the image. + * @param memory The list of memory units available to the image. + */ + public MachineModel(Iterable cpus, Iterable memory) { + this(cpus, memory, Collections.emptyList(), Collections.emptyList()); + } + + /** + * Optimize the [MachineModel] by merging all resources of the same type into a single resource with the combined + * capacity. Such configurations can be simulated more efficiently by OpenDC. + */ + public MachineModel optimize() { + ProcessingUnit originalCpu = cpus.get(0); + + double freq = 0.0; + for (ProcessingUnit cpu : cpus) { + freq += cpu.getFrequency(); + } + + ProcessingNode originalNode = originalCpu.getNode(); + ProcessingNode processingNode = new ProcessingNode( + originalNode.getVendor(), originalNode.getModelName(), originalNode.getArchitecture(), 1); + ProcessingUnit processingUnit = new ProcessingUnit(processingNode, originalCpu.getId(), freq); + + long memorySize = 0; + for (MemoryUnit mem : memory) { + memorySize += mem.getSize(); + } + MemoryUnit memoryUnit = new MemoryUnit("Generic", "Generic", 3200.0, memorySize); + + return new MachineModel(List.of(processingUnit), List.of(memoryUnit)); + } + + /** + * Return the processing units of this machine. + */ + public List getCpus() { + return Collections.unmodifiableList(cpus); + } + + /** + * Return the memory units of this machine. + */ + public List getMemory() { + return Collections.unmodifiableList(memory); + } + + /** + * Return the network adapters of this machine. + */ + public List getNetwork() { + return Collections.unmodifiableList(net); + } + + /** + * Return the storage devices of this machine. + */ + public List getStorage() { + return Collections.unmodifiableList(storage); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MachineModel that = (MachineModel) o; + return cpus.equals(that.cpus) + && memory.equals(that.memory) + && net.equals(that.net) + && storage.equals(that.storage); + } + + @Override + public int hashCode() { + return Objects.hash(cpus, memory, net, storage); + } + + @Override + public String toString() { + return "MachineModel[cpus=" + cpus + ",memory=" + memory + ",net=" + net + ",storage=" + storage + "]"; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/MemoryUnit.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/MemoryUnit.java new file mode 100644 index 00000000..4250f5a2 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/MemoryUnit.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2022 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.model; + +import java.util.Objects; + +/** + * A memory unit of a compute resource, either virtual or physical. + */ +public final class MemoryUnit { + private final String vendor; + private final String modelName; + private final double speed; + private final long size; + + /** + * Construct a {@link ProcessingNode} instance. + * + * @param vendor The vendor of the storage device. + * @param modelName The model name of the device. + * @param speed The access speed of the memory in MHz. + * @param size The size of the memory unit in MBs. + */ + public MemoryUnit(String vendor, String modelName, double speed, long size) { + this.vendor = vendor; + this.modelName = modelName; + this.speed = speed; + this.size = size; + } + + /** + * Return the vendor of the storage device. + */ + public String getVendor() { + return vendor; + } + + /** + * Return the model name of the device. + */ + public String getModelName() { + return modelName; + } + + /** + * Return the access speed of the memory in MHz. + */ + public double getSpeed() { + return speed; + } + + /** + * Return the size of the memory unit in MBs. + */ + public long getSize() { + return size; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MemoryUnit that = (MemoryUnit) o; + return Double.compare(that.speed, speed) == 0 + && size == that.size + && vendor.equals(that.vendor) + && modelName.equals(that.modelName); + } + + @Override + public int hashCode() { + return Objects.hash(vendor, modelName, speed, size); + } + + @Override + public String toString() { + return "ProcessingNode[vendor='" + vendor + "',modelName='" + modelName + "',speed=" + speed + "MHz,size=" + + size + "MB]"; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/NetworkAdapter.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/NetworkAdapter.java new file mode 100644 index 00000000..ff3daa40 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/NetworkAdapter.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2022 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.model; + +import java.util.Objects; + +/** + * A description of a network adapter + */ +public final class NetworkAdapter { + private final String vendor; + private final String modelName; + private final double bandwidth; + + /** + * Construct a {@link NetworkAdapter} instance. + * + * @param vendor The vendor of the storage device. + * @param modelName The model name of the device. + * @param bandwidth The bandwidth of the network adapter in Mbps. + */ + public NetworkAdapter(String vendor, String modelName, double bandwidth) { + this.vendor = vendor; + this.modelName = modelName; + this.bandwidth = bandwidth; + } + + /** + * Return the vendor of the storage device. + */ + public String getVendor() { + return vendor; + } + + /** + * Return the model name of the device. + */ + public String getModelName() { + return modelName; + } + + /** + * Return the bandwidth of the network adapter in Mbps. + */ + public double getBandwidth() { + return bandwidth; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NetworkAdapter that = (NetworkAdapter) o; + return Double.compare(that.bandwidth, bandwidth) == 0 + && vendor.equals(that.vendor) + && modelName.equals(that.modelName); + } + + @Override + public int hashCode() { + return Objects.hash(vendor, modelName, bandwidth); + } + + @Override + public String toString() { + return "NetworkAdapter[vendor='" + vendor + "',modelName='" + modelName + "',bandwidth=" + bandwidth + "Mbps]"; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/ProcessingNode.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/ProcessingNode.java new file mode 100644 index 00000000..01a87b96 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/ProcessingNode.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2022 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.model; + +import java.util.Objects; + +/** + * A processing node/package/socket containing possibly several CPU cores. + */ +public final class ProcessingNode { + private final String vendor; + private final String modelName; + private final String arch; + private final int coreCount; + + /** + * Construct a {@link ProcessingNode} instance. + * + * @param vendor The vendor of the storage device. + * @param modelName The model name of the device. + * @param arch The micro-architecture of the processor node. + * @param coreCount The number of logical CPUs in the processor node. + */ + public ProcessingNode(String vendor, String modelName, String arch, int coreCount) { + this.vendor = vendor; + this.modelName = modelName; + this.arch = arch; + this.coreCount = coreCount; + } + + /** + * Return the vendor of the storage device. + */ + public String getVendor() { + return vendor; + } + + /** + * Return the model name of the device. + */ + public String getModelName() { + return modelName; + } + + /** + * Return the micro-architecture of the processor node. + */ + public String getArchitecture() { + return arch; + } + + /** + * Return the number of logical CPUs in the processor node. + */ + public int getCoreCount() { + return coreCount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ProcessingNode that = (ProcessingNode) o; + return coreCount == that.coreCount + && vendor.equals(that.vendor) + && modelName.equals(that.modelName) + && arch.equals(that.arch); + } + + @Override + public int hashCode() { + return Objects.hash(vendor, modelName, arch, coreCount); + } + + @Override + public String toString() { + return "ProcessingNode[vendor='" + vendor + "',modelName='" + modelName + "',arch=" + arch + ",coreCount=" + + coreCount + "]"; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/ProcessingUnit.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/ProcessingUnit.java new file mode 100644 index 00000000..51a045d1 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/ProcessingUnit.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2022 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.model; + +import java.util.Objects; + +/** + * A single logical compute unit of processor node, either virtual or physical. + */ +public final class ProcessingUnit { + private final ProcessingNode node; + private final int id; + private final double frequency; + + /** + * Construct a {@link ProcessingUnit} instance. + * + * @param node The processing node containing the CPU core. + * @param id The identifier of the CPU core within the processing node. + * @param frequency The clock rate of the CPU in MHz. + */ + public ProcessingUnit(ProcessingNode node, int id, double frequency) { + this.node = node; + this.id = id; + this.frequency = frequency; + } + + /** + * Return the processing node containing the CPU core. + */ + public ProcessingNode getNode() { + return node; + } + + /** + * Return the identifier of the CPU core within the processing node. + */ + public int getId() { + return id; + } + + /** + * Return the clock rate of the CPU in MHz. + */ + public double getFrequency() { + return frequency; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ProcessingUnit that = (ProcessingUnit) o; + return id == that.id && Double.compare(that.frequency, frequency) == 0 && Objects.equals(node, that.node); + } + + @Override + public int hashCode() { + return Objects.hash(node, id, frequency); + } + + @Override + public String toString() { + return "ProcessingUnit[node=" + node + ",id=" + id + ",frequency=" + frequency + "]"; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/StorageDevice.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/StorageDevice.java new file mode 100644 index 00000000..549ccc7e --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/StorageDevice.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2022 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.model; + +import java.util.Objects; + +/** + * Model for a physical storage device attached to a machine. + */ +public final class StorageDevice { + private final String vendor; + private final String modelName; + private final double capacity; + private final double readBandwidth; + private final double writeBandwidth; + + /** + * Construct a {@link StorageDevice} instance. + * + * @param vendor The vendor of the storage device. + * @param modelName The model name of the device. + * @param capacity The capacity of the device. + * @param readBandwidth The read bandwidth of the device in MBps. + * @param writeBandwidth The write bandwidth of the device in MBps. + */ + public StorageDevice( + String vendor, String modelName, double capacity, double readBandwidth, double writeBandwidth) { + this.vendor = vendor; + this.modelName = modelName; + this.capacity = capacity; + this.readBandwidth = readBandwidth; + this.writeBandwidth = writeBandwidth; + } + + /** + * Return the vendor of the storage device. + */ + public String getVendor() { + return vendor; + } + + /** + * Return the model name of the device. + */ + public String getModelName() { + return modelName; + } + + /** + * Return the capacity of the device. + */ + public double getCapacity() { + return capacity; + } + + /** + * Return the read bandwidth of the device in MBps. + */ + public double getReadBandwidth() { + return readBandwidth; + } + + /** + * Return the write bandwidth of the device in MBps. + */ + public double getWriteBandwidth() { + return writeBandwidth; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StorageDevice that = (StorageDevice) o; + return Double.compare(that.capacity, capacity) == 0 + && Double.compare(that.readBandwidth, readBandwidth) == 0 + && Double.compare(that.writeBandwidth, writeBandwidth) == 0 + && vendor.equals(that.vendor) + && modelName.equals(that.modelName); + } + + @Override + public int hashCode() { + return Objects.hash(vendor, modelName, capacity, readBandwidth, writeBandwidth); + } + + @Override + public String toString() { + return "StorageDevice[vendor='" + vendor + "',modelName='" + modelName + "',capacity=" + capacity + + ",readBandwidth=" + readBandwidth + ",writeBandwidth=" + writeBandwidth + "]"; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CpuPowerModel.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CpuPowerModel.java new file mode 100644 index 00000000..e023d098 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CpuPowerModel.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 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.power; + +import org.opendc.simulator.compute.SimMachine; + +/** + * A model for estimating the power usage of a {@link SimMachine} based on the CPU usage. + */ +public interface CpuPowerModel { + /** + * Computes CPU power consumption for each host. + * + * @param utilization The CPU utilization percentage. + * @return A double value of CPU power consumption (in W). + */ + double computePower(double utilization); +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CpuPowerModels.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CpuPowerModels.java new file mode 100644 index 00000000..5d3d936b --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CpuPowerModels.java @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2022 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.power; + +import java.util.Arrays; + +/** + * A collection {@link CpuPowerModel} implementations. + */ +public class CpuPowerModels { + private CpuPowerModels() {} + + /** + * Construct a constant {@link CpuPowerModel}. + * + * @param power The power consumption fo the server at all times (in W). + */ + public static CpuPowerModel constant(double power) { + return new ConstantPowerModel(power); + } + + /** + * Construct a square root {@link CpuPowerModel} that is adapted from CloudSim. + * + * @param maxPower The maximum power draw of the server in W. + * @param idlePower The power draw of the server at its lowest utilization level in W. + */ + public static CpuPowerModel sqrt(double maxPower, double idlePower) { + return new SqrtPowerModel(maxPower, idlePower); + } + + /** + * Construct a linear {@link CpuPowerModel} that is adapted from CloudSim. + * + * @param maxPower The maximum power draw of the server in W. + * @param idlePower The power draw of the server at its lowest utilization level in W. + */ + public static CpuPowerModel linear(double maxPower, double idlePower) { + return new LinearPowerModel(maxPower, idlePower); + } + + /** + * Construct a square {@link CpuPowerModel} that is adapted from CloudSim. + * + * @param maxPower The maximum power draw of the server in W. + * @param idlePower The power draw of the server at its lowest utilization level in W. + */ + public static CpuPowerModel square(double maxPower, double idlePower) { + return new SquarePowerModel(maxPower, idlePower); + } + + /** + * Construct a cubic {@link CpuPowerModel} that is adapted from CloudSim. + * + * @param maxPower The maximum power draw of the server in W. + * @param idlePower The power draw of the server at its lowest utilization level in W. + */ + public static CpuPowerModel cubic(double maxPower, double idlePower) { + return new CubicPowerModel(maxPower, idlePower); + } + + /** + * Construct a {@link CpuPowerModel} that minimizes the mean squared error (MSE) + * to the actual power measurement by tuning the calibration parameter. + * + * @param maxPower The maximum power draw of the server in W. + * @param idlePower The power draw of the server at its lowest utilization level in W. + * @param calibrationFactor The parameter set to minimize the MSE. + * @see + * Fan et al., Power provisioning for a warehouse-sized computer, ACM SIGARCH'07 + */ + public static CpuPowerModel mse(double maxPower, double idlePower, double calibrationFactor) { + return new MsePowerModel(maxPower, idlePower, calibrationFactor); + } + + /** + * Construct an asymptotic {@link CpuPowerModel} adapted from GreenCloud. + * + * @param maxPower The maximum power draw of the server in W. + * @param idlePower The power draw of the server at its lowest utilization level in W. + * @param asymUtil A utilization level at which the server attains asymptotic, + * i.e., close to linear power consumption versus the offered load. + * For most of the CPUs,a is in [0.2, 0.5]. + * @param dvfs A flag indicates whether DVFS is enabled. + */ + public static CpuPowerModel asymptotic(double maxPower, double idlePower, double asymUtil, boolean dvfs) { + return new AsymptoticPowerModel(maxPower, idlePower, asymUtil, dvfs); + } + + /** + * Construct a linear interpolation model {@link CpuPowerModel} that is adapted from CloudSim. + * + *

+ * The power consumption is linearly interpolated over the given power levels. In case of two values, the first + * represents 0% utilization, while the last value represent 100% utilization. + * + * @param powerLevels An array of power consumption steps (in W) for a specific CPU utilization. + * @see Machines used in the SPEC benchmark + */ + public static CpuPowerModel interpolate(double... powerLevels) { + return new InterpolationPowerModel(powerLevels.clone()); + } + + /** + * Decorate an existing {@link CpuPowerModel} to ensure that zero power consumption is reported when there is no + * utilization. + * + * @param delegate The existing {@link CpuPowerModel} to decorate. + */ + public static CpuPowerModel zeroIdle(CpuPowerModel delegate) { + return new ZeroIdlePowerDecorator(delegate); + } + + private static final class ConstantPowerModel implements CpuPowerModel { + private final double power; + + ConstantPowerModel(double power) { + this.power = power; + } + + @Override + public double computePower(double utilization) { + return power; + } + + @Override + public String toString() { + return "ConstantPowerModel[power=" + power + "]"; + } + } + + private abstract static class MaxIdlePowerModel implements CpuPowerModel { + protected final double maxPower; + protected final double idlePower; + + MaxIdlePowerModel(double maxPower, double idlePower) { + this.maxPower = maxPower; + this.idlePower = idlePower; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[max=" + maxPower + ",idle=" + idlePower + "]"; + } + } + + private static final class SqrtPowerModel extends MaxIdlePowerModel { + private final double factor; + + SqrtPowerModel(double maxPower, double idlePower) { + super(maxPower, idlePower); + this.factor = (maxPower - idlePower) / Math.sqrt(100); + } + + @Override + public double computePower(double utilization) { + return idlePower + factor * Math.sqrt(utilization * 100); + } + } + + private static final class LinearPowerModel extends MaxIdlePowerModel { + private final double factor; + + LinearPowerModel(double maxPower, double idlePower) { + super(maxPower, idlePower); + this.factor = (maxPower - idlePower) / 100; + } + + @Override + public double computePower(double utilization) { + return idlePower + factor * utilization * 100; + } + } + + private static final class SquarePowerModel extends MaxIdlePowerModel { + private final double factor; + + SquarePowerModel(double maxPower, double idlePower) { + super(maxPower, idlePower); + this.factor = (maxPower - idlePower) / Math.pow(100, 2); + } + + @Override + public double computePower(double utilization) { + return idlePower + factor * Math.pow(utilization * 100, 2); + } + } + + private static final class CubicPowerModel extends MaxIdlePowerModel { + private final double factor; + + CubicPowerModel(double maxPower, double idlePower) { + super(maxPower, idlePower); + this.factor = (maxPower - idlePower) / Math.pow(100, 3); + } + + @Override + public double computePower(double utilization) { + return idlePower + factor * Math.pow(utilization * 100, 3); + } + } + + private static final class MsePowerModel extends MaxIdlePowerModel { + private final double calibrationFactor; + private final double factor; + + MsePowerModel(double maxPower, double idlePower, double calibrationFactor) { + super(maxPower, idlePower); + this.calibrationFactor = calibrationFactor; + this.factor = (maxPower - idlePower) / 100; + } + + @Override + public double computePower(double utilization) { + return idlePower + factor * (2 * utilization - Math.pow(utilization, calibrationFactor)) * 100; + } + + @Override + public String toString() { + return "MsePowerModel[max=" + maxPower + ",idle=" + idlePower + ",calibrationFactor=" + calibrationFactor + + "]"; + } + } + + private static final class AsymptoticPowerModel extends MaxIdlePowerModel { + private final double asymUtil; + private final boolean dvfs; + private final double factor; + + AsymptoticPowerModel(double maxPower, double idlePower, double asymUtil, boolean dvfs) { + super(maxPower, idlePower); + this.asymUtil = asymUtil; + this.dvfs = dvfs; + this.factor = (maxPower - idlePower) / 100; + } + + @Override + public double computePower(double utilization) { + if (dvfs) { + return idlePower + + (factor * 100) + / 2 + * (1 + + Math.pow(utilization, 3) + - Math.pow(Math.E, -Math.pow(utilization, 3) / asymUtil)); + } else { + return idlePower + (factor * 100) / 2 * (1 + utilization - Math.pow(Math.E, -utilization / asymUtil)); + } + } + + @Override + public String toString() { + return "AsymptoticPowerModel[max=" + maxPower + ",idle=" + idlePower + ",asymUtil=" + asymUtil + ",dvfs=" + + dvfs + "]"; + } + } + + private static final class InterpolationPowerModel implements CpuPowerModel { + private final double[] powerLevels; + + InterpolationPowerModel(double[] powerLevels) { + this.powerLevels = powerLevels; + } + + @Override + public double computePower(double utilization) { + final double[] powerLevels = this.powerLevels; + double clampedUtilization = Math.min(1.0, Math.max(0.0, utilization)); + + if (utilization % 0.1 == 0.0) { + return powerLevels[(int) (clampedUtilization * 10)]; + } + + int utilizationFlr = (int) Math.floor(clampedUtilization * 10); + int utilizationCil = (int) Math.ceil(clampedUtilization * 10); + double powerFlr = powerLevels[utilizationFlr]; + double powerCil = powerLevels[utilizationCil]; + double delta = (powerCil - powerFlr) / 10; + + return powerFlr + delta * (clampedUtilization - utilizationFlr / 10.0) * 100; + } + + @Override + public String toString() { + return "InterpolationPowerModel[levels=" + Arrays.toString(powerLevels) + "]"; + } + } + + private static final class ZeroIdlePowerDecorator implements CpuPowerModel { + private final CpuPowerModel delegate; + + ZeroIdlePowerDecorator(CpuPowerModel delegate) { + this.delegate = delegate; + } + + @Override + public double computePower(double utilization) { + if (utilization == 0.0) { + return 0.0; + } + + return delegate.computePower(utilization); + } + + @Override + public String toString() { + return "ZeroIdlePowerDecorator[delegate=" + delegate + "]"; + } + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimFlopsWorkload.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimFlopsWorkload.java new file mode 100644 index 00000000..255fd1b2 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimFlopsWorkload.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2022 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.workload; + +import java.util.List; +import org.opendc.simulator.compute.SimMachineContext; +import org.opendc.simulator.compute.SimProcessingUnit; +import org.opendc.simulator.flow2.FlowGraph; +import org.opendc.simulator.flow2.FlowStage; +import org.opendc.simulator.flow2.FlowStageLogic; +import org.opendc.simulator.flow2.OutPort; + +/** + * A {@link SimWorkload} that models applications as a static number of floating point operations executed on + * multiple cores of a compute resource. + */ +public class SimFlopsWorkload implements SimWorkload, FlowStageLogic { + private final long flops; + private final double utilization; + + private SimMachineContext ctx; + private FlowStage stage; + private OutPort[] outputs; + + private float remainingAmount; + private long lastUpdate; + + /** + * Construct a new {@link SimFlopsWorkload}. + * + * @param flops The number of floating point operations to perform for this task in MFLOPs. + * @param utilization A model of the CPU utilization of the application. + */ + public SimFlopsWorkload(long flops, double utilization) { + if (flops < 0) { + throw new IllegalArgumentException("Number of FLOPs must be positive"); + } else if (utilization <= 0.0 || utilization > 1.0) { + throw new IllegalArgumentException("Utilization must be in (0, 1]"); + } + + this.flops = flops; + this.utilization = utilization; + } + + @Override + public void onStart(SimMachineContext ctx) { + this.ctx = ctx; + + final FlowGraph graph = ctx.getGraph(); + final FlowStage stage = graph.newStage(this); + this.stage = stage; + + final List cpus = ctx.getCpus(); + final OutPort[] outputs = new OutPort[cpus.size()]; + this.outputs = outputs; + + for (int i = 0; i < cpus.size(); i++) { + final SimProcessingUnit cpu = cpus.get(i); + final OutPort output = stage.getOutlet("cpu" + i); + + graph.connect(output, cpu.getInput()); + outputs[i] = output; + } + + this.remainingAmount = flops; + this.lastUpdate = graph.getEngine().getClock().millis(); + } + + @Override + public void onStop(SimMachineContext ctx) { + this.ctx = null; + + final FlowStage stage = this.stage; + if (stage != null) { + this.stage = null; + stage.close(); + } + } + + @Override + public String toString() { + return "SimFlopsWorkload[FLOPs=" + flops + ",utilization=" + utilization + "]"; + } + + @Override + public long onUpdate(FlowStage ctx, long now) { + long lastUpdate = this.lastUpdate; + this.lastUpdate = now; + + long delta = Math.max(0, now - lastUpdate); + + float consumed = 0.f; + float limit = 0.f; + + for (final OutPort output : outputs) { + consumed += output.getRate() * delta; + + float outputLimit = (float) (output.getCapacity() * utilization); + limit += outputLimit; + + output.push(outputLimit); + } + consumed = (float) (consumed * 0.001); + + float remainingAmount = this.remainingAmount - consumed; + this.remainingAmount = remainingAmount; + + long duration = (long) Math.ceil(remainingAmount / limit * 1000); + + if (duration <= 0) { + final SimMachineContext machineContext = this.ctx; + if (machineContext != null) { + machineContext.shutdown(); + } + ctx.close(); + return Long.MAX_VALUE; + } + + return now + duration; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimRuntimeWorkload.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimRuntimeWorkload.java new file mode 100644 index 00000000..c3380b31 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimRuntimeWorkload.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2022 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.workload; + +import java.util.List; +import org.opendc.simulator.compute.SimMachineContext; +import org.opendc.simulator.compute.SimProcessingUnit; +import org.opendc.simulator.flow2.FlowGraph; +import org.opendc.simulator.flow2.FlowStage; +import org.opendc.simulator.flow2.FlowStageLogic; +import org.opendc.simulator.flow2.OutPort; + +/** + * A [SimWorkload] that models application execution as a single duration. + */ +public class SimRuntimeWorkload implements SimWorkload, FlowStageLogic { + private final long duration; + private final double utilization; + + private SimMachineContext ctx; + private FlowStage stage; + private OutPort[] outputs; + + private long remainingDuration; + private long lastUpdate; + + /** + * Construct a new {@link SimRuntimeWorkload}. + * + * @param duration The duration of the workload in milliseconds. + * @param utilization A model of the CPU utilization of the application. + */ + public SimRuntimeWorkload(long duration, double utilization) { + if (duration < 0) { + throw new IllegalArgumentException("Duration must be positive"); + } else if (utilization <= 0.0 || utilization > 1.0) { + throw new IllegalArgumentException("Utilization must be in (0, 1]"); + } + + this.duration = duration; + this.utilization = utilization; + } + + @Override + public void onStart(SimMachineContext ctx) { + this.ctx = ctx; + + final FlowGraph graph = ctx.getGraph(); + final FlowStage stage = graph.newStage(this); + this.stage = stage; + + final List cpus = ctx.getCpus(); + final OutPort[] outputs = new OutPort[cpus.size()]; + this.outputs = outputs; + + for (int i = 0; i < cpus.size(); i++) { + final SimProcessingUnit cpu = cpus.get(i); + final OutPort output = stage.getOutlet("cpu" + i); + + graph.connect(output, cpu.getInput()); + outputs[i] = output; + } + + this.remainingDuration = duration; + this.lastUpdate = graph.getEngine().getClock().millis(); + } + + @Override + public void onStop(SimMachineContext ctx) { + this.ctx = null; + + final FlowStage stage = this.stage; + if (stage != null) { + this.stage = null; + this.outputs = null; + stage.close(); + } + } + + @Override + public long onUpdate(FlowStage ctx, long now) { + long lastUpdate = this.lastUpdate; + this.lastUpdate = now; + + long delta = now - lastUpdate; + long duration = this.remainingDuration - delta; + + if (duration <= 0) { + final SimMachineContext machineContext = this.ctx; + if (machineContext != null) { + machineContext.shutdown(); + } + ctx.close(); + return Long.MAX_VALUE; + } + + this.remainingDuration = duration; + + for (final OutPort output : outputs) { + float limit = (float) (output.getCapacity() * utilization); + output.push(limit); + } + + return now + duration; + } + + @Override + public String toString() { + return "SimDurationWorkload[duration=" + duration + "ms,utilization=" + utilization + "]"; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTrace.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTrace.java new file mode 100644 index 00000000..0bd2b2eb --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTrace.java @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2022 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.workload; + +import java.util.Arrays; +import java.util.List; +import org.opendc.simulator.compute.SimMachineContext; +import org.opendc.simulator.compute.SimProcessingUnit; +import org.opendc.simulator.flow2.FlowGraph; +import org.opendc.simulator.flow2.FlowStage; +import org.opendc.simulator.flow2.FlowStageLogic; +import org.opendc.simulator.flow2.OutPort; + +/** + * A workload trace that describes the resource utilization over time in a collection of {@link SimTraceFragment}s. + */ +public final class SimTrace { + private final double[] usageCol; + private final long[] deadlineCol; + private final int[] coresCol; + private final int size; + + /** + * Construct a {@link SimTrace} instance. + * + * @param usageCol The column containing the CPU usage of each fragment (in MHz). + * @param deadlineCol The column containing the ending timestamp for each fragment (in epoch millis). + * @param coresCol The column containing the utilized cores. + * @param size The number of fragments in the trace. + */ + private SimTrace(double[] usageCol, long[] deadlineCol, int[] coresCol, int size) { + if (size < 0) { + throw new IllegalArgumentException("Invalid trace size"); + } else if (usageCol.length < size) { + throw new IllegalArgumentException("Invalid number of usage entries"); + } else if (deadlineCol.length < size) { + throw new IllegalArgumentException("Invalid number of deadline entries"); + } else if (coresCol.length < size) { + throw new IllegalArgumentException("Invalid number of core entries"); + } + + this.usageCol = usageCol; + this.deadlineCol = deadlineCol; + this.coresCol = coresCol; + this.size = size; + } + + /** + * Construct a {@link SimWorkload} for this trace. + * + * @param offset The offset for the timestamps. + */ + public SimWorkload createWorkload(long offset) { + return new Workload(offset, usageCol, deadlineCol, coresCol, size); + } + + /** + * Create a new {@link Builder} instance with the specified initial capacity. + */ + public static Builder builder(int initialCapacity) { + return new Builder(initialCapacity); + } + + /** + * Create a new {@link Builder} instance with a default initial capacity. + */ + public static Builder builder() { + return builder(256); + } + + /** + * Construct a {@link SimTrace} from the specified fragments. + * + * @param fragments The array of fragments to construct the trace from. + */ + public static SimTrace ofFragments(SimTraceFragment... fragments) { + final Builder builder = builder(fragments.length); + + for (SimTraceFragment fragment : fragments) { + builder.add(fragment.timestamp + fragment.duration, fragment.usage, fragment.cores); + } + + return builder.build(); + } + + /** + * Construct a {@link SimTrace} from the specified fragments. + * + * @param fragments The fragments to construct the trace from. + */ + public static SimTrace ofFragments(List fragments) { + final Builder builder = builder(fragments.size()); + + for (SimTraceFragment fragment : fragments) { + builder.add(fragment.timestamp + fragment.duration, fragment.usage, fragment.cores); + } + + return builder.build(); + } + + /** + * Builder class for a {@link SimTrace}. + */ + public static final class Builder { + private double[] usageCol; + private long[] deadlineCol; + private int[] coresCol; + + private int size; + private boolean isBuilt; + + /** + * Construct a new {@link Builder} instance. + */ + private Builder(int initialCapacity) { + this.usageCol = new double[initialCapacity]; + this.deadlineCol = new long[initialCapacity]; + this.coresCol = new int[initialCapacity]; + } + + /** + * Add a fragment to the trace. + * + * @param deadline The timestamp at which the fragment ends (in epoch millis). + * @param usage The CPU usage at this fragment. + * @param cores The number of cores used during this fragment. + */ + public void add(long deadline, double usage, int cores) { + if (isBuilt) { + recreate(); + } + + int size = this.size; + double[] usageCol = this.usageCol; + + if (size == usageCol.length) { + grow(); + usageCol = this.usageCol; + } + + deadlineCol[size] = deadline; + usageCol[size] = usage; + coresCol[size] = cores; + + this.size++; + } + + /** + * Build the {@link SimTrace} instance. + */ + public SimTrace build() { + isBuilt = true; + return new SimTrace(usageCol, deadlineCol, coresCol, size); + } + + /** + * Helper method to grow the capacity of the trace. + */ + private void grow() { + int arraySize = usageCol.length; + int newSize = arraySize + (arraySize >> 1); + + usageCol = Arrays.copyOf(usageCol, newSize); + deadlineCol = Arrays.copyOf(deadlineCol, newSize); + coresCol = Arrays.copyOf(coresCol, newSize); + } + + /** + * Clone the columns of the trace. + * + *

+ * This is necessary when a {@link SimTrace} has been built already, but the user is again adding entries to + * the builder. + */ + private void recreate() { + isBuilt = false; + usageCol = usageCol.clone(); + deadlineCol = deadlineCol.clone(); + coresCol = coresCol.clone(); + } + } + + /** + * Implementation of {@link SimWorkload} that executes a trace. + */ + private static class Workload implements SimWorkload, FlowStageLogic { + private SimMachineContext ctx; + private FlowStage stage; + private OutPort[] outputs; + private int index; + private int coreCount; + + private final long offset; + private final double[] usageCol; + private final long[] deadlineCol; + private final int[] coresCol; + private final int size; + + private Workload(long offset, double[] usageCol, long[] deadlineCol, int[] coresCol, int size) { + this.offset = offset; + this.usageCol = usageCol; + this.deadlineCol = deadlineCol; + this.coresCol = coresCol; + this.size = size; + } + + @Override + public void onStart(SimMachineContext ctx) { + this.ctx = ctx; + + final FlowGraph graph = ctx.getGraph(); + final List cpus = ctx.getCpus(); + + stage = graph.newStage(this); + coreCount = cpus.size(); + + final OutPort[] outputs = new OutPort[cpus.size()]; + this.outputs = outputs; + + for (int i = 0; i < cpus.size(); i++) { + final SimProcessingUnit cpu = cpus.get(i); + final OutPort output = stage.getOutlet("cpu" + i); + + graph.connect(output, cpu.getInput()); + outputs[i] = output; + } + } + + @Override + public void onStop(SimMachineContext ctx) { + this.ctx = null; + + final FlowStage stage = this.stage; + + if (stage != null) { + this.stage = null; + stage.close(); + } + } + + @Override + public long onUpdate(FlowStage ctx, long now) { + int size = this.size; + long offset = this.offset; + long nowOffset = now - offset; + + int index = this.index; + + long[] deadlines = deadlineCol; + long deadline = deadlines[index]; + + while (deadline <= nowOffset && ++index < size) { + deadline = deadlines[index]; + } + + if (index >= size) { + final SimMachineContext machineContext = this.ctx; + if (machineContext != null) { + machineContext.shutdown(); + } + ctx.close(); + return Long.MAX_VALUE; + } + + this.index = index; + + int cores = Math.min(coreCount, coresCol[index]); + float usage = (float) usageCol[index] / cores; + + final OutPort[] outputs = this.outputs; + + for (int i = 0; i < cores; i++) { + outputs[i].push(usage); + } + + for (int i = cores; i < outputs.length; i++) { + outputs[i].push(0.f); + } + + return deadline + offset; + } + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTraceFragment.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTraceFragment.java new file mode 100644 index 00000000..12c1348d --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTraceFragment.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2022 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.workload; + +import java.util.Objects; + +/** + * A fragment of the workload trace. + */ +public final class SimTraceFragment { + final long timestamp; + final long duration; + final double usage; + final int cores; + + /** + * Construct a {@link SimTraceFragment}. + * + * @param timestamp The timestamp at which the fragment starts (in epoch millis). + * @param duration The duration of the fragment (in milliseconds). + * @param usage The CPU usage during the fragment (in MHz). + * @param cores The amount of cores utilized during the fragment. + */ + public SimTraceFragment(long timestamp, long duration, double usage, int cores) { + this.timestamp = timestamp; + this.duration = duration; + this.usage = usage; + this.cores = cores; + } + + /** + * Return the timestamp at which the fragment starts (in epoch millis). + */ + public long getTimestamp() { + return timestamp; + } + + /** + * Return the duration of the fragment (in milliseconds). + */ + public long getDuration() { + return duration; + } + + /** + * Return the CPU usage during the fragment (in MHz). + */ + public double getUsage() { + return usage; + } + + /** + * Return the amount of cores utilized during the fragment. + */ + public int getCores() { + return cores; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SimTraceFragment that = (SimTraceFragment) o; + return timestamp == that.timestamp + && duration == that.duration + && Double.compare(that.usage, usage) == 0 + && cores == that.cores; + } + + @Override + public int hashCode() { + return Objects.hash(timestamp, duration, usage, cores); + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkload.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkload.java new file mode 100644 index 00000000..7be51265 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkload.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 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.workload; + +import org.opendc.simulator.compute.SimMachineContext; + +/** + * A model that characterizes the runtime behavior of some particular workload. + * + *

+ * Workloads are stateful objects that may be paused and resumed at a later moment. As such, be careful when using the + * same {@link SimWorkload} from multiple contexts. + */ +public interface SimWorkload { + /** + * This method is invoked when the workload is started. + * + * @param ctx The execution context in which the machine runs. + */ + void onStart(SimMachineContext ctx); + + /** + * This method is invoked when the workload is stopped. + * + * @param ctx The execution context in which the machine runs. + */ + void onStop(SimMachineContext ctx); +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkloadLifecycle.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkloadLifecycle.java new file mode 100644 index 00000000..f0e2561f --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkloadLifecycle.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2022 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.workload; + +import java.util.HashSet; +import org.opendc.simulator.compute.SimMachineContext; + +/** + * A helper class to manage the lifecycle of a {@link SimWorkload}. + */ +public final class SimWorkloadLifecycle { + private final SimMachineContext ctx; + private final HashSet waiting = new HashSet<>(); + + /** + * Construct a {@link SimWorkloadLifecycle} instance. + * + * @param ctx The {@link SimMachineContext} of the workload. + */ + public SimWorkloadLifecycle(SimMachineContext ctx) { + this.ctx = ctx; + } + + /** + * Register a "completer" callback that must be invoked before ending the lifecycle of the workload. + */ + public Runnable newCompleter() { + Runnable completer = new Runnable() { + @Override + public void run() { + final HashSet waiting = SimWorkloadLifecycle.this.waiting; + if (waiting.remove(this) && waiting.isEmpty()) { + ctx.shutdown(); + } + } + }; + waiting.add(completer); + return completer; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractMachine.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractMachine.kt deleted file mode 100644 index 71784567..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractMachine.kt +++ /dev/null @@ -1,239 +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 - -import mu.KotlinLogging -import org.opendc.simulator.compute.device.SimNetworkAdapter -import org.opendc.simulator.compute.device.SimPeripheral -import org.opendc.simulator.compute.model.MachineModel -import org.opendc.simulator.compute.model.MemoryUnit -import org.opendc.simulator.compute.model.NetworkAdapter -import org.opendc.simulator.compute.model.StorageDevice -import org.opendc.simulator.compute.workload.SimWorkload -import org.opendc.simulator.flow.FlowConsumer -import org.opendc.simulator.flow.FlowConvergenceListener -import org.opendc.simulator.flow.FlowEngine -import org.opendc.simulator.flow.FlowForwarder -import org.opendc.simulator.flow.FlowSink -import org.opendc.simulator.flow.FlowSource -import org.opendc.simulator.flow.batch - -/** - * Abstract implementation of the [SimMachine] interface. - * - * @param engine The engine to manage the machine's resources. - * @param model The model of the machine. - */ -public abstract class SimAbstractMachine( - protected val engine: FlowEngine, - final override val model: MachineModel -) : SimMachine, FlowConvergenceListener { - /** - * The resources allocated for this machine. - */ - public abstract val cpus: List - - /** - * The memory interface of the machine. - */ - public val memory: SimMemory = Memory(FlowSink(engine, model.memory.sumOf { it.size }.toDouble()), model.memory) - - /** - * The network interfaces available to the machine. - */ - public val net: List = model.net.mapIndexed { i, adapter -> NetworkAdapterImpl(engine, adapter, i) } - - /** - * The network interfaces available to the machine. - */ - public val storage: List = model.storage.mapIndexed { i, device -> StorageDeviceImpl(engine, device, i) } - - /** - * The peripherals of the machine. - */ - public override val peripherals: List = net.map { it as SimNetworkAdapter } - - /** - * The current active [Context]. - */ - private var _ctx: Context? = null - - override fun startWorkload(workload: SimWorkload, meta: Map): SimMachineContext { - check(_ctx == null) { "A machine cannot run concurrently" } - - val ctx = Context(workload, meta) - ctx.start() - return ctx - } - - override fun cancel() { - _ctx?.close() - } - - override fun onConverge(now: Long) {} - - /** - * The execution context in which the workload runs. - * - * @param workload The workload that is running on the machine. - * @param meta The metadata passed to the workload. - */ - private inner class Context( - private val workload: SimWorkload, - override val meta: Map - ) : SimMachineContext { - /** - * A flag to indicate that the context has been closed. - */ - private var isClosed = false - - val engine: FlowEngine = this@SimAbstractMachine.engine - - /** - * Start this context. - */ - fun start() { - try { - _ctx = this - engine.batch { workload.onStart(this) } - } catch (cause: Throwable) { - logger.warn(cause) { "Workload failed during onStart callback" } - close() - } - } - - override val cpus: List = this@SimAbstractMachine.cpus - - override val memory: SimMemory = this@SimAbstractMachine.memory - - override val net: List = this@SimAbstractMachine.net - - override val storage: List = this@SimAbstractMachine.storage - - override fun close() { - if (isClosed) { - return - } - - isClosed = true - assert(_ctx == this) { "Invariant violation: multiple contexts active for a single machine" } - _ctx = null - - // Cancel all the resources associated with the machine - doCancel() - - try { - workload.onStop(this) - } catch (cause: Throwable) { - logger.warn(cause) { "Workload failed during onStop callback" } - } - } - - /** - * Run the stop procedures for the resources associated with the machine. - */ - private fun doCancel() { - engine.batch { - for (cpu in cpus) { - cpu.cancel() - } - - memory.cancel() - - for (ifx in net) { - (ifx as NetworkAdapterImpl).disconnect() - } - - for (storage in storage) { - val impl = storage as StorageDeviceImpl - impl.read.cancel() - impl.write.cancel() - } - } - } - - override fun toString(): String = "SimAbstractMachine.Context" - } - - /** - * The [SimMemory] implementation for a machine. - */ - private class Memory(source: FlowSink, override val models: List) : SimMemory, FlowConsumer by source { - override fun toString(): String = "SimAbstractMachine.Memory" - } - - /** - * The [SimNetworkAdapter] implementation for a machine. - */ - private class NetworkAdapterImpl( - engine: FlowEngine, - model: NetworkAdapter, - index: Int - ) : SimNetworkAdapter(), SimNetworkInterface { - override val name: String = "eth$index" - - override val bandwidth: Double = model.bandwidth - - override val provider: FlowConsumer - get() = _rx - - override fun createConsumer(): FlowSource = _tx - - override val tx: FlowConsumer - get() = _tx - private val _tx = FlowForwarder(engine) - - override val rx: FlowSource - get() = _rx - private val _rx = FlowForwarder(engine) - - override fun toString(): String = "SimAbstractMachine.NetworkAdapterImpl[name=$name,bandwidth=$bandwidth]" - } - - /** - * The [SimStorageInterface] implementation for a machine. - */ - private class StorageDeviceImpl( - engine: FlowEngine, - model: StorageDevice, - index: Int - ) : SimStorageInterface { - override val name: String = "disk$index" - - override val capacity: Double = model.capacity - - override val read: FlowConsumer = FlowSink(engine, model.readBandwidth) - - override val write: FlowConsumer = FlowSink(engine, model.writeBandwidth) - - override fun toString(): String = "SimAbstractMachine.StorageDeviceImpl[name=$name,capacity=$capacity]" - } - - private companion object { - /** - * The logging instance associated with this class. - */ - @JvmStatic - private val logger = KotlinLogging.logger {} - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt deleted file mode 100644 index 4c824440..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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 org.opendc.simulator.compute - -import org.opendc.simulator.compute.device.SimPsu -import org.opendc.simulator.compute.model.MachineModel -import org.opendc.simulator.compute.model.ProcessingUnit -import org.opendc.simulator.compute.power.PowerDriver -import org.opendc.simulator.flow.FlowConsumer -import org.opendc.simulator.flow.FlowEngine -import org.opendc.simulator.flow.FlowSink -import kotlin.math.max - -/** - * A simulated bare-metal machine that is able to run a single workload. - * - * A [SimBareMetalMachine] is a stateful object, and you should be careful when operating this object concurrently. For - * example, the class expects only a single concurrent call to [run]. - * - * @param engine The [FlowEngine] to drive the simulation. - * @param model The machine model to simulate. - * @param powerDriver The power driver to use. - * @param psu The power supply of the machine. - */ -public class SimBareMetalMachine( - engine: FlowEngine, - model: MachineModel, - powerDriver: PowerDriver, - public val psu: SimPsu = SimPsu(500.0, mapOf(1.0 to 1.0)) -) : SimAbstractMachine(engine, model) { - /** - * The current power usage of the machine (without PSU loss) in W. - */ - public val powerUsage: Double - get() = _powerUsage - private var _powerUsage = 0.0 - - /** - * The total energy usage of the machine (without PSU loss) in Joules. - */ - public val energyUsage: Double - get() { - computeEnergyUsage(engine.clock.millis()) - return _energyUsage - } - private var _energyUsage = 0.0 - private var _energyLastComputation = 0L - - /** - * The processing units of the machine. - */ - override val cpus: List = model.cpus.map { cpu -> - Cpu(FlowSink(engine, cpu.frequency, this@SimBareMetalMachine), cpu) - } - - /** - * The logic of the power driver. - */ - private val powerDriverLogic = powerDriver.createLogic(this, cpus) - - private var _lastConverge = Long.MAX_VALUE - - override fun onConverge(now: Long) { - // Update the PSU stage - psu.update() - - val lastConverge = _lastConverge - _lastConverge = now - val duration = max(0, now - lastConverge) - if (duration > 0) { - // Compute the power and energy usage of the machine - computeEnergyUsage(now) - } - - _powerUsage = powerDriverLogic.computePower() - } - - init { - psu.connect(powerDriverLogic) - _powerUsage = powerDriverLogic.computePower() - } - - /** - * Helper method to compute total energy usage. - */ - private fun computeEnergyUsage(now: Long) { - val duration = max(0, now - _energyLastComputation) - _energyLastComputation = now - - // Compute the energy usage of the machine - _energyUsage += _powerUsage * (duration / 1000.0) - } - - /** - * A [SimProcessingUnit] of a bare-metal machine. - */ - private class Cpu( - private val source: FlowSink, - override val model: ProcessingUnit - ) : SimProcessingUnit, FlowConsumer by source { - override var capacity: Double - get() = source.capacity - set(value) { - // Clamp the capacity of the CPU between [0.0, maxFreq] - source.capacity = value.coerceIn(0.0, model.frequency) - } - - override fun toString(): String = "SimBareMetalMachine.Cpu[model=$model]" - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachine.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachine.kt deleted file mode 100644 index 94581e89..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachine.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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 org.opendc.simulator.compute - -import org.opendc.simulator.compute.device.SimPeripheral -import org.opendc.simulator.compute.model.MachineModel -import org.opendc.simulator.compute.workload.SimWorkload - -/** - * A generic machine that is able to run a [SimWorkload]. - */ -public interface SimMachine { - /** - * The model of the machine containing its specifications. - */ - public val model: MachineModel - - /** - * The peripherals attached to the machine. - */ - public val peripherals: List - - /** - * Start the specified [SimWorkload] on this machine. - * - * @param workload The workload to start on the machine. - * @param meta The metadata to pass to the workload. - * @return A [SimMachineContext] that represents the execution context for the workload. - * @throws IllegalStateException if a workload is already active on the machine or if the machine is closed. - */ - public fun startWorkload(workload: SimWorkload, meta: Map = emptyMap()): SimMachineContext - - /** - * Cancel the workload that is currently running on this machine. - * - * If no workload is active, this operation is a no-op. - */ - public fun cancel() -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachineContext.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachineContext.kt deleted file mode 100644 index 5e3a7766..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachineContext.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 org.opendc.simulator.compute - -/** - * A simulated execution context in which a bootable image runs. This interface represents the - * firmware interface between the running image (e.g. operating system) and the physical or virtual firmware on - * which the image runs. - */ -public interface SimMachineContext : AutoCloseable { - /** - * The metadata associated with the context. - */ - public val meta: Map - - /** - * The CPUs available on the machine. - */ - public val cpus: List - - /** - * The memory interface of the machine. - */ - public val memory: SimMemory - - /** - * The network interfaces available to the workload. - */ - public val net: List - - /** - * The storage devices available to the workload. - */ - public val storage: List - - /** - * Stop the workload. - */ - public override fun close() -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMemory.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMemory.kt deleted file mode 100644 index b1aef495..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMemory.kt +++ /dev/null @@ -1,36 +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 - -import org.opendc.simulator.compute.model.MemoryUnit -import org.opendc.simulator.flow.FlowConsumer - -/** - * An interface to control the memory usage of simulated workloads. - */ -public interface SimMemory : FlowConsumer { - /** - * The models representing the static information of the memory units supporting this interface. - */ - public val models: List -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimNetworkInterface.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimNetworkInterface.kt deleted file mode 100644 index 660b2871..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimNetworkInterface.kt +++ /dev/null @@ -1,51 +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 - -import org.opendc.simulator.flow.FlowConsumer -import org.opendc.simulator.flow.FlowSource - -/** - * A firmware interface to a network adapter. - */ -public interface SimNetworkInterface { - /** - * The name of the network interface. - */ - public val name: String - - /** - * The unidirectional bandwidth of the network interface in Mbps. - */ - public val bandwidth: Double - - /** - * The resource provider for the transmit channel of the network interface. - */ - public val tx: FlowConsumer - - /** - * The resource consumer for the receive channel of the network interface. - */ - public val rx: FlowSource -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimProcessingUnit.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimProcessingUnit.kt deleted file mode 100644 index c9f36ece..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimProcessingUnit.kt +++ /dev/null @@ -1,41 +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 - -import org.opendc.simulator.compute.model.ProcessingUnit -import org.opendc.simulator.flow.FlowConsumer - -/** - * A simulated processing unit. - */ -public interface SimProcessingUnit : FlowConsumer { - /** - * The capacity of the processing unit, which can be adjusted by the workload if supported by the machine. - */ - public override var capacity: Double - - /** - * The model representing the static properties of the processing unit. - */ - public val model: ProcessingUnit -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimStorageInterface.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimStorageInterface.kt deleted file mode 100644 index 3d648671..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimStorageInterface.kt +++ /dev/null @@ -1,50 +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 - -import org.opendc.simulator.flow.FlowConsumer - -/** - * A firmware interface to a storage device. - */ -public interface SimStorageInterface { - /** - * The name of the storage device. - */ - public val name: String - - /** - * The capacity of the storage device in MBs. - */ - public val capacity: Double - - /** - * The resource provider for the read operations of the storage device. - */ - public val read: FlowConsumer - - /** - * The resource consumer for the write operation of the storage device. - */ - public val write: FlowConsumer -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimNetworkAdapter.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimNetworkAdapter.kt deleted file mode 100644 index dfb4ecf3..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimNetworkAdapter.kt +++ /dev/null @@ -1,36 +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.device - -import org.opendc.simulator.compute.SimMachine -import org.opendc.simulator.network.SimNetworkPort - -/** - * A simulated network interface card (NIC or network adapter) that can be attached to a [SimMachine]. - */ -public abstract class SimNetworkAdapter : SimNetworkPort(), SimPeripheral { - /** - * The unidirectional bandwidth of the network adapter in Mbps. - */ - public abstract val bandwidth: Double -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimPeripheral.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimPeripheral.kt deleted file mode 100644 index 268271be..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimPeripheral.kt +++ /dev/null @@ -1,33 +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.device - -import org.opendc.simulator.compute.SimMachine - -/** - * A component that can be attached to a [SimMachine]. - * - * This interface represents the physical view of the peripheral and should be used to configure the physical properties - * of the peripheral. - */ -public interface SimPeripheral diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimPsu.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimPsu.kt deleted file mode 100644 index 3d3703ae..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimPsu.kt +++ /dev/null @@ -1,114 +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.device - -import org.opendc.simulator.compute.power.PowerDriver -import org.opendc.simulator.flow.FlowConnection -import org.opendc.simulator.flow.FlowSource -import org.opendc.simulator.power.SimPowerInlet -import java.util.TreeMap - -/** - * A power supply of a [SimBareMetalMachine]. - * - * @param ratedOutputPower The rated output power of the PSU. - * @param energyEfficiency The energy efficiency of the PSU for various power draws. - */ -public class SimPsu( - private val ratedOutputPower: Double, - energyEfficiency: Map -) : SimPowerInlet() { - /** - * The power draw of the machine at this instant. - */ - public val powerDraw: Double - get() = _powerDraw - private var _powerDraw = 0.0 - - /** - * The energy efficiency of the PSU at various power draws. - */ - private val energyEfficiency = TreeMap(energyEfficiency) - - /** - * The consumer context. - */ - private var _ctx: FlowConnection? = null - - /** - * The driver that is connected to the PSU. - */ - private var _driver: PowerDriver.Logic? = null - - init { - require(energyEfficiency.isNotEmpty()) { "Must specify at least one entry for energy efficiency of PSU" } - } - - /** - * Update the power draw of the PSU. - */ - public fun update() { - _ctx?.pull() - } - - /** - * Connect the specified [PowerDriver.Logic] to this PSU. - */ - public fun connect(driver: PowerDriver.Logic) { - check(_driver == null) { "PSU already connected" } - _driver = driver - update() - } - - override fun createSource(): FlowSource = object : FlowSource { - override fun onStart(conn: FlowConnection, now: Long) { - _ctx = conn - conn.shouldSourceConverge = true - } - - override fun onStop(conn: FlowConnection, now: Long) { - _ctx = null - } - - override fun onPull(conn: FlowConnection, now: Long): Long { - val powerDraw = computePowerDraw(_driver?.computePower() ?: 0.0) - conn.push(powerDraw) - return Long.MAX_VALUE - } - - override fun onConverge(conn: FlowConnection, now: Long) { - _powerDraw = conn.rate - } - } - - /** - * Compute the power draw of the PSU including the power loss. - */ - private fun computePowerDraw(load: Double): Double { - val loadPercentage = (load / ratedOutputPower).coerceIn(0.0, 1.0) - val efficiency = energyEfficiency.ceilingEntry(loadPercentage)?.value ?: 1.0 - return load / efficiency - } - - override fun toString(): String = "SimPsu[draw=$_powerDraw]" -} 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 deleted file mode 100644 index e1486d71..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisor.kt +++ /dev/null @@ -1,442 +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 - -import org.opendc.simulator.compute.SimAbstractMachine -import org.opendc.simulator.compute.SimMachine -import org.opendc.simulator.compute.SimMachineContext -import org.opendc.simulator.compute.SimProcessingUnit -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.VmInterferenceMember -import org.opendc.simulator.compute.kernel.interference.VmInterferenceProfile -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.FlowConsumer -import org.opendc.simulator.flow.FlowConvergenceListener -import org.opendc.simulator.flow.FlowEngine -import org.opendc.simulator.flow.mux.FlowMultiplexer -import org.opendc.simulator.flow.mux.FlowMultiplexerFactory -import java.util.SplittableRandom -import kotlin.math.roundToLong - -/** - * A SimHypervisor facilitates the execution of multiple concurrent [SimWorkload]s, while acting as a single workload - * to another [SimMachine]. - * - * @param engine The [FlowEngine] to drive the simulation. - * @param muxFactory The factor for the [FlowMultiplexer] to multiplex the workloads. - * @param random A randomness generator for the interference calculations. - * @param scalingGovernor The scaling governor to use for scaling the CPU frequency of the underlying hardware. - * @param interferenceDomain The interference domain to which the hypervisor belongs. - */ -public class SimHypervisor( - private val engine: FlowEngine, - muxFactory: FlowMultiplexerFactory, - private val random: SplittableRandom, - private val scalingGovernor: ScalingGovernor? = null, - private val interferenceDomain: VmInterferenceDomain = VmInterferenceDomain() -) : SimWorkload, FlowConvergenceListener { - /** - * The [FlowMultiplexer] to multiplex the virtual machines. - */ - private val mux = muxFactory.newMultiplexer(engine, this) - - /** - * The virtual machines running on this hypervisor. - */ - private val _vms = mutableSetOf() - public val vms: Set - get() = _vms - - /** - * The resource counters associated with the hypervisor. - */ - public val counters: SimHypervisorCounters - get() = _counters - private val _counters = CountersImpl(this) - - /** - * The CPU capacity of the hypervisor in MHz. - */ - public val cpuCapacity: Double - get() = mux.capacity - - /** - * The CPU demand of the hypervisor in MHz. - */ - public val cpuDemand: Double - get() = mux.demand - - /** - * The CPU usage of the hypervisor in MHz. - */ - public val cpuUsage: Double - get() = mux.rate - - /** - * The machine on which the hypervisor runs. - */ - private lateinit var context: SimMachineContext - - /** - * The scaling governors attached to the physical CPUs backing this hypervisor. - */ - private val governors = mutableListOf() - - /* SimHypervisor */ - /** - * Create a [SimMachine] instance on which users may run a [SimWorkload]. - * - * @param model The machine to create. - */ - public fun newMachine(model: MachineModel): SimVirtualMachine { - require(canFit(model)) { "Machine does not fit" } - val vm = VirtualMachine(model) - _vms.add(vm) - return vm - } - - /** - * Remove the specified [machine] from the hypervisor. - * - * @param machine The machine to remove. - */ - public fun removeMachine(machine: SimVirtualMachine) { - if (_vms.remove(machine)) { - // This cast must always succeed, since `_vms` only contains `VirtualMachine` types. - (machine as VirtualMachine).close() - } - } - - /** - * Determine whether the specified machine characterized by [model] can fit on this hypervisor at this moment. - */ - public fun canFit(model: MachineModel): Boolean { - return (mux.maxInputs - mux.inputs.size) >= model.cpus.size - } - - /* SimWorkload */ - override fun onStart(ctx: SimMachineContext) { - context = ctx - - _cpuCount = ctx.cpus.size - _cpuCapacity = ctx.cpus.sumOf { it.model.frequency } - _counters.d = _cpuCount / _cpuCapacity * 1000L - - // Clear the existing outputs of the multiplexer - mux.clearOutputs() - - for (cpu in ctx.cpus) { - val governor = scalingGovernor?.createLogic(ScalingPolicyImpl(cpu)) - if (governor != null) { - governors.add(governor) - governor.onStart() - } - - cpu.startConsumer(mux.newOutput()) - } - } - - override fun onStop(ctx: SimMachineContext) {} - - private var _cpuCount = 0 - private var _cpuCapacity = 0.0 - private var _lastConverge = engine.clock.millis() - - /* FlowConvergenceListener */ - override fun onConverge(now: Long) { - val lastConverge = _lastConverge - _lastConverge = now - val delta = now - lastConverge - - 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(random, load) - } - } - - val load = cpuDemand / cpuCapacity - for (governor in governors) { - governor.onLimit(load) - } - } - - /** - * A virtual machine running on the hypervisor. - * - * @param model The machine model of the virtual machine. - */ - private inner class VirtualMachine(model: MachineModel) : SimAbstractMachine(engine, model), SimVirtualMachine, AutoCloseable { - /** - * A flag to indicate that the machine is closed. - */ - private var isClosed = false - - /** - * The vCPUs of the machine. - */ - 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 - - @JvmField val _counters = VmCountersImpl(cpus, null) - - /** - * The CPU capacity of the hypervisor in MHz. - */ - override val cpuCapacity: Double - get() = cpus.sumOf(FlowConsumer::capacity) - - /** - * The CPU demand of the hypervisor in MHz. - */ - override val cpuDemand: Double - get() = cpus.sumOf(FlowConsumer::demand) - - /** - * The CPU usage of the hypervisor in MHz. - */ - override val cpuUsage: Double - get() = cpus.sumOf(FlowConsumer::rate) - - override fun startWorkload(workload: SimWorkload, meta: Map): SimMachineContext { - check(!isClosed) { "Machine is closed" } - - val profile = meta["interference-profile"] as? VmInterferenceProfile - val interferenceMember = if (profile != null) interferenceDomain.join(profile) else null - - val counters = _counters - counters.member = interferenceMember - - return super.startWorkload( - object : SimWorkload { - override fun onStart(ctx: SimMachineContext) { - try { - interferenceMember?.activate() - workload.onStart(ctx) - } catch (cause: Throwable) { - interferenceMember?.deactivate() - throw cause - } - } - - override fun onStop(ctx: SimMachineContext) { - interferenceMember?.deactivate() - counters.member = null - workload.onStop(ctx) - } - }, - meta - ) - } - - override fun close() { - if (isClosed) { - return - } - - isClosed = true - cancel() - - for (cpu in cpus) { - cpu.close() - } - } - } - - /** - * A [SimProcessingUnit] of a virtual machine. - */ - private class VCpu( - private val switch: FlowMultiplexer, - private val source: FlowConsumer, - override val model: ProcessingUnit - ) : SimProcessingUnit, FlowConsumer by source { - override var capacity: Double - get() = source.capacity - set(_) = TODO("Capacity changes on vCPU not supported") - - override fun toString(): String = "SimAbstractHypervisor.VCpu[model=$model]" - - /** - * Close the CPU - */ - fun close() { - switch.removeInput(source) - } - - fun flush() { - switch.flushCounters(source) - } - } - - /** - * A [ScalingPolicy] for a physical CPU of the hypervisor. - */ - private class ScalingPolicyImpl(override val cpu: SimProcessingUnit) : ScalingPolicy { - override var target: Double - get() = cpu.capacity - set(value) { - cpu.capacity = value - } - - override val max: Double = cpu.model.frequency - - override val min: Double = 0.0 - } - - /** - * Implementation of [SimHypervisorCounters]. - */ - private class CountersImpl(private val hv: SimHypervisor) : SimHypervisorCounters { - @JvmField var d = 1.0 // Number of CPUs divided by total CPU capacity - - override val cpuActiveTime: Long - get() = _cpuTime[0] - override val cpuIdleTime: Long - get() = _cpuTime[1] - override val cpuStealTime: Long - get() = _cpuTime[2] - override val cpuLostTime: Long - get() = _cpuTime[3] - - val _cpuTime = LongArray(4) - private val _previous = DoubleArray(3) - - /** - * Record the CPU time of the hypervisor. - */ - fun record() { - val cpuTime = _cpuTime - val previous = _previous - val counters = hv.mux.counters - - val demand = counters.demand - val actual = counters.actual - val 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 - - cpuTime[0] += (actualDelta * d).roundToLong() - cpuTime[1] += (remainingDelta * d).roundToLong() - cpuTime[2] += ((demandDelta - actualDelta) * d).roundToLong() - } - - override fun flush() { - hv.mux.flushCounters() - record() - } - } - - /** - * A [SimHypervisorCounters] implementation for a virtual machine. - */ - private inner class VmCountersImpl( - private val cpus: List, - @JvmField var member: VmInterferenceMember? - ) : SimHypervisorCounters { - private val d = cpus.size / cpus.sumOf { it.model.frequency } * 1000 - - override val cpuActiveTime: Long - get() = _cpuTime[0] - override val cpuIdleTime: Long - get() = _cpuTime[1] - override val cpuStealTime: Long - get() = _cpuTime[2] - override val cpuLostTime: Long - get() = _cpuTime[3] - - private val _cpuTime = LongArray(4) - private val _previous = DoubleArray(3) - - /** - * Record the CPU time of the hypervisor. - */ - fun record(random: SplittableRandom, load: Double) { - 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 member = member - if (member != null) { - val penalty = 1 - member.apply(random, load) - val interference = (actualDelta * d * penalty).roundToLong() - - if (interference > 0) { - cpuTime[3] += interference - _counters._cpuTime[3] += interference - } - } - } - - override fun flush() { - for (cpu in cpus) { - cpu.flush() - } - } - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisorCounters.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisorCounters.kt deleted file mode 100644 index 63fee507..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisorCounters.kt +++ /dev/null @@ -1,53 +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 - -/** - * Performance counters of a [SimHypervisor]. - */ -public interface SimHypervisorCounters { - /** - * The amount of time (in milliseconds) the CPUs of the hypervisor were actively running. - */ - public val cpuActiveTime: Long - - /** - * The amount of time (in milliseconds) the CPUs of the hypervisor were idle. - */ - public val cpuIdleTime: Long - - /** - * The amount of CPU time (in milliseconds) that virtual machines were ready to run, but were not able to. - */ - public val cpuStealTime: Long - - /** - * The amount of CPU time (in milliseconds) that was lost due to interference between virtual machines. - */ - public val cpuLostTime: Long - - /** - * Flush the counter values. - */ - public fun flush() -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimVirtualMachine.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimVirtualMachine.kt deleted file mode 100644 index 36219ef2..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimVirtualMachine.kt +++ /dev/null @@ -1,50 +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 - -import org.opendc.simulator.compute.SimMachine - -/** - * A virtual [SimMachine] running on top of another [SimMachine]. - */ -public interface SimVirtualMachine : SimMachine { - /** - * The resource counters associated with the virtual machine. - */ - public val counters: SimHypervisorCounters - - /** - * The CPU usage of the VM in MHz. - */ - public val cpuUsage: Double - - /** - * The CPU usage of the VM in MHz. - */ - public val cpuDemand: Double - - /** - * The CPU capacity of the VM in MHz. - */ - public val cpuCapacity: Double -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernor.kt deleted file mode 100644 index 1a03221d..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernor.kt +++ /dev/null @@ -1,66 +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.cpufreq - -/** - * A CPUFreq [ScalingGovernor] that models the conservative scaling governor in the Linux kernel. - */ -public class ConservativeScalingGovernor(public val threshold: Double = 0.8, private val stepSize: Double = -1.0) : - ScalingGovernor { - override fun createLogic(policy: ScalingPolicy): ScalingGovernor.Logic = object : ScalingGovernor.Logic { - /** - * The step size to use. - */ - private val stepSize = if (this@ConservativeScalingGovernor.stepSize < 0) { - // https://github.com/torvalds/linux/blob/master/drivers/cpufreq/cpufreq_conservative.c#L33 - policy.max * 0.05 - } else { - this@ConservativeScalingGovernor.stepSize.coerceAtMost(policy.max) - } - - /** - * The previous load of the CPU. - */ - private var previousLoad = threshold - - override fun onStart() { - policy.target = policy.min - } - - override fun onLimit(load: Double) { - val currentTarget = policy.target - if (load > threshold) { - // Check for load increase (see: https://github.com/torvalds/linux/blob/master/drivers/cpufreq/cpufreq_conservative.c#L102) - val step = when { - load > previousLoad -> stepSize - load < previousLoad -> -stepSize - else -> 0.0 - } - policy.target = (currentTarget + step).coerceIn(policy.min, policy.max) - } - previousLoad = load - } - } - - override fun toString(): String = "ConservativeScalingGovernor[threshold=$threshold,stepSize=$stepSize]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernor.kt deleted file mode 100644 index aef15ce9..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernor.kt +++ /dev/null @@ -1,50 +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.cpufreq - -/** - * A CPUFreq [ScalingGovernor] that models the on-demand scaling governor in the Linux kernel. - */ -public class OnDemandScalingGovernor(public val threshold: Double = 0.8) : ScalingGovernor { - override fun createLogic(policy: ScalingPolicy): ScalingGovernor.Logic = object : ScalingGovernor.Logic { - /** - * The multiplier used for the linear frequency scaling. - */ - private val multiplier = (policy.max - policy.min) / 100 - - override fun onStart() { - policy.target = policy.min - } - - override fun onLimit(load: Double) { - policy.target = if (load < threshold) { - /* Proportional scaling (see: https://github.com/torvalds/linux/blob/master/drivers/cpufreq/cpufreq_ondemand.c#L151). */ - policy.min + load * multiplier - } else { - policy.max - } - } - } - - override fun toString(): String = "OnDemandScalingGovernor[threshold=$threshold]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernor.kt deleted file mode 100644 index 13109a9a..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernor.kt +++ /dev/null @@ -1,36 +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.cpufreq - -/** - * A CPUFreq [ScalingGovernor] that causes the highest possible frequency to be requested from the resource. - */ -public class PerformanceScalingGovernor : ScalingGovernor { - override fun createLogic(policy: ScalingPolicy): ScalingGovernor.Logic = object : ScalingGovernor.Logic { - override fun onStart() { - policy.target = policy.max - } - } - - override fun toString(): String = "PerformanceScalingGovernor" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernor.kt deleted file mode 100644 index 32c0703a..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernor.kt +++ /dev/null @@ -1,36 +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.cpufreq - -/** - * A CPUFreq [ScalingGovernor] that causes the lowest possible frequency to be requested from the resource. - */ -public class PowerSaveScalingGovernor : ScalingGovernor { - override fun createLogic(policy: ScalingPolicy): ScalingGovernor.Logic = object : ScalingGovernor.Logic { - override fun onStart() { - policy.target = policy.min - } - } - - override fun toString(): String = "PowerSaveScalingGovernor" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernor.kt deleted file mode 100644 index d33827db..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernor.kt +++ /dev/null @@ -1,56 +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.cpufreq - -/** - * A [ScalingGovernor] in the CPUFreq subsystem of OpenDC is responsible for scaling the frequency of simulated CPUs - * independent of the particular implementation of the CPU. - * - * Each of the scaling governors implements a single, possibly parametrized, performance scaling algorithm. - * - * For more information, see the documentation of the Linux CPUFreq subsystem: - * https://www.kernel.org/doc/html/latest/admin-guide/pm/cpufreq.html - */ -public interface ScalingGovernor { - /** - * Create the scaling logic for the specified [policy] - */ - public fun createLogic(policy: ScalingPolicy): Logic - - /** - * The logic of the scaling governor. - */ - public interface Logic { - /** - * This method is invoked when the governor is started. - */ - public fun onStart() {} - - /** - * This method is invoked when the governor should re-decide the frequency limits. - * - * @param load The load of the system. - */ - public fun onLimit(load: Double) {} - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ScalingPolicy.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ScalingPolicy.kt deleted file mode 100644 index f9351896..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ScalingPolicy.kt +++ /dev/null @@ -1,51 +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.cpufreq - -import org.opendc.simulator.compute.SimProcessingUnit - -/** - * An interface that holds the state managed by a [ScalingGovernor] and used by the underlying machine to control the - * CPU frequencies. - */ -public interface ScalingPolicy { - /** - * The processing unit that is associated with this policy. - */ - public val cpu: SimProcessingUnit - - /** - * The target frequency which the CPU should attempt to attain. - */ - public var target: Double - - /** - * The minimum frequency to which the CPU may scale. - */ - public val min: Double - - /** - * The maximum frequency to which the CPU may scale. - */ - public val max: Double -} 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 deleted file mode 100644 index 6861823b..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.kt +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2022 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 - -import java.util.ArrayDeque -import java.util.ArrayList -import java.util.WeakHashMap - -/** - * A domain where virtual machines may incur performance variability due to operating on the same resource and - * therefore causing interference. - */ -public class VmInterferenceDomain { - /** - * A cache to maintain a mapping between the active profiles in this domain. - */ - private val cache = WeakHashMap() - - /** - * The set of members active in this domain. - */ - private val activeKeys = ArrayList() - - /** - * Queue of participants that will be removed or added to the active groups. - */ - private val participants = ArrayDeque() - - /** - * Join this interference domain with the specified [profile] and return the [VmInterferenceMember] associated with - * the profile. If the member does not exist, it will be created. - */ - public fun join(profile: VmInterferenceProfile): VmInterferenceMember { - return cache.computeIfAbsent(profile) { key -> key.newMember(this) } - } - - /** - * Mark the specified [member] as active in this interference domain. - */ - internal fun activate(member: VmInterferenceMember) { - val activeKeys = activeKeys - val pos = activeKeys.binarySearch(member) - if (pos < 0) { - activeKeys.add(-pos - 1, member) - } - - computeActiveGroups(activeKeys, member) - } - - /** - * Mark the specified [member] as inactive in this interference domain. - */ - internal fun deactivate(member: VmInterferenceMember) { - val activeKeys = activeKeys - activeKeys.remove(member) - computeActiveGroups(activeKeys, member) - } - - /** - * (Re-)compute the active groups. - */ - private fun computeActiveGroups(activeKeys: ArrayList, member: VmInterferenceMember) { - if (activeKeys.isEmpty()) { - return - } - - val groups = member.membership - val members = member.members - val participants = participants - - for (group in groups) { - val groupMembers = members[group] - - var i = 0 - var j = 0 - var intersection = 0 - - // Compute the intersection of the group members and the current active members - while (i < groupMembers.size && j < activeKeys.size) { - val l = groupMembers[i] - val rightEntry = activeKeys[j] - val r = rightEntry.id - - if (l < r) { - i++ - } else if (l > r) { - j++ - } else { - if (++intersection > 1) { - rightEntry.addGroup(group) - } else { - participants.add(rightEntry) - } - - i++ - j++ - } - } - - while (true) { - val participant = participants.poll() ?: break - - if (intersection <= 1) { - participant.removeGroup(group) - } else { - participant.addGroup(group) - } - } - } - } -} 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 deleted file mode 100644 index 4b56a058..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceMember.kt +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (c) 2022 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 - -import java.util.SplittableRandom - -/** - * A participant of an interference domain. - */ -public class VmInterferenceMember( - private val domain: VmInterferenceDomain, - private val model: VmInterferenceModel, - @JvmField internal val id: Int, - @JvmField internal val membership: IntArray, - @JvmField internal val members: Array, - private val targets: DoubleArray, - private val scores: DoubleArray -) : Comparable { - /** - * The active groups to which the key belongs. - */ - private var groups: IntArray = IntArray(2) - private var groupsSize: Int = 0 - - /** - * The number of users of the interference key. - */ - private var refCount: Int = 0 - - /** - * Mark this member as active in this interference domain. - */ - public fun activate() { - if (refCount++ <= 0) { - domain.activate(this) - } - } - - /** - * Mark this member as inactive in this interference domain. - */ - public fun deactivate() { - if (--refCount <= 0) { - domain.deactivate(this) - } - } - - /** - * 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(random: SplittableRandom, load: Double): Double { - val groupsSize = groupsSize - - if (groupsSize == 0) { - return 1.0 - } - - val groups = groups - val targets = targets - - var low = 0 - var high = groupsSize - 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 - } - } - - /** - * Add an active group to this member. - */ - internal fun addGroup(group: Int) { - var groups = groups - val groupsSize = groupsSize - val pos = groups.binarySearch(group, toIndex = groupsSize) - - if (pos >= 0) { - return - } - - val idx = -pos - 1 - - if (groups.size == groupsSize) { - val newSize = groupsSize + (groupsSize shr 1) - groups = groups.copyOf(newSize) - this.groups = groups - } - - groups.copyInto(groups, idx + 1, idx, groupsSize) - groups[idx] = group - this.groupsSize += 1 - } - - /** - * Remove an active group from this member. - */ - internal fun removeGroup(group: Int) { - val groups = groups - val groupsSize = groupsSize - val pos = groups.binarySearch(group, toIndex = groupsSize) - - if (pos < 0) { - return - } - - groups.copyInto(groups, pos, pos + 1, groupsSize) - this.groupsSize -= 1 - } - - override fun compareTo(other: VmInterferenceMember): Int { - val cmp = model.hashCode().compareTo(other.model.hashCode()) - if (cmp != 0) { - return cmp - } - - return id.compareTo(other.id) - } -} 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 deleted file mode 100644 index 238bffc0..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.kt +++ /dev/null @@ -1,191 +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 - -import java.util.TreeMap -import java.util.TreeSet - -/** - * An interference model that models the resource interference between virtual machines on a host. - * - * @param members The target load of each group. - * @param scores The performance score of each group. - * @param members The members belonging to each group. - * @param membership The identifier of each key. - * @param size The number of groups. - */ -public class VmInterferenceModel private constructor( - private val idMapping: Map, - private val members: Array, - private val membership: Array, - private val targets: DoubleArray, - private val scores: DoubleArray, - private val size: Int -) { - /** - * Return the [VmInterferenceProfile] associated with the specified [id]. - * - * @param id The identifier of the virtual machine. - * @return A [VmInterferenceProfile] representing the virtual machine as part of interference model or `null` if - * there is no profile for the virtual machine. - */ - public fun getProfile(id: String): VmInterferenceProfile? { - val intId = idMapping[id] ?: return null - return VmInterferenceProfile(this, intId, membership[intId], members, targets, scores) - } - - public companion object { - /** - * Construct a [Builder] instance. - */ - @JvmStatic - public fun builder(): Builder = Builder() - } - - /** - * Builder class for a [VmInterferenceModel] - */ - public class Builder internal constructor() { - /** - * The target load of each group. - */ - private var _targets = DoubleArray(INITIAL_CAPACITY) { Double.POSITIVE_INFINITY } - - /** - * The performance score of each group. - */ - private var _scores = DoubleArray(INITIAL_CAPACITY) { Double.POSITIVE_INFINITY } - - /** - * The members of each group. - */ - private var _members = ArrayList>(INITIAL_CAPACITY) - - /** - * The mapping from member to group id. - */ - private val ids = TreeSet() - - /** - * The number of groups in the model. - */ - private var size = 0 - - /** - * Add the specified group to the model. - */ - public fun addGroup(members: Set, targetLoad: Double, score: Double): Builder { - val size = size - - if (size == _targets.size) { - grow() - } - - _targets[size] = targetLoad - _scores[size] = score - _members.add(members) - ids.addAll(members) - - this.size++ - - return this - } - - /** - * Build the [VmInterferenceModel]. - */ - public fun build(): VmInterferenceModel { - val size = size - val targets = _targets - val scores = _scores - val members = _members - - val indices = IntArray(size) { it } - indices.sortedWith( - Comparator { l, r -> - var cmp = targets[l].compareTo(targets[r]) // Order by target load - if (cmp != 0) { - return@Comparator cmp - } - - cmp = scores[l].compareTo(scores[r]) // Higher penalty first (this means lower performance score first) - if (cmp != 0) { - cmp - } else { - l.compareTo(r) - } - } - ) - - val newTargets = DoubleArray(size) - val newScores = DoubleArray(size) - val newMembers = arrayOfNulls(size) - - var nextId = 0 - val idMapping = ids.associateWith { nextId++ } - val membership = ids.associateWithTo(TreeMap()) { ArrayList() } - - for ((group, j) in indices.withIndex()) { - newTargets[group] = targets[j] - newScores[group] = scores[j] - val groupMembers = members[j] - val newGroupMembers = groupMembers.map { idMapping.getValue(it) }.toIntArray() - - newGroupMembers.sort() - newMembers[group] = newGroupMembers - - for (member in groupMembers) { - membership.getValue(member).add(group) - } - } - - @Suppress("UNCHECKED_CAST") - return VmInterferenceModel( - idMapping, - newMembers as Array, - membership.map { it.value.toIntArray() }.toTypedArray(), - newTargets, - newScores, - size - ) - } - - /** - * Helper function to grow the capacity of the internal arrays. - */ - private fun grow() { - val oldSize = _targets.size - val newSize = oldSize + (oldSize shr 1) - - _targets = _targets.copyOf(newSize) - _scores = _scores.copyOf(newSize) - } - - private companion object { - /** - * The initial capacity of the builder. - */ - const val INITIAL_CAPACITY = 256 - } - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceProfile.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceProfile.kt deleted file mode 100644 index 004dbd07..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceProfile.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2022 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 profile of a particular virtual machine describing its interference pattern with other virtual machines. - * - * @param model The model to which this profile belongs. - * @property id The identifier of the profile inside the model. - * @property membership The membership of the profile in the groups. - * @param members The members in the model. - * @param targets The targets in the model. - * @param scores The scores in the model. - */ -public class VmInterferenceProfile internal constructor( - private val model: VmInterferenceModel, - private val id: Int, - private val membership: IntArray, - private val members: Array, - private val targets: DoubleArray, - private val scores: DoubleArray -) { - /** - * Create a new [VmInterferenceMember] based on this profile for the specified [domain]. - */ - internal fun newMember(domain: VmInterferenceDomain): VmInterferenceMember { - return VmInterferenceMember(domain, model, id, membership, members, targets, scores) - } - - override fun toString(): String = "VmInterferenceProfile[id=$id]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/MachineModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/MachineModel.kt deleted file mode 100644 index 22dcaef4..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/MachineModel.kt +++ /dev/null @@ -1,54 +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.model - -/** - * A description of the physical or virtual machine on which a bootable image runs. - * - * @property cpus The list of processing units available to the image. - * @property memory The list of memory units available to the image. - * @property net A list of network adapters available to the machine. - * @property storage A list of storage devices available to the machine. - */ -public data class MachineModel( - public val cpus: List, - public val memory: List, - public val net: List = emptyList(), - public val storage: List = emptyList() -) { - /** - * Optimize the [MachineModel] by merging all resources of the same type into a single resource with the combined - * capacity. Such configurations can be simulated more efficiently by OpenDC. - */ - public fun optimize(): MachineModel { - val originalCpu = cpus[0] - val freq = cpus.sumOf { it.frequency } - val processingNode = originalCpu.node.copy(coreCount = 1) - val processingUnits = listOf(originalCpu.copy(frequency = freq, node = processingNode)) - - val memorySize = memory.sumOf { it.size } - val memoryUnits = listOf(MemoryUnit("Generic", "Generic", 3200.0, memorySize)) - - return MachineModel(processingUnits, memoryUnits) - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/MemoryUnit.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/MemoryUnit.kt deleted file mode 100644 index bcbde5b1..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/MemoryUnit.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 org.opendc.simulator.compute.model - -/** - * A memory unit of a compute resource, either virtual or physical. - * - * @property vendor The vendor string of the memory. - * @property modelName The name of the memory model. - * @property speed The access speed of the memory in MHz. - * @property size The size of the memory unit in MBs. - */ -public data class MemoryUnit( - public val vendor: String, - public val modelName: String, - public val speed: Double, - public val size: Long -) diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/NetworkAdapter.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/NetworkAdapter.kt deleted file mode 100644 index 46472144..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/NetworkAdapter.kt +++ /dev/null @@ -1,36 +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.model - -/** - * A description of a network adapter that is - * - * @property vendor The vendor of the network adapter. - * @property modelName The model name of the network adapter. - * @property bandwidth The bandwidth of the network adapter in Mbps. - */ -public data class NetworkAdapter( - public val vendor: String, - public val modelName: String, - public val bandwidth: Double -) diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingNode.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingNode.kt deleted file mode 100644 index 58ed816c..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingNode.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 org.opendc.simulator.compute.model - -/** - * A processing node/package/socket containing possibly several CPU cores. - * - * @property vendor The vendor string of the processor node. - * @property modelName The name of the processor node. - * @property arch The micro-architecture of the processor node. - * @property coreCount The number of logical CPUs in the processor node. - */ -public data class ProcessingNode( - public val vendor: String, - public val arch: String, - public val modelName: String, - public val coreCount: Int -) diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingUnit.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingUnit.kt deleted file mode 100644 index 415e95e6..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingUnit.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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 org.opendc.simulator.compute.model - -/** - * A single logical compute unit of processor node, either virtual or physical. - * - * @property node The processing node containing the CPU core. - * @property id The identifier of the CPU core within the processing node. - * @property frequency The clock rate of the CPU in MHz. - */ -public data class ProcessingUnit( - public val node: ProcessingNode, - public val id: Int, - public val frequency: Double -) diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/StorageDevice.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/StorageDevice.kt deleted file mode 100644 index 2621ad6d..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/StorageDevice.kt +++ /dev/null @@ -1,40 +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.model - -/** - * Model for a physical storage device attached to a machine. - * - * @property vendor The vendor of the storage device. - * @property modelName The model name of the device. - * @property capacity The capacity of the device. - * @property readBandwidth The read bandwidth of the device in MBps. - * @property writeBandwidth The write bandwidth of the device in MBps. - */ -public data class StorageDevice( - public val vendor: String, - public val modelName: String, - public val capacity: Double, - public val readBandwidth: Double, - public val writeBandwidth: Double -) diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/AsymptoticPowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/AsymptoticPowerModel.kt deleted file mode 100644 index 46c397fe..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/AsymptoticPowerModel.kt +++ /dev/null @@ -1,54 +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.power - -import kotlin.math.E -import kotlin.math.pow - -/** - * The asymptotic power model partially adapted from GreenCloud. - * - * @param maxPower The maximum power draw of the server in W. - * @param idlePower The power draw of the server at its lowest utilization level in W. - * @param asymUtil A utilization level at which the server attains asymptotic, - * i.e., close to linear power consumption versus the offered load. - * For most of the CPUs,a is in [0.2, 0.5]. - * @param isDvfsEnabled A flag indicates whether DVFS is enabled. - */ -public class AsymptoticPowerModel( - private val maxPower: Double, - private val idlePower: Double, - private val asymUtil: Double, - private val isDvfsEnabled: Boolean -) : PowerModel { - private val factor: Double = (maxPower - idlePower) / 100 - - public override fun computePower(utilization: Double): Double = - if (isDvfsEnabled) { - idlePower + (factor * 100) / 2 * (1 + utilization.pow(3) - E.pow(-utilization.pow(3) / asymUtil)) - } else { - idlePower + (factor * 100) / 2 * (1 + utilization - E.pow(-utilization / asymUtil)) - } - - override fun toString(): String = "AsymptoticPowerModel[max=$maxPower,idle=$idlePower,asymptotic=$asymUtil]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ConstantPowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ConstantPowerModel.kt deleted file mode 100644 index 0fe32b0d..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ConstantPowerModel.kt +++ /dev/null @@ -1,32 +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.power - -/** - * A power model which produces a constant value [power]. - */ -public class ConstantPowerModel(private val power: Double) : PowerModel { - public override fun computePower(utilization: Double): Double = power - - override fun toString(): String = "ConstantPowerModel[power=$power]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/CubicPowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/CubicPowerModel.kt deleted file mode 100644 index 0d3bf6cc..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/CubicPowerModel.kt +++ /dev/null @@ -1,41 +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.power - -import kotlin.math.pow - -/** - * The cubic power model partially adapted from CloudSim. - * - * @param maxPower The maximum power draw of the server in W. - * @param idlePower The power draw of the server at its lowest utilization level in W. - */ -public class CubicPowerModel(private val maxPower: Double, private val idlePower: Double) : PowerModel { - private val factor: Double = (maxPower - idlePower) / 100.0.pow(3) - - public override fun computePower(utilization: Double): Double { - return idlePower + factor * (utilization * 100).pow(3) - } - - override fun toString(): String = "CubicPowerModel[max=$maxPower,idle=$idlePower]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/InterpolationPowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/InterpolationPowerModel.kt deleted file mode 100644 index b17b87a9..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/InterpolationPowerModel.kt +++ /dev/null @@ -1,64 +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.power - -import kotlin.math.ceil -import kotlin.math.floor -import kotlin.math.max -import kotlin.math.min - -/** - * The linear interpolation power model partially adapted from CloudSim. - * This model is developed to adopt the SPECpower benchmark. - * - * @param powerValues A [List] of average active power measured by the power analyzer(s) and accumulated by the - * PTDaemon (Power and Temperature Daemon) for this measurement interval, displayed as watts (W). - * @see Machines used in the SPEC benchmark - */ -public class InterpolationPowerModel(private val powerValues: List) : PowerModel { - public override fun computePower(utilization: Double): Double { - val clampedUtilization = min(1.0, max(0.0, utilization)) - val utilizationFlr = floor(clampedUtilization * 10).toInt() - val utilizationCil = ceil(clampedUtilization * 10).toInt() - val powerFlr: Double = getAveragePowerValue(utilizationFlr) - val powerCil: Double = getAveragePowerValue(utilizationCil) - val delta = (powerCil - powerFlr) / 10 - - return if (utilization % 0.1 == 0.0) { - getAveragePowerValue((clampedUtilization * 10).toInt()) - } else { - powerFlr + delta * (clampedUtilization - utilizationFlr.toDouble() / 10) * 100 - } - } - - override fun toString(): String = "InterpolationPowerModel[entries=${powerValues.size}]" - - /** - * Gets the power consumption for a given utilization percentage. - * - * @param index the utilization percentage in the scale from [0 to 10], - * where 10 means 100% of utilization. - * @return the power consumption for the given utilization percentage - */ - private fun getAveragePowerValue(index: Int): Double = powerValues[index] -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/LinearPowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/LinearPowerModel.kt deleted file mode 100644 index dadc56ec..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/LinearPowerModel.kt +++ /dev/null @@ -1,42 +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.power - -/** - * The linear power model partially adapted from CloudSim. - * - * @param maxPower The maximum power draw of the server in W. - * @param idlePower The power draw of the server at its lowest utilization level in W. - */ -public class LinearPowerModel(private val maxPower: Double, private val idlePower: Double) : PowerModel { - /** - * The linear interpolation factor of the model. - */ - private val factor: Double = (maxPower - idlePower) / 100 - - public override fun computePower(utilization: Double): Double { - return idlePower + factor * utilization * 100 - } - - override fun toString(): String = "LinearPowerModel[max=$maxPower,idle=$idlePower]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/MsePowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/MsePowerModel.kt deleted file mode 100644 index e9e72da8..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/MsePowerModel.kt +++ /dev/null @@ -1,49 +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.power - -import kotlin.math.pow - -/** - * The power model that minimizes the mean squared error (MSE) - * to the actual power measurement by tuning the calibration parameter. - * @see - * Fan et al., Power provisioning for a warehouse-sized computer, ACM SIGARCH'07 - * - * @param maxPower The maximum power draw of the server in W. - * @param idlePower The power draw of the server at its lowest utilization level in W. - * @param calibrationParam The parameter set to minimize the MSE. - */ -public class MsePowerModel( - private val maxPower: Double, - private val idlePower: Double, - private val calibrationParam: Double -) : PowerModel { - private val factor: Double = (maxPower - idlePower) / 100 - - public override fun computePower(utilization: Double): Double { - return idlePower + factor * (2 * utilization - utilization.pow(calibrationParam)) * 100 - } - - override fun toString(): String = "MsePowerModel[max=$maxPower,idle=$idlePower,MSE_param=$calibrationParam]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PStatePowerDriver.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PStatePowerDriver.kt deleted file mode 100644 index ce7225d2..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PStatePowerDriver.kt +++ /dev/null @@ -1,60 +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.power - -import org.opendc.simulator.compute.SimMachine -import org.opendc.simulator.compute.SimProcessingUnit -import java.util.TreeMap -import kotlin.math.max -import kotlin.math.min - -/** - * A [PowerDriver] that computes the power draw using multiple [PowerModel]s based on multiple frequency states. - * - * @param states A map describing the states of the driver. - */ -public class PStatePowerDriver(states: Map) : PowerDriver { - /** - * The P-States defined by the user and ordered by key. - */ - private val states: TreeMap = TreeMap(states) - - override fun createLogic(machine: SimMachine, cpus: List): PowerDriver.Logic = object : PowerDriver.Logic { - override fun computePower(): Double { - var targetFreq = 0.0 - var totalSpeed = 0.0 - - for (cpu in cpus) { - targetFreq = max(cpu.capacity, targetFreq) - totalSpeed += cpu.rate - } - - val maxFreq = states.lastKey() - val (actualFreq, model) = states.ceilingEntry(min(maxFreq, targetFreq)) - val utilization = totalSpeed / (actualFreq * cpus.size) - return model.computePower(utilization) - } - } - - override fun toString(): String = "PStatePowerDriver[states=$states]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerDriver.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerDriver.kt deleted file mode 100644 index 1a46dd4a..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerDriver.kt +++ /dev/null @@ -1,48 +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.power - -import org.opendc.simulator.compute.SimMachine -import org.opendc.simulator.compute.SimProcessingUnit - -/** - * A [PowerDriver] is responsible for tracking the power usage for a component of the machine. - */ -public interface PowerDriver { - /** - * Create the driver logic for the specified [machine]. - */ - public fun createLogic(machine: SimMachine, cpus: List): Logic - - /** - * The logic of the power driver. - */ - public interface Logic { - /** - * Compute the power consumption of the component. - * - * @return The power consumption of the component in W. - */ - public fun computePower(): Double - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerModel.kt deleted file mode 100644 index decb2420..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerModel.kt +++ /dev/null @@ -1,38 +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.power - -import org.opendc.simulator.compute.SimMachine - -/** - * A model for estimating the power usage of a [SimMachine]. - */ -public interface PowerModel { - /** - * Computes CPU power consumption for each host. - * - * @param utilization The CPU utilization percentage. - * @return A [Double] value of CPU power consumption. - */ - public fun computePower(utilization: Double): Double -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SimplePowerDriver.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SimplePowerDriver.kt deleted file mode 100644 index 34e91c35..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SimplePowerDriver.kt +++ /dev/null @@ -1,48 +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.power - -import org.opendc.simulator.compute.SimMachine -import org.opendc.simulator.compute.SimProcessingUnit - -/** - * A [PowerDriver] that computes the power consumption based on a single specified [power model][model]. - */ -public class SimplePowerDriver(private val model: PowerModel) : PowerDriver { - override fun createLogic(machine: SimMachine, cpus: List): PowerDriver.Logic = object : PowerDriver.Logic { - - override fun computePower(): Double { - var targetFreq = 0.0 - var totalSpeed = 0.0 - - for (cpu in cpus) { - targetFreq += cpu.capacity - totalSpeed += cpu.rate - } - - return model.computePower(totalSpeed / targetFreq) - } - } - - override fun toString(): String = "SimplePowerDriver[model=$model]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SqrtPowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SqrtPowerModel.kt deleted file mode 100644 index 0665dbd9..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SqrtPowerModel.kt +++ /dev/null @@ -1,41 +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.power - -import kotlin.math.sqrt - -/** - * The square root power model partially adapted from CloudSim. - * - * @param maxPower The maximum power draw of the server in W. - * @param idlePower The power draw of the server at its lowest utilization level in W. - */ -public class SqrtPowerModel(private val maxPower: Double, private val idlePower: Double) : PowerModel { - private val factor: Double = (maxPower - idlePower) / sqrt(100.0) - - override fun computePower(utilization: Double): Double { - return idlePower + factor * sqrt(utilization * 100) - } - - override fun toString(): String = "SqrtPowerModel[max=$maxPower,idle=$idlePower]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SquarePowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SquarePowerModel.kt deleted file mode 100644 index e4ae88a9..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SquarePowerModel.kt +++ /dev/null @@ -1,41 +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.power - -import kotlin.math.pow - -/** - * The square power model partially adapted from CloudSim. - * - * @param maxPower The maximum power draw of the server in W. - * @param idlePower The power draw of the server at its lowest utilization level in W. - */ -public class SquarePowerModel(private val maxPower: Double, private val idlePower: Double) : PowerModel { - private val factor: Double = (maxPower - idlePower) / 100.0.pow(2) - - override fun computePower(utilization: Double): Double { - return idlePower + factor * (utilization * 100).pow(2) - } - - override fun toString(): String = "SquarePowerModel[max=$maxPower,idle=$idlePower]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ZeroIdlePowerDecorator.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ZeroIdlePowerDecorator.kt deleted file mode 100644 index 05ab4631..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ZeroIdlePowerDecorator.kt +++ /dev/null @@ -1,40 +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.power - -/** - * A decorator for ignoring the idle power when computing energy consumption of components. - * - * @param delegate The [PowerModel] to delegate to. - */ -public class ZeroIdlePowerDecorator(private val delegate: PowerModel) : PowerModel { - override fun computePower(utilization: Double): Double { - return if (utilization == 0.0) { - 0.0 - } else { - delegate.computePower(utilization) - } - } - - override fun toString(): String = "ZeroIdlePowerDecorator[delegate=$delegate]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkload.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkload.kt deleted file mode 100644 index 726d1f56..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkload.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 org.opendc.simulator.compute.workload - -import org.opendc.simulator.compute.SimMachineContext -import org.opendc.simulator.flow.source.FixedFlowSource - -/** - * A [SimWorkload] that models applications as a static number of floating point operations ([flops]) executed on - * multiple cores of a compute resource. - * - * @property flops The number of floating point operations to perform for this task in MFLOPs. - * @property utilization A model of the CPU utilization of the application. - */ -public class SimFlopsWorkload( - public val flops: Long, - public val utilization: Double = 0.8 -) : SimWorkload { - init { - require(flops >= 0) { "Number of FLOPs must be positive" } - require(utilization > 0.0 && utilization <= 1.0) { "Utilization must be in (0, 1]" } - } - - override fun onStart(ctx: SimMachineContext) { - val lifecycle = SimWorkloadLifecycle(ctx) - for (cpu in ctx.cpus) { - cpu.startConsumer(lifecycle.waitFor(FixedFlowSource(flops.toDouble() / ctx.cpus.size, utilization))) - } - } - - override fun onStop(ctx: SimMachineContext) {} - - override fun toString(): String = "SimFlopsWorkload(FLOPs=$flops,utilization=$utilization)" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimRuntimeWorkload.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimRuntimeWorkload.kt deleted file mode 100644 index 8a3f5f84..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimRuntimeWorkload.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 org.opendc.simulator.compute.workload - -import org.opendc.simulator.compute.SimMachineContext -import org.opendc.simulator.flow.source.FixedFlowSource - -/** - * A [SimWorkload] that models application execution as a single duration. - * - * @property duration The duration of the workload. - * @property utilization The utilization of the application during runtime. - */ -public class SimRuntimeWorkload( - public val duration: Long, - public val utilization: Double = 0.8 -) : SimWorkload { - init { - require(duration >= 0) { "Duration must be non-negative" } - require(utilization > 0.0 && utilization <= 1.0) { "Utilization must be in (0, 1]" } - } - - override fun onStart(ctx: SimMachineContext) { - val lifecycle = SimWorkloadLifecycle(ctx) - for (cpu in ctx.cpus) { - val limit = cpu.capacity * utilization - cpu.startConsumer(lifecycle.waitFor(FixedFlowSource((limit / 1000) * duration, utilization))) - } - } - - override fun onStop(ctx: SimMachineContext) {} - - override fun toString(): String = "SimRuntimeWorkload(duration=$duration,utilization=$utilization)" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTrace.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTrace.kt deleted file mode 100644 index db6a4629..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTrace.kt +++ /dev/null @@ -1,218 +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.workload - -import org.opendc.simulator.compute.model.ProcessingUnit -import org.opendc.simulator.flow.FlowConnection -import org.opendc.simulator.flow.FlowSource -import kotlin.math.min - -/** - * A workload trace that describes the resource utilization over time in a collection of [SimTraceFragment]s. - * - * @param usageCol The column containing the CPU usage of each fragment (in MHz). - * @param deadlineCol The column containing the ending timestamp for each fragment (in epoch millis). - * @param coresCol The column containing the utilized cores. - * @param size The number of fragments in the trace. - */ -public class SimTrace( - private val usageCol: DoubleArray, - private val deadlineCol: LongArray, - private val coresCol: IntArray, - private val size: Int -) { - init { - require(size >= 0) { "Invalid trace size" } - require(usageCol.size >= size) { "Invalid number of usage entries" } - require(deadlineCol.size >= size) { "Invalid number of deadline entries" } - require(coresCol.size >= size) { "Invalid number of core entries" } - } - - public companion object { - /** - * Construct a [SimTrace] with the specified fragments. - */ - @JvmStatic - public fun ofFragments(fragments: List): SimTrace { - val size = fragments.size - val usageCol = DoubleArray(size) - val deadlineCol = LongArray(size) - val coresCol = IntArray(size) - - for (i in fragments.indices) { - val fragment = fragments[i] - usageCol[i] = fragment.usage - deadlineCol[i] = fragment.timestamp + fragment.duration - coresCol[i] = fragment.cores - } - - return SimTrace(usageCol, deadlineCol, coresCol, size) - } - - /** - * Construct a [SimTrace] with the specified fragments. - */ - @JvmStatic - public fun ofFragments(vararg fragments: SimTraceFragment): SimTrace { - val size = fragments.size - val usageCol = DoubleArray(size) - val deadlineCol = LongArray(size) - val coresCol = IntArray(size) - - for (i in fragments.indices) { - val fragment = fragments[i] - usageCol[i] = fragment.usage - deadlineCol[i] = fragment.timestamp + fragment.duration - coresCol[i] = fragment.cores - } - - return SimTrace(usageCol, deadlineCol, coresCol, size) - } - - /** - * Create a [SimTrace.Builder] instance. - */ - @JvmStatic - public fun builder(): Builder = Builder() - } - - /** - * Construct a new [FlowSource] for the specified [cpu]. - * - * @param cpu The [ProcessingUnit] for which to create the source. - * @param offset The time offset to use for the trace. - */ - public fun newSource(cpu: ProcessingUnit, offset: Long): FlowSource { - return CpuConsumer(cpu, offset, usageCol, deadlineCol, coresCol, size) - } - - /** - * A builder class for a [SimTrace]. - */ - public class Builder internal constructor() { - /** - * The columns of the trace. - */ - private var usageCol: DoubleArray = DoubleArray(16) - private var deadlineCol: LongArray = LongArray(16) - private var coresCol: IntArray = IntArray(16) - - /** - * The number of entries in the trace. - */ - private var size = 0 - - /** - * Add the specified [SimTraceFragment] to the trace. - */ - public fun add(fragment: SimTraceFragment) { - add(fragment.timestamp + fragment.duration, fragment.usage, fragment.cores) - } - - /** - * Add a fragment to the trace. - * - * @param deadline Timestamp at which the fragment ends (in epoch millis). - * @param usage CPU usage of this fragment. - * @param cores Number of cores used. - */ - public fun add(deadline: Long, usage: Double, cores: Int) { - val size = size - - if (size == usageCol.size) { - grow() - } - - deadlineCol[size] = deadline - usageCol[size] = usage - coresCol[size] = cores - - this.size++ - } - - /** - * Helper function to grow the capacity of the column arrays. - */ - private fun grow() { - val arraySize = usageCol.size - val newSize = arraySize + (arraySize shr 1) - - usageCol = usageCol.copyOf(newSize) - deadlineCol = deadlineCol.copyOf(newSize) - coresCol = coresCol.copyOf(newSize) - } - - /** - * Construct the immutable [SimTrace]. - */ - public fun build(): SimTrace { - return SimTrace(usageCol, deadlineCol, coresCol, size) - } - } - - /** - * A CPU consumer for the trace workload. - */ - private class CpuConsumer( - cpu: ProcessingUnit, - private val offset: Long, - private val usageCol: DoubleArray, - private val deadlineCol: LongArray, - private val coresCol: IntArray, - private val size: Int - ) : FlowSource { - private val id = cpu.id - private val coreCount = cpu.node.coreCount - - /** - * The index in the trace. - */ - private var _idx = 0 - - override fun onPull(conn: FlowConnection, now: Long): Long { - val size = size - val nowOffset = now - offset - - var idx = _idx - val deadlines = deadlineCol - var deadline = deadlines[idx] - - while (deadline <= nowOffset && ++idx < size) { - deadline = deadlines[idx] - } - - if (idx >= size) { - conn.close() - return Long.MAX_VALUE - } - - _idx = idx - - val cores = min(coreCount, coresCol[idx]) - val usage = usageCol[idx] - - conn.push(if (id < cores) usage / cores else 0.0) - return deadline - nowOffset - } - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceFragment.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceFragment.kt deleted file mode 100644 index 5285847f..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceFragment.kt +++ /dev/null @@ -1,38 +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.workload - -/** - * A fragment of the workload trace. - * - * @param timestamp The timestamp at which the fragment starts (in epoch millis). - * @param duration The duration of the fragment (in milliseconds). - * @param usage The CPU usage during the fragment (in MHz). - * @param cores The amount of cores utilized during the fragment. - */ -public data class SimTraceFragment( - @JvmField val timestamp: Long, - @JvmField val duration: Long, - @JvmField val usage: Double, - @JvmField val cores: Int -) diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt deleted file mode 100644 index ce04a790..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 org.opendc.simulator.compute.workload - -import org.opendc.simulator.compute.SimMachineContext - -/** - * A [SimWorkload] that replays a workload trace consisting of multiple fragments, each indicating the resource - * consumption for some period of time. - * - * @param trace The trace of fragments to use. - * @param offset The offset for the timestamps. - */ -public class SimTraceWorkload(private val trace: SimTrace, private val offset: Long = 0L) : SimWorkload { - override fun onStart(ctx: SimMachineContext) { - val lifecycle = SimWorkloadLifecycle(ctx) - - for (cpu in ctx.cpus) { - cpu.startConsumer(lifecycle.waitFor(trace.newSource(cpu.model, offset))) - } - } - - override fun onStop(ctx: SimMachineContext) {} - - override fun toString(): String = "SimTraceWorkload" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkload.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkload.kt deleted file mode 100644 index 61c6e2ad..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkload.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 org.opendc.simulator.compute.workload - -import org.opendc.simulator.compute.SimMachineContext - -/** - * A model that characterizes the runtime behavior of some particular workload. - * - * Workloads are stateful objects that may be paused and resumed at a later moment. As such, be careful when using the - * same [SimWorkload] from multiple contexts. - */ -public interface SimWorkload { - /** - * This method is invoked when the workload is started. - * - * @param ctx The execution context in which the machine runs. - */ - public fun onStart(ctx: SimMachineContext) - - /** - * This method is invoked when the workload is stopped. - * - * @param ctx The execution context in which the machine runs. - */ - public fun onStop(ctx: SimMachineContext) -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkloadLifecycle.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkloadLifecycle.kt deleted file mode 100644 index 46113bb0..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkloadLifecycle.kt +++ /dev/null @@ -1,82 +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.workload - -import org.opendc.simulator.compute.SimMachineContext -import org.opendc.simulator.flow.FlowConnection -import org.opendc.simulator.flow.FlowSource - -/** - * A helper class to manage the lifecycle of a [SimWorkload] - */ -public class SimWorkloadLifecycle(private val ctx: SimMachineContext) { - /** - * The resource consumers which represent the lifecycle of the workload. - */ - private val waiting = HashSet() - - /** - * Wait for the specified [source] to complete before ending the lifecycle of the workload. - */ - public fun waitFor(source: FlowSource): FlowSource { - val wrapper = Wrapper(source) - waiting.add(wrapper) - return wrapper - } - - /** - * Complete the specified [Wrapper]. - */ - private fun complete(wrapper: Wrapper) { - if (waiting.remove(wrapper) && waiting.isEmpty()) { - ctx.close() - } - } - - /** - * A [FlowSource] that wraps [delegate] and informs [SimWorkloadLifecycle] that is has completed. - */ - private inner class Wrapper(private val delegate: FlowSource) : FlowSource { - override fun onStart(conn: FlowConnection, now: Long) { - delegate.onStart(conn, now) - } - - override fun onPull(conn: FlowConnection, now: Long): Long { - return delegate.onPull(conn, now) - } - - override fun onConverge(conn: FlowConnection, now: Long) { - delegate.onConverge(conn, now) - } - - override fun onStop(conn: FlowConnection, now: Long) { - try { - delegate.onStop(conn, now) - } finally { - complete(this) - } - } - - override fun toString(): String = "SimWorkloadLifecycle.Wrapper[delegate=$delegate]" - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt index 1eddf82c..f0aae15b 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt @@ -26,6 +26,7 @@ import kotlinx.coroutines.CancellationException import kotlinx.coroutines.cancel import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch +import kotlinx.coroutines.yield import org.junit.jupiter.api.Assertions.assertAll import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach @@ -38,17 +39,16 @@ import org.opendc.simulator.compute.model.NetworkAdapter import org.opendc.simulator.compute.model.ProcessingNode import org.opendc.simulator.compute.model.ProcessingUnit import org.opendc.simulator.compute.model.StorageDevice -import org.opendc.simulator.compute.power.ConstantPowerModel -import org.opendc.simulator.compute.power.LinearPowerModel -import org.opendc.simulator.compute.power.SimplePowerDriver +import org.opendc.simulator.compute.power.CpuPowerModels import org.opendc.simulator.compute.workload.SimFlopsWorkload +import org.opendc.simulator.compute.workload.SimTrace import org.opendc.simulator.compute.workload.SimWorkload -import org.opendc.simulator.compute.workload.SimWorkloadLifecycle -import org.opendc.simulator.flow.FlowEngine -import org.opendc.simulator.flow.source.FixedFlowSource +import org.opendc.simulator.flow2.FlowEngine +import org.opendc.simulator.flow2.source.SimpleFlowSource import org.opendc.simulator.kotlin.runSimulation import org.opendc.simulator.network.SimNetworkSink import org.opendc.simulator.power.SimPowerSource +import java.util.concurrent.ThreadLocalRandom /** * Test suite for the [SimBareMetalMachine] class. @@ -61,41 +61,69 @@ class SimMachineTest { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) machineModel = MachineModel( - cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, - memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }, - net = listOf(NetworkAdapter("Mellanox", "ConnectX-5", 25000.0)), - storage = listOf(StorageDevice("Samsung", "EVO", 1000.0, 250.0, 250.0)) + /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, + /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }, + /*net*/ listOf(NetworkAdapter("Mellanox", "ConnectX-5", 25000.0)), + /*storage*/ listOf(StorageDevice("Samsung", "EVO", 1000.0, 250.0, 250.0)) ) } @Test fun testFlopsWorkload() = runSimulation { - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) - machine.runWorkload(SimFlopsWorkload(2_000, utilization = 1.0)) + machine.runWorkload(SimFlopsWorkload(2_000, /*utilization*/ 1.0)) // Two cores execute 1000 MFlOps per second (1000 ms) assertEquals(1000, clock.millis()) } + @Test + fun testTraceWorkload() = runSimulation { + val random = ThreadLocalRandom.current() + val builder = SimTrace.builder() + repeat(1000000) { + val timestamp = it.toLong() * 1000 + val deadline = timestamp + 1000 + builder.add(deadline, random.nextDouble(0.0, 4500.0), 1) + } + val trace = builder.build() + + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + val machine = SimBareMetalMachine.create( + graph, + machineModel + ) + + machine.runWorkload(trace.createWorkload(0)) + + // Two cores execute 1000 MFlOps per second (1000 ms) + assertEquals(1000000000, clock.millis()) + } + @Test fun testDualSocketMachine() = runSimulation { + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + val cpuNode = machineModel.cpus[0].node val machineModel = MachineModel( - cpus = List(cpuNode.coreCount * 2) { ProcessingUnit(cpuNode, it % 2, 1000.0) }, - memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + /*cpus*/ List(cpuNode.coreCount * 2) { ProcessingUnit(cpuNode, it % 2, 1000.0) }, + /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } ) - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val machine = SimBareMetalMachine.create( + graph, + machineModel ) - machine.runWorkload(SimFlopsWorkload(2_000, utilization = 1.0)) + machine.runWorkload(SimFlopsWorkload(2_000, /*utilization*/ 1.0)) // Two sockets with two cores execute 2000 MFlOps per second (500 ms) assertEquals(500, clock.millis()) @@ -103,42 +131,47 @@ class SimMachineTest { @Test fun testPower() = runSimulation { - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine( - engine, + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + val machine = SimBareMetalMachine.create( + graph, machineModel, - SimplePowerDriver(LinearPowerModel(100.0, 50.0)) + SimPsuFactories.simple(CpuPowerModels.linear(100.0, 50.0)) ) - val source = SimPowerSource(engine, capacity = 1000.0) + val source = SimPowerSource(graph, /*capacity*/ 1000.0f) source.connect(machine.psu) coroutineScope { - launch { machine.runWorkload(SimFlopsWorkload(2_000, utilization = 1.0)) } + launch { machine.runWorkload(SimFlopsWorkload(2_000, /*utilization*/ 1.0)) } + + yield() assertAll( - { assertEquals(100.0, machine.psu.powerDraw) }, - { assertEquals(100.0, source.powerDraw) } + { assertEquals(100.0, machine.psu.powerUsage) }, + { assertEquals(100.0f, source.powerDraw) } ) } } @Test fun testCapacityClamp() = runSimulation { - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) machine.runWorkload(object : SimWorkload { override fun onStart(ctx: SimMachineContext) { val cpu = ctx.cpus[0] - cpu.capacity = cpu.model.frequency + 1000.0 - assertEquals(cpu.model.frequency, cpu.capacity) - cpu.capacity = -1.0 - assertEquals(0.0, cpu.capacity) + cpu.frequency = (cpu.model.frequency + 1000.0) + assertEquals(cpu.model.frequency, cpu.frequency) + cpu.frequency = -1.0 + assertEquals(0.0, cpu.frequency) - ctx.close() + ctx.shutdown() } override fun onStop(ctx: SimMachineContext) {} @@ -147,16 +180,18 @@ class SimMachineTest { @Test fun testMemory() = runSimulation { - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) machine.runWorkload(object : SimWorkload { override fun onStart(ctx: SimMachineContext) { assertEquals(32_000 * 4.0, ctx.memory.capacity) - ctx.close() + ctx.shutdown() } override fun onStop(ctx: SimMachineContext) {} @@ -165,104 +200,111 @@ class SimMachineTest { @Test fun testMemoryUsage() = runSimulation { - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) machine.runWorkload(object : SimWorkload { override fun onStart(ctx: SimMachineContext) { - val lifecycle = SimWorkloadLifecycle(ctx) - ctx.memory.startConsumer(lifecycle.waitFor(FixedFlowSource(ctx.memory.capacity, utilization = 0.8))) + val source = SimpleFlowSource(ctx.graph, ctx.memory.capacity.toFloat(), 1.0f) { ctx.shutdown() } + ctx.graph.connect(source.output, ctx.memory.input) } override fun onStop(ctx: SimMachineContext) {} }) - assertEquals(1250, clock.millis()) + assertEquals(1000, clock.millis()) } @Test fun testNetUsage() = runSimulation { - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine( - engine, - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) val adapter = (machine.peripherals[0] as SimNetworkAdapter) - adapter.connect(SimNetworkSink(engine, adapter.bandwidth)) + adapter.connect(SimNetworkSink(graph, adapter.bandwidth.toFloat())) machine.runWorkload(object : SimWorkload { override fun onStart(ctx: SimMachineContext) { - val lifecycle = SimWorkloadLifecycle(ctx) - val iface = ctx.net[0] - iface.tx.startConsumer(lifecycle.waitFor(FixedFlowSource(iface.bandwidth, utilization = 0.8))) + val iface = ctx.networkInterfaces[0] + val source = SimpleFlowSource(ctx.graph, 800.0f, 0.8f) { ctx.shutdown(); it.close(); } + ctx.graph.connect(source.output, iface.tx) } override fun onStop(ctx: SimMachineContext) {} }) - assertEquals(1250, clock.millis()) + assertEquals(40, clock.millis()) } @Test fun testDiskReadUsage() = runSimulation { - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine( - engine, - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) machine.runWorkload(object : SimWorkload { override fun onStart(ctx: SimMachineContext) { - val lifecycle = SimWorkloadLifecycle(ctx) - val disk = ctx.storage[0] - disk.read.startConsumer(lifecycle.waitFor(FixedFlowSource(disk.read.capacity, utilization = 0.8))) + val disk = ctx.storageInterfaces[0] + val source = SimpleFlowSource(ctx.graph, 800.0f, 0.8f) { ctx.shutdown() } + ctx.graph.connect(source.output, disk.read) } override fun onStop(ctx: SimMachineContext) {} }) - assertEquals(1250, clock.millis()) + assertEquals(4000, clock.millis()) } @Test fun testDiskWriteUsage() = runSimulation { - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine( - engine, - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) machine.runWorkload(object : SimWorkload { override fun onStart(ctx: SimMachineContext) { - val lifecycle = SimWorkloadLifecycle(ctx) - val disk = ctx.storage[0] - disk.write.startConsumer(lifecycle.waitFor(FixedFlowSource(disk.write.capacity, utilization = 0.8))) + val disk = ctx.storageInterfaces[0] + val source = SimpleFlowSource(ctx.graph, 800.0f, 0.8f) { ctx.shutdown() } + ctx.graph.connect(source.output, disk.write) } override fun onStop(ctx: SimMachineContext) {} }) - assertEquals(1250, clock.millis()) + assertEquals(4000, clock.millis()) } @Test fun testCancellation() = runSimulation { - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) try { coroutineScope { - launch { machine.runWorkload(SimFlopsWorkload(2_000, utilization = 1.0)) } + launch { machine.runWorkload(SimFlopsWorkload(2_000, /*utilization*/ 1.0)) } cancel() } } catch (_: CancellationException) { @@ -274,19 +316,21 @@ class SimMachineTest { @Test fun testConcurrentRuns() = runSimulation { - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) coroutineScope { launch { - machine.runWorkload(SimFlopsWorkload(2_000, utilization = 1.0)) + machine.runWorkload(SimFlopsWorkload(2_000, /*utilization*/ 1.0)) } assertThrows { - machine.runWorkload(SimFlopsWorkload(2_000, utilization = 1.0)) + machine.runWorkload(SimFlopsWorkload(2_000, /*utilization*/ 1.0)) } } } diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/device/SimPsuTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/device/SimPsuTest.kt deleted file mode 100644 index 0a6cb29f..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/device/SimPsuTest.kt +++ /dev/null @@ -1,102 +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.device - -import io.mockk.every -import io.mockk.mockk -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.opendc.simulator.compute.power.PowerDriver -import org.opendc.simulator.flow.FlowEngine -import org.opendc.simulator.kotlin.runSimulation -import org.opendc.simulator.power.SimPowerSource - -/** - * Test suite for [SimPsu] - */ -internal class SimPsuTest { - - @Test - fun testInvalidInput() { - assertThrows { SimPsu(1.0, emptyMap()) } - } - - @Test - fun testDoubleConnect() { - val psu = SimPsu(1.0, mapOf(0.0 to 1.0)) - val cpuLogic = mockk() - psu.connect(cpuLogic) - assertThrows { psu.connect(mockk()) } - } - - @Test - fun testPsuIdle() = runSimulation { - val ratedOutputPower = 240.0 - val energyEfficiency = mapOf(0.0 to 1.0) - - val engine = FlowEngine(coroutineContext, clock) - val source = SimPowerSource(engine, capacity = ratedOutputPower) - - val cpuLogic = mockk() - every { cpuLogic.computePower() } returns 0.0 - - val psu = SimPsu(ratedOutputPower, energyEfficiency) - psu.connect(cpuLogic) - source.connect(psu) - - assertEquals(0.0, source.powerDraw, 0.01) - } - - @Test - fun testPsuPowerLoss() = runSimulation { - val ratedOutputPower = 240.0 - // Efficiency of 80 Plus Titanium PSU - val energyEfficiency = sortedMapOf( - 0.3 to 0.9, - 0.7 to 0.92, - 1.0 to 0.94 - ) - - val engine = FlowEngine(coroutineContext, clock) - val source = SimPowerSource(engine, capacity = ratedOutputPower) - - val cpuLogic = mockk() - every { cpuLogic.computePower() } returnsMany listOf(50.0, 100.0, 150.0, 200.0) - - val psu = SimPsu(ratedOutputPower, energyEfficiency) - psu.connect(cpuLogic) - source.connect(psu) - - assertEquals(55.55, source.powerDraw, 0.01) - - psu.update() - assertEquals(108.695, source.powerDraw, 0.01) - - psu.update() - assertEquals(163.043, source.powerDraw, 0.01) - - psu.update() - assertEquals(212.765, source.powerDraw, 0.01) - } -} 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 6b498119..79669d40 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 @@ -31,20 +31,17 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertAll import org.junit.jupiter.api.assertDoesNotThrow import org.opendc.simulator.compute.SimBareMetalMachine -import org.opendc.simulator.compute.kernel.cpufreq.PerformanceScalingGovernor +import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernors import org.opendc.simulator.compute.kernel.interference.VmInterferenceModel import org.opendc.simulator.compute.model.MachineModel import org.opendc.simulator.compute.model.MemoryUnit import org.opendc.simulator.compute.model.ProcessingNode import org.opendc.simulator.compute.model.ProcessingUnit -import org.opendc.simulator.compute.power.ConstantPowerModel -import org.opendc.simulator.compute.power.SimplePowerDriver import org.opendc.simulator.compute.runWorkload import org.opendc.simulator.compute.workload.SimTrace import org.opendc.simulator.compute.workload.SimTraceFragment -import org.opendc.simulator.compute.workload.SimTraceWorkload -import org.opendc.simulator.flow.FlowEngine -import org.opendc.simulator.flow.mux.FlowMultiplexerFactory +import org.opendc.simulator.flow2.FlowEngine +import org.opendc.simulator.flow2.mux.FlowMultiplexerFactory import org.opendc.simulator.kotlin.runSimulation import java.util.SplittableRandom @@ -58,8 +55,8 @@ internal class SimFairShareHypervisorTest { fun setUp() { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 1) model = MachineModel( - cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, - memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, + /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } ) } @@ -70,23 +67,20 @@ internal class SimFairShareHypervisorTest { fun testOvercommittedSingle() = runSimulation { val duration = 5 * 60L val workloadA = - SimTraceWorkload( - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 28.0, 1), - SimTraceFragment(duration * 1000, duration * 1000, 3500.0, 1), - SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), - SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1) - ) - ) - - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, model, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1), PerformanceScalingGovernor()) + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 28.0, 1), + SimTraceFragment(duration * 1000, duration * 1000, 3500.0, 1), + SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), + SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1) + ).createWorkload(0) - launch { - machine.runWorkload(hypervisor) - println("Hypervisor finished") - } + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, model) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(0L), ScalingGovernors.performance()) + + launch { machine.runWorkload(hypervisor) } yield() val vm = hypervisor.newMachine(model) @@ -110,31 +104,27 @@ internal class SimFairShareHypervisorTest { fun testOvercommittedDual() = runSimulation { val duration = 5 * 60L val workloadA = - SimTraceWorkload( - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 28.0, 1), - SimTraceFragment(duration * 1000, duration * 1000, 3500.0, 1), - SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), - SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1) - ) - ) + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 28.0, 1), + SimTraceFragment(duration * 1000, duration * 1000, 3500.0, 1), + SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), + SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1) + ).createWorkload(0) val workloadB = - SimTraceWorkload( - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 28.0, 1), - SimTraceFragment(duration * 1000, duration * 1000, 3100.0, 1), - SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), - SimTraceFragment(duration * 3000, duration * 1000, 73.0, 1) - ) - ) - - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, model, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1), null) + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 28.0, 1), + SimTraceFragment(duration * 1000, duration * 1000, 3100.0, 1), + SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), + SimTraceFragment(duration * 3000, duration * 1000, 73.0, 1) + ).createWorkload(0) - launch { - machine.runWorkload(hypervisor) - } + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, model) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(0L), ScalingGovernors.performance()) + + launch { machine.runWorkload(hypervisor) } yield() coroutineScope { @@ -163,18 +153,18 @@ internal class SimFairShareHypervisorTest { fun testMultipleCPUs() = runSimulation { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) val model = MachineModel( - cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, - memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, + /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } ) - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, model, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1), null) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, model) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(0L), ScalingGovernors.performance()) assertDoesNotThrow { - launch { - machine.runWorkload(hypervisor) - } + launch { machine.runWorkload(hypervisor) } } machine.cancel() @@ -184,39 +174,37 @@ internal class SimFairShareHypervisorTest { fun testInterference() = runSimulation { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) val model = MachineModel( - cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, - memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, + /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } ) val interferenceModel = VmInterferenceModel.builder() - .addGroup(targetLoad = 0.0, score = 0.9, members = setOf("a", "b")) - .addGroup(targetLoad = 0.0, score = 0.6, members = setOf("a", "c")) - .addGroup(targetLoad = 0.1, score = 0.8, members = setOf("a", "n")) + .addGroup(setOf("a", "b"), 0.0, 0.9) + .addGroup(setOf("a", "c"), 0.0, 0.6) + .addGroup(setOf("a", "n"), 0.1, 0.8) .build() - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, model, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1), null) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, model) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(0L)) val duration = 5 * 60L val workloadA = - SimTraceWorkload( - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 0.0, 1), - SimTraceFragment(duration * 1000, duration * 1000, 28.0, 1), - SimTraceFragment(duration * 2000, duration * 1000, 3500.0, 1), - SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1) - ) - ) + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 0.0, 1), + SimTraceFragment(duration * 1000, duration * 1000, 28.0, 1), + SimTraceFragment(duration * 2000, duration * 1000, 3500.0, 1), + SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1) + ).createWorkload(0) val workloadB = - SimTraceWorkload( - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 0.0, 1), - SimTraceFragment(duration * 1000, duration * 1000, 28.0, 1), - SimTraceFragment(duration * 2000, duration * 1000, 3100.0, 1), - SimTraceFragment(duration * 3000, duration * 1000, 73.0, 1) - ) - ) + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 0.0, 1), + SimTraceFragment(duration * 1000, duration * 1000, 28.0, 1), + SimTraceFragment(duration * 2000, duration * 1000, 3100.0, 1), + SimTraceFragment(duration * 3000, duration * 1000, 73.0, 1) + ).createWorkload(0) launch { machine.runWorkload(hypervisor) 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 57fe3766..ba5a5c68 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 @@ -37,16 +37,13 @@ import org.opendc.simulator.compute.model.MachineModel import org.opendc.simulator.compute.model.MemoryUnit import org.opendc.simulator.compute.model.ProcessingNode import org.opendc.simulator.compute.model.ProcessingUnit -import org.opendc.simulator.compute.power.ConstantPowerModel -import org.opendc.simulator.compute.power.SimplePowerDriver import org.opendc.simulator.compute.runWorkload import org.opendc.simulator.compute.workload.SimFlopsWorkload import org.opendc.simulator.compute.workload.SimRuntimeWorkload import org.opendc.simulator.compute.workload.SimTrace import org.opendc.simulator.compute.workload.SimTraceFragment -import org.opendc.simulator.compute.workload.SimTraceWorkload -import org.opendc.simulator.flow.FlowEngine -import org.opendc.simulator.flow.mux.FlowMultiplexerFactory +import org.opendc.simulator.flow2.FlowEngine +import org.opendc.simulator.flow2.mux.FlowMultiplexerFactory import org.opendc.simulator.kotlin.runSimulation import java.util.SplittableRandom @@ -60,8 +57,8 @@ internal class SimSpaceSharedHypervisorTest { fun setUp() { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 1) machineModel = MachineModel( - cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, - memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, + /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } ) } @@ -72,18 +69,18 @@ internal class SimSpaceSharedHypervisorTest { fun testTrace() = runSimulation { val duration = 5 * 60L val workloadA = - SimTraceWorkload( - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 28.0, 1), - SimTraceFragment(duration * 1000, duration * 1000, 3500.0, 1), - SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), - SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1) - ) - ) - - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(1), null) + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 28.0, 1), + SimTraceFragment(duration * 1000, duration * 1000, 3500.0, 1), + SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), + SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1) + ).createWorkload(0) + + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) launch { machine.runWorkload(hypervisor) } val vm = hypervisor.newMachine(machineModel) @@ -102,10 +99,12 @@ internal class SimSpaceSharedHypervisorTest { @Test fun testRuntimeWorkload() = runSimulation { val duration = 5 * 60L * 1000 - val workload = SimRuntimeWorkload(duration) - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(1), null) + val workload = SimRuntimeWorkload(duration, 1.0) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) launch { machine.runWorkload(hypervisor) } yield() @@ -125,9 +124,11 @@ internal class SimSpaceSharedHypervisorTest { fun testFlopsWorkload() = runSimulation { val duration = 5 * 60L * 1000 val workload = SimFlopsWorkload((duration * 3.2).toLong(), 1.0) - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(1), null) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) launch { machine.runWorkload(hypervisor) } yield() @@ -144,21 +145,23 @@ internal class SimSpaceSharedHypervisorTest { @Test fun testTwoWorkloads() = runSimulation { val duration = 5 * 60L * 1000 - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(1), null) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) launch { machine.runWorkload(hypervisor) } yield() val vm = hypervisor.newMachine(machineModel) - vm.runWorkload(SimRuntimeWorkload(duration)) + vm.runWorkload(SimRuntimeWorkload(duration, 1.0)) hypervisor.removeMachine(vm) yield() val vm2 = hypervisor.newMachine(machineModel) - vm2.runWorkload(SimRuntimeWorkload(duration)) + vm2.runWorkload(SimRuntimeWorkload(duration, 1.0)) hypervisor.removeMachine(vm2) machine.cancel() @@ -171,14 +174,18 @@ internal class SimSpaceSharedHypervisorTest { */ @Test fun testConcurrentWorkloadFails() = runSimulation { - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(1), null) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) launch { machine.runWorkload(hypervisor) } yield() - hypervisor.newMachine(machineModel) + val vm = hypervisor.newMachine(machineModel) + launch { vm.runWorkload(SimFlopsWorkload(10_000, 1.0)) } + yield() assertAll( { assertFalse(hypervisor.canFit(machineModel)) }, @@ -186,6 +193,7 @@ internal class SimSpaceSharedHypervisorTest { ) machine.cancel() + vm.cancel() } /** @@ -193,9 +201,11 @@ internal class SimSpaceSharedHypervisorTest { */ @Test fun testConcurrentWorkloadSucceeds() = runSimulation { - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(1), null) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) launch { machine.runWorkload(hypervisor) } yield() diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernorTest.kt index ef354569..6b182f4c 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernorTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernorTest.kt @@ -25,11 +25,10 @@ package org.opendc.simulator.compute.kernel.cpufreq import io.mockk.every import io.mockk.mockk import io.mockk.verify -import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test /** - * Test suite for the [ConservativeScalingGovernor] + * Test suite for the conservative [ScalingGovernor]. */ internal class ConservativeScalingGovernorTest { @Test @@ -38,7 +37,7 @@ internal class ConservativeScalingGovernorTest { val minSpeed = cpuCapacity / 2 val defaultThreshold = 0.8 val defaultStepSize = 0.05 * cpuCapacity - val governor = ConservativeScalingGovernor() + val governor = ScalingGovernors.conservative(defaultThreshold) val policy = mockk(relaxUnitFun = true) every { policy.max } returns cpuCapacity @@ -48,10 +47,8 @@ internal class ConservativeScalingGovernorTest { every { policy.target } answers { target } every { policy.target = any() } propertyType Double::class answers { target = value } - val logic = governor.createLogic(policy) + val logic = governor.newGovernor(policy) logic.onStart() - assertEquals(defaultThreshold, governor.threshold) - logic.onLimit(0.5) /* Upwards scaling */ @@ -71,7 +68,7 @@ internal class ConservativeScalingGovernorTest { val minSpeed = firstPState val threshold = 0.5 val stepSize = 0.02 * cpuCapacity - val governor = ConservativeScalingGovernor(threshold, stepSize) + val governor = ScalingGovernors.conservative(threshold, stepSize) val policy = mockk(relaxUnitFun = true) every { policy.max } returns cpuCapacity @@ -81,9 +78,8 @@ internal class ConservativeScalingGovernorTest { every { policy.target } answers { target } every { policy.target = any() } propertyType Double::class answers { target = value } - val logic = governor.createLogic(policy) + val logic = governor.newGovernor(policy) logic.onStart() - assertEquals(threshold, governor.threshold) logic.onLimit(0.5) /* Upwards scaling */ diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernorTest.kt index ca759e39..d6a7090b 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernorTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernorTest.kt @@ -25,11 +25,10 @@ package org.opendc.simulator.compute.kernel.cpufreq import io.mockk.every import io.mockk.mockk import io.mockk.verify -import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test /** - * Test suite for the [OnDemandScalingGovernor] + * Test suite for the on-demand [ScalingGovernor]. */ internal class OnDemandScalingGovernorTest { @Test @@ -37,15 +36,14 @@ internal class OnDemandScalingGovernorTest { val cpuCapacity = 4100.0 val minSpeed = cpuCapacity / 2 val defaultThreshold = 0.8 - val governor = OnDemandScalingGovernor() + val governor = ScalingGovernors.ondemand(defaultThreshold) val policy = mockk(relaxUnitFun = true) every { policy.min } returns minSpeed every { policy.max } returns cpuCapacity - val logic = governor.createLogic(policy) + val logic = governor.newGovernor(policy) logic.onStart() - assertEquals(defaultThreshold, governor.threshold) verify(exactly = 1) { policy.target = minSpeed } logic.onLimit(0.5) @@ -60,16 +58,15 @@ internal class OnDemandScalingGovernorTest { val firstPState = 1000.0 val cpuCapacity = 4100.0 val threshold = 0.5 - val governor = OnDemandScalingGovernor(threshold) + val governor = ScalingGovernors.ondemand(threshold) val policy = mockk(relaxUnitFun = true) every { policy.max } returns cpuCapacity every { policy.min } returns firstPState - val logic = governor.createLogic(policy) + val logic = governor.newGovernor(policy) logic.onStart() - assertEquals(threshold, governor.threshold) verify(exactly = 1) { policy.target = firstPState } logic.onLimit(0.1) diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernorTest.kt index a4bb24f2..f03f41fe 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernorTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernorTest.kt @@ -34,7 +34,7 @@ internal class PerformanceScalingGovernorTest { @Test fun testSetStartLimit() { val policy = spyk() - val logic = PerformanceScalingGovernor().createLogic(policy) + val logic = ScalingGovernors.performance().newGovernor(policy) every { policy.max } returns 4100.0 diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernorTest.kt index 662d55fb..4cee8199 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernorTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernorTest.kt @@ -36,7 +36,7 @@ internal class PowerSaveScalingGovernorTest { val cpuCapacity = 4100.0 val minSpeed = cpuCapacity / 2 val policy = mockk(relaxUnitFun = true) - val logic = PowerSaveScalingGovernor().createLogic(policy) + val logic = ScalingGovernors.powerSave().newGovernor(policy) every { policy.max } returns cpuCapacity every { policy.min } returns minSpeed @@ -55,7 +55,7 @@ internal class PowerSaveScalingGovernorTest { val cpuCapacity = 4100.0 val firstPState = 1000.0 val policy = mockk(relaxUnitFun = true) - val logic = PowerSaveScalingGovernor().createLogic(policy) + val logic = ScalingGovernors.powerSave().newGovernor(policy) every { policy.max } returns cpuCapacity every { policy.min } returns firstPState diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PStatePowerDriverTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PStatePowerDriverTest.kt deleted file mode 100644 index 3c0a55a6..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PStatePowerDriverTest.kt +++ /dev/null @@ -1,123 +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.power - -import io.mockk.every -import io.mockk.mockk -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.opendc.simulator.compute.SimBareMetalMachine -import org.opendc.simulator.compute.SimProcessingUnit - -/** - * Test suite for [PStatePowerDriver]. - */ -internal class PStatePowerDriverTest { - @Test - fun testPowerBaseline() { - val machine = mockk() - - val driver = PStatePowerDriver( - sortedMapOf( - 2800.0 to ConstantPowerModel(200.0), - 3300.0 to ConstantPowerModel(300.0), - 3600.0 to ConstantPowerModel(350.0) - ) - ) - - val logic = driver.createLogic(machine, emptyList()) - assertEquals(200.0, logic.computePower()) - } - - @Test - fun testPowerWithSingleCpu() { - val machine = mockk() - val cpu = mockk(relaxUnitFun = true) - - every { cpu.capacity } returns 3200.0 - every { cpu.rate } returns 1200.0 - - val driver = PStatePowerDriver( - sortedMapOf( - 2800.0 to ConstantPowerModel(200.0), - 3300.0 to ConstantPowerModel(300.0), - 3600.0 to ConstantPowerModel(350.0) - ) - ) - - val logic = driver.createLogic(machine, listOf(cpu)) - - assertEquals(300.0, logic.computePower()) - } - - @Test - fun testPowerWithMultipleCpus() { - val machine = mockk() - val cpu = mockk(relaxUnitFun = true) - val cpus = listOf(cpu, cpu) - - every { cpus[0].capacity } returns 1000.0 - every { cpus[0].rate } returns 1200.0 - - every { cpus[1].capacity } returns 3500.0 - every { cpus[1].rate } returns 1200.0 - - val driver = PStatePowerDriver( - sortedMapOf( - 2800.0 to ConstantPowerModel(200.0), - 3300.0 to ConstantPowerModel(300.0), - 3600.0 to ConstantPowerModel(350.0) - ) - ) - - val logic = driver.createLogic(machine, cpus) - - assertEquals(350.0, logic.computePower()) - } - - @Test - fun testPowerBasedOnUtilization() { - val machine = mockk() - val cpu = mockk(relaxUnitFun = true) - - every { cpu.model.frequency } returns 4200.0 - - val driver = PStatePowerDriver( - sortedMapOf( - 2800.0 to LinearPowerModel(200.0, 100.0), - 3300.0 to LinearPowerModel(250.0, 150.0), - 4000.0 to LinearPowerModel(300.0, 200.0) - ) - ) - - val logic = driver.createLogic(machine, listOf(cpu)) - - every { cpu.rate } returns 1400.0 - every { cpu.capacity } returns 1400.0 - assertEquals(150.0, logic.computePower()) - - every { cpu.rate } returns 1400.0 - every { cpu.capacity } returns 4000.0 - assertEquals(235.0, logic.computePower()) - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PowerModelTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PowerModelTest.kt index 67532d5b..9a6263c5 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PowerModelTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PowerModelTest.kt @@ -38,7 +38,7 @@ internal class PowerModelTest { @ParameterizedTest @MethodSource("MachinePowerModelArgs") fun `compute power consumption given CPU loads`( - powerModel: PowerModel, + powerModel: CpuPowerModel, expectedPowerConsumption: Double ) { val computedPowerConsumption = powerModel.computePower(cpuUtil) @@ -48,10 +48,10 @@ internal class PowerModelTest { @ParameterizedTest @MethodSource("MachinePowerModelArgs") fun `ignore idle power when computing power consumptions`( - powerModel: PowerModel, + powerModel: CpuPowerModel, expectedPowerConsumption: Double ) { - val zeroPowerModel = ZeroIdlePowerDecorator(powerModel) + val zeroPowerModel = CpuPowerModels.zeroIdle(powerModel) assertAll( { assertEquals(expectedPowerConsumption, zeroPowerModel.computePower(cpuUtil), epsilon) }, @@ -61,8 +61,9 @@ internal class PowerModelTest { @Test fun `compute power draw by the SPEC benchmark model`() { - val ibm = listOf(58.4, 98.0, 109.0, 118.0, 128.0, 140.0, 153.0, 170.0, 189.0, 205.0, 222.0) - val powerModel = InterpolationPowerModel(ibm) + val powerModel = CpuPowerModels.interpolate( + 58.4, 98.0, 109.0, 118.0, 128.0, 140.0, 153.0, 170.0, 189.0, 205.0, 222.0 + ) assertAll( { assertEquals(58.4, powerModel.computePower(0.0)) }, @@ -80,14 +81,14 @@ internal class PowerModelTest { private companion object { @JvmStatic fun MachinePowerModelArgs(): Stream = Stream.of( - Arguments.of(ConstantPowerModel(0.0), 0.0), - Arguments.of(LinearPowerModel(350.0, 200.0), 335.0), - Arguments.of(SquarePowerModel(350.0, 200.0), 321.5), - Arguments.of(CubicPowerModel(350.0, 200.0), 309.35), - Arguments.of(SqrtPowerModel(350.0, 200.0), 342.302), - Arguments.of(MsePowerModel(350.0, 200.0, 1.4), 340.571), - Arguments.of(AsymptoticPowerModel(350.0, 200.0, 0.3, false), 338.765), - Arguments.of(AsymptoticPowerModel(350.0, 200.0, 0.3, true), 323.072) + Arguments.of(CpuPowerModels.constant(0.0), 0.0), + Arguments.of(CpuPowerModels.linear(350.0, 200.0), 335.0), + Arguments.of(CpuPowerModels.square(350.0, 200.0), 321.5), + Arguments.of(CpuPowerModels.cubic(350.0, 200.0), 309.35), + Arguments.of(CpuPowerModels.sqrt(350.0, 200.0), 342.302), + Arguments.of(CpuPowerModels.mse(350.0, 200.0, 1.4), 340.571), + Arguments.of(CpuPowerModels.asymptotic(350.0, 200.0, 0.3, false), 338.765), + Arguments.of(CpuPowerModels.asymptotic(350.0, 200.0, 0.3, true), 323.072) ) } } diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkloadTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkloadTest.kt index b3e57453..edbc0571 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkloadTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkloadTest.kt @@ -32,7 +32,7 @@ class SimFlopsWorkloadTest { @Test fun testFlopsNonNegative() { assertThrows("FLOPs must be non-negative") { - SimFlopsWorkload(-1) + SimFlopsWorkload(-1, 1.0) } } diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt index 83e1f81c..e3b6e6c5 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt @@ -31,10 +31,8 @@ import org.opendc.simulator.compute.model.MachineModel import org.opendc.simulator.compute.model.MemoryUnit import org.opendc.simulator.compute.model.ProcessingNode import org.opendc.simulator.compute.model.ProcessingUnit -import org.opendc.simulator.compute.power.ConstantPowerModel -import org.opendc.simulator.compute.power.SimplePowerDriver import org.opendc.simulator.compute.runWorkload -import org.opendc.simulator.flow.FlowEngine +import org.opendc.simulator.flow2.FlowEngine import org.opendc.simulator.kotlin.runSimulation /** @@ -48,28 +46,28 @@ class SimTraceWorkloadTest { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) machineModel = MachineModel( - cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, - memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, + /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } ) } @Test fun testSmoke() = runSimulation { - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) - val workload = SimTraceWorkload( + val workload = SimTrace.ofFragments( SimTraceFragment(0, 1000, 2 * 28.0, 2), SimTraceFragment(1000, 1000, 2 * 3100.0, 2), SimTraceFragment(2000, 1000, 0.0, 2), SimTraceFragment(3000, 1000, 2 * 73.0, 2) - ), - offset = 0 - ) + ).createWorkload(0) machine.runWorkload(workload) @@ -78,21 +76,21 @@ class SimTraceWorkloadTest { @Test fun testOffset() = runSimulation { - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) - val workload = SimTraceWorkload( + val workload = SimTrace.ofFragments( SimTraceFragment(0, 1000, 2 * 28.0, 2), SimTraceFragment(1000, 1000, 2 * 3100.0, 2), SimTraceFragment(2000, 1000, 0.0, 2), SimTraceFragment(3000, 1000, 2 * 73.0, 2) - ), - offset = 1000 - ) + ).createWorkload(1000) machine.runWorkload(workload) @@ -101,21 +99,21 @@ class SimTraceWorkloadTest { @Test fun testSkipFragment() = runSimulation { - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) - val workload = SimTraceWorkload( + val workload = SimTrace.ofFragments( SimTraceFragment(0, 1000, 2 * 28.0, 2), SimTraceFragment(1000, 1000, 2 * 3100.0, 2), SimTraceFragment(2000, 1000, 0.0, 2), SimTraceFragment(3000, 1000, 2 * 73.0, 2) - ), - offset = 0 - ) + ).createWorkload(0) delay(1000L) machine.runWorkload(workload) @@ -125,21 +123,21 @@ class SimTraceWorkloadTest { @Test fun testZeroCores() = runSimulation { - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) - val workload = SimTraceWorkload( + val workload = SimTrace.ofFragments( SimTraceFragment(0, 1000, 2 * 28.0, 2), SimTraceFragment(1000, 1000, 2 * 3100.0, 2), SimTraceFragment(2000, 1000, 0.0, 0), SimTraceFragment(3000, 1000, 2 * 73.0, 2) - ), - offset = 0 - ) + ).createWorkload(0) machine.runWorkload(workload) diff --git a/opendc-simulator/opendc-simulator-compute/src/test/resources/spec_machines.yml b/opendc-simulator/opendc-simulator-compute/src/test/resources/spec_machines.yml deleted file mode 100644 index d51cba80..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/test/resources/spec_machines.yml +++ /dev/null @@ -1,29 +0,0 @@ ---- -# The power model of an IBM server x3550 (2 x [Xeon X5675 3067 MHz, 6 cores], 16GB).
-# -# http://www.spec.org/power_ssj2008/results/res2011q2/power_ssj2008-20110406-00368.html -IBMx3550M3_XeonX5675: [58.4, 98.0, 109.0, 118.0, 128.0, 140.0, 153.0, 170.0, 189.0, 205.0, 222.0] - # The power model of an IBM server x3550 (2 x [Xeon X5670 2933 MHz, 6 cores], 12GB).
- # - # http://www.spec.org/power_ssj2008/results/res2010q2/power_ssj2008-20100315-00239.html -IBMx3550M3_XeonX5670: [66.0, 107.0, 120.0, 131.0, 143.0, 156.0, 173.0, 191.0, 211.0, 229.0, 247.0] - # the power model of an IBM server x3250 (1 x [Xeon X3480 3067 MHz, 4 cores], 8GB).
- # - # http://www.spec.org/power_ssj2008/results/res2010q4/power_ssj2008-20101001-00297.html -IBMx3250M3_XeonX3480: [42.3, 46.7, 49.7, 55.4, 61.8, 69.3, 76.1, 87.0, 96.1, 106.0, 113.0] - # The power model of an IBM server x3250 (1 x [Xeon X3470 2933 MHz, 4 cores], 8GB).
- # - # http://www.spec.org/power_ssj2008/results/res2009q4/power_ssj2008-20091104-00213.html -IBMx3250M3_XeonX3470: [41.6, 46.7, 52.3, 57.9, 65.4, 73.0, 80.7, 89.5, 99.6, 105.0, 113.0] - # The power model of an HP ProLiant ML110 G5 (1 x [Xeon 3075 2660 MHz, 2 cores], 4GB).
- # - # http://www.spec.org/power_ssj2008/results/res2011q1/power_ssj2008-20110124-00339.html -HPProLiantMl110G5_Xeon3075: [93.7, 97.0, 101.0, 105.0, 110.0, 116.0, 121.0, 125.0, 129.0, 133.0, 135.0] - # The power model of an HP ProLiant ML110 G4 (1 x [Xeon 3040 1860 MHz, 2 cores], 4GB).
- # - # http://www.spec.org/power_ssj2008/results/res2011q1/power_ssj2008-20110127-00342.html -HPProLiantMl110G4_Xeon3040: [86.0, 89.4, 92.6, 96.0, 99.5, 102.0, 106.0, 108.0, 112.0, 114.0, 117.0] - # The power model of an HP ProLiant ML110 G3 (1 x [Pentium D930 3000 MHz, 2 cores], 4GB).
- # - # http://www.spec.org/power_ssj2008/results/res2011q1/power_ssj2008-20110127-00342.html -HPProLiantMl110G3_PentiumD930: [105.0, 112.0, 118.0, 125.0, 131.0, 137.0, 147.0, 153.0, 157.0, 164.0, 169.0] -- cgit v1.2.3 From 037c0600d0fe3ec699f239c41ab0e60a458484d7 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Tue, 18 Oct 2022 11:35:56 +0200 Subject: perf(sim/compute): Optimize workload implementation of SimTrace This change updates the workload implementation of SimTrace by introducing multiple implementations of the FlowStageLogic interface, which is selected dynamically when the workload starts. This change helps eliminate the unecessary division (and memory fetch) when running a workload with just a single CPU. --- .../simulator/compute/workload/SimTrace.java | 155 ++++++++++++++++++--- 1 file changed, 137 insertions(+), 18 deletions(-) (limited to 'opendc-simulator/opendc-simulator-compute/src') diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTrace.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTrace.java index 0bd2b2eb..12a567ff 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTrace.java +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTrace.java @@ -203,12 +203,8 @@ public final class SimTrace { /** * Implementation of {@link SimWorkload} that executes a trace. */ - private static class Workload implements SimWorkload, FlowStageLogic { - private SimMachineContext ctx; - private FlowStage stage; - private OutPort[] outputs; - private int index; - private int coreCount; + private static class Workload implements SimWorkload { + private WorkloadStageLogic logic; private final long offset; private final double[] usageCol; @@ -226,7 +222,137 @@ public final class SimTrace { @Override public void onStart(SimMachineContext ctx) { + final WorkloadStageLogic logic; + if (ctx.getCpus().size() == 1) { + logic = new SingleWorkloadLogic(ctx, offset, usageCol, deadlineCol, size); + } else { + logic = new MultiWorkloadLogic(ctx, offset, usageCol, deadlineCol, coresCol, size); + } + this.logic = logic; + } + + @Override + public void onStop(SimMachineContext ctx) { + final WorkloadStageLogic logic = this.logic; + + if (logic != null) { + this.logic = null; + logic.getStage().close(); + } + } + } + + /** + * Interface to represent the {@link FlowStage} that simulates the trace workload. + */ + private interface WorkloadStageLogic extends FlowStageLogic { + /** + * Return the {@link FlowStage} belonging to this instance. + */ + FlowStage getStage(); + } + + /** + * Implementation of {@link FlowStageLogic} for just a single CPU resource. + */ + private static class SingleWorkloadLogic implements WorkloadStageLogic { + private final FlowStage stage; + private final OutPort output; + private int index; + + private final long offset; + private final double[] usageCol; + private final long[] deadlineCol; + private final int size; + + private final SimMachineContext ctx; + + private SingleWorkloadLogic( + SimMachineContext ctx, long offset, double[] usageCol, long[] deadlineCol, int size) { this.ctx = ctx; + this.offset = offset; + this.usageCol = usageCol; + this.deadlineCol = deadlineCol; + this.size = size; + + final FlowGraph graph = ctx.getGraph(); + final List cpus = ctx.getCpus(); + + stage = graph.newStage(this); + + final SimProcessingUnit cpu = cpus.get(0); + final OutPort output = stage.getOutlet("cpu"); + this.output = output; + + graph.connect(output, cpu.getInput()); + } + + @Override + public long onUpdate(FlowStage ctx, long now) { + int size = this.size; + long offset = this.offset; + long nowOffset = now - offset; + + int index = this.index; + + long[] deadlines = deadlineCol; + long deadline = deadlines[index]; + + while (deadline <= nowOffset) { + if (++index >= size) { + return doStop(ctx); + } + deadline = deadlines[index]; + } + + this.index = index; + this.output.push((float) usageCol[index]); + return deadline + offset; + } + + @Override + public FlowStage getStage() { + return stage; + } + + /** + * Helper method to stop the execution of the workload. + */ + private long doStop(FlowStage ctx) { + final SimMachineContext machineContext = this.ctx; + if (machineContext != null) { + machineContext.shutdown(); + } + ctx.close(); + return Long.MAX_VALUE; + } + } + + /** + * Implementation of {@link FlowStageLogic} for multiple CPUs. + */ + private static class MultiWorkloadLogic implements WorkloadStageLogic { + private final FlowStage stage; + private final OutPort[] outputs; + private int index; + private final int coreCount; + + private final long offset; + private final double[] usageCol; + private final long[] deadlineCol; + private final int[] coresCol; + private final int size; + + private final SimMachineContext ctx; + + private MultiWorkloadLogic( + SimMachineContext ctx, long offset, double[] usageCol, long[] deadlineCol, int[] coresCol, int size) { + this.ctx = ctx; + this.offset = offset; + this.usageCol = usageCol; + this.deadlineCol = deadlineCol; + this.coresCol = coresCol; + this.size = size; final FlowGraph graph = ctx.getGraph(); final List cpus = ctx.getCpus(); @@ -246,18 +372,6 @@ public final class SimTrace { } } - @Override - public void onStop(SimMachineContext ctx) { - this.ctx = null; - - final FlowStage stage = this.stage; - - if (stage != null) { - this.stage = null; - stage.close(); - } - } - @Override public long onUpdate(FlowStage ctx, long now) { int size = this.size; @@ -299,5 +413,10 @@ public final class SimTrace { return deadline + offset; } + + @Override + public FlowStage getStage() { + return stage; + } } } -- cgit v1.2.3