From 5a365dbc068f2a8cdfa9813c39cc84bb30e15637 Mon Sep 17 00:00:00 2001 From: Dante Niewenhuis Date: Fri, 25 Oct 2024 13:32:41 +0200 Subject: Rewrote the FlowEngine (#256) * Removed unused components. Updated tests. Improved checkpointing model Improved model, started with SimPowerSource implemented FailureModels and Checkpointing First working version midway commit first update All simulation are now run with a single CPU and single MemoryUnit. multi CPUs are combined into one. This is for performance and explainability. * fixed merge conflicts * Updated M3SA paths. * Fixed small typo --- .../opendc-simulator-compute/build.gradle.kts | 2 - .../simulator/compute/SimMachineBenchmarks.kt | 15 +- .../simulator/compute/SimAbstractMachine.java | 354 -------- .../simulator/compute/SimBareMetalMachine.java | 280 ------- .../org/opendc/simulator/compute/SimMachine.java | 61 -- .../simulator/compute/SimMachineContext.java | 96 --- .../org/opendc/simulator/compute/SimMemory.java | 41 - .../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 -- .../simulator/compute/cpu/CPUPowerModelsFactory.kt | 61 ++ .../simulator/compute/cpu/CpuPowerModel.java | 44 + .../simulator/compute/cpu/CpuPowerModels.java | 409 +++++++++ .../org/opendc/simulator/compute/cpu/SimCpu.java | 260 ++++++ .../compute/device/SimNetworkAdapter.java | 36 - .../simulator/compute/device/SimPeripheral.java | 33 - .../simulator/compute/kernel/SimHypervisor.java | 933 --------------------- .../compute/kernel/SimHypervisorCounters.java | 53 -- .../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 -- .../compute/machine/PerformanceCounters.java | 102 +++ .../simulator/compute/machine/SimMachine.java | 184 ++++ .../simulator/compute/machine/VirtualMachine.java | 246 ++++++ .../opendc/simulator/compute/memory/Memory.java | 55 ++ .../org/opendc/simulator/compute/model/Cpu.java | 137 --- .../simulator/compute/model/MachineModel.java | 139 --- .../opendc/simulator/compute/model/MemoryUnit.java | 100 --- .../simulator/compute/model/NetworkAdapter.java | 88 -- .../simulator/compute/model/ProcessingNode.java | 100 --- .../simulator/compute/model/StorageDevice.java | 112 --- .../opendc/simulator/compute/models/CpuModel.java | 137 +++ .../simulator/compute/models/MachineModel.java | 98 +++ .../simulator/compute/models/MemoryUnit.java | 100 +++ .../compute/power/CPUPowerModelsFactory.kt | 61 -- .../simulator/compute/power/CpuPowerModel.java | 44 - .../simulator/compute/power/CpuPowerModels.java | 409 --------- .../simulator/compute/power/SimPowerSource.java | 169 ++++ .../org/opendc/simulator/compute/power/SimPsu.java | 196 +++++ .../simulator/compute/workload/ChainWorkload.java | 72 ++ .../compute/workload/CheckpointModel.java | 94 +++ .../compute/workload/SimChainWorkload.java | 423 ++++------ .../compute/workload/SimFlopsWorkload.java | 179 ---- .../compute/workload/SimRuntimeWorkload.java | 227 ----- .../simulator/compute/workload/SimTrace.java | 413 --------- .../compute/workload/SimTraceFragment.java | 30 - .../compute/workload/SimTraceWorkload.java | 270 ++++++ .../simulator/compute/workload/SimWorkload.java | 32 +- .../simulator/compute/workload/SimWorkloads.java | 82 -- .../simulator/compute/workload/TraceFragment.java | 30 + .../simulator/compute/workload/TraceWorkload.java | 154 ++++ .../simulator/compute/workload/Workload.java | 36 + .../org/opendc/simulator/compute/Coroutines.kt | 11 +- .../org/opendc/simulator/compute/SimMachineTest.kt | 883 ++++++++----------- .../compute/kernel/SimFairShareHypervisorTest.kt | 269 ------ .../compute/kernel/SimSpaceSharedHypervisorTest.kt | 234 ------ .../cpufreq/ConservativeScalingGovernorTest.kt | 94 --- .../kernel/cpufreq/OnDemandScalingGovernorTest.kt | 78 -- .../cpufreq/PerformanceScalingGovernorTest.kt | 50 -- .../kernel/cpufreq/PowerSaveScalingGovernorTest.kt | 72 -- .../simulator/compute/power/PowerModelTest.kt | 180 ---- .../compute/workload/SimChainWorkloadTest.kt | 310 ------- .../compute/workload/SimFlopsWorkloadTest.kt | 59 -- .../compute/workload/SimTraceWorkloadTest.kt | 160 ---- .../org/opendc/simulator/flow2/FlowBenchmarks.kt | 8 +- .../java/org/opendc/simulator/Multiplexer.java | 210 +++++ .../org/opendc/simulator/engine/FlowConsumer.java | 34 + .../java/org/opendc/simulator/engine/FlowEdge.java | 114 +++ .../org/opendc/simulator/engine/FlowEngine.java | 204 +++++ .../org/opendc/simulator/engine/FlowGraph.java | 112 +++ .../java/org/opendc/simulator/engine/FlowNode.java | 191 +++++ .../org/opendc/simulator/engine/FlowNodeQueue.java | 109 +++ .../org/opendc/simulator/engine/FlowSupplier.java | 36 + .../opendc/simulator/engine/FlowTimerQueue.java | 205 +++++ .../opendc/simulator/engine/InvocationStack.java | 94 +++ .../org/opendc/simulator/flow2/FlowEngine.java | 256 ------ .../java/org/opendc/simulator/flow2/FlowGraph.java | 63 -- .../opendc/simulator/flow2/FlowGraphInternal.java | 93 -- .../java/org/opendc/simulator/flow2/FlowStage.java | 312 ------- .../org/opendc/simulator/flow2/FlowStageLogic.java | 38 - .../org/opendc/simulator/flow2/FlowStageQueue.java | 109 --- .../org/opendc/simulator/flow2/FlowTimerQueue.java | 208 ----- .../java/org/opendc/simulator/flow2/InHandler.java | 54 -- .../org/opendc/simulator/flow2/InHandlers.java | 53 -- .../java/org/opendc/simulator/flow2/InPort.java | 214 ----- .../java/org/opendc/simulator/flow2/Inlet.java | 38 - .../opendc/simulator/flow2/InvocationStack.java | 95 --- .../org/opendc/simulator/flow2/OutHandler.java | 47 -- .../org/opendc/simulator/flow2/OutHandlers.java | 53 -- .../java/org/opendc/simulator/flow2/OutPort.java | 224 ----- .../java/org/opendc/simulator/flow2/Outlet.java | 38 - .../simulator/flow2/mux/FlowMultiplexer.java | 95 --- .../flow2/mux/FlowMultiplexerFactory.java | 51 -- .../flow2/mux/ForwardingFlowMultiplexer.java | 287 ------- .../simulator/flow2/mux/MaxMinFlowMultiplexer.java | 297 ------- .../org/opendc/simulator/flow2/sink/FlowSink.java | 36 - .../simulator/flow2/sink/SimpleFlowSink.java | 123 --- .../simulator/flow2/source/EmptyFlowSource.java | 65 -- .../opendc/simulator/flow2/source/FlowSource.java | 36 - .../simulator/flow2/source/RuntimeFlowSource.java | 128 --- .../simulator/flow2/source/SimpleFlowSource.java | 131 --- .../simulator/flow2/source/TraceFlowSource.java | 151 ---- .../opendc/simulator/flow2/util/FlowTransform.java | 41 - .../simulator/flow2/util/FlowTransformer.java | 124 --- .../simulator/flow2/util/FlowTransforms.java | 57 -- .../src/test/kotlin/InvocationStackTest.kt | 70 ++ .../org/opendc/simulator/flow2/FlowEngineTest.kt | 210 ----- .../opendc/simulator/flow2/FlowTimerQueueTest.kt | 385 --------- .../opendc/simulator/flow2/InvocationStackTest.kt | 71 -- .../flow2/mux/ForwardingFlowMultiplexerTest.kt | 72 -- .../flow2/mux/MaxMinFlowMultiplexerTest.kt | 55 -- .../opendc/simulator/flow2/sink/FlowSinkTest.kt | 131 --- .../opendc-simulator-network/build.gradle.kts | 34 - .../opendc/simulator/network/SimNetworkLink.java | 77 -- .../opendc/simulator/network/SimNetworkPort.java | 110 --- .../opendc/simulator/network/SimNetworkSink.java | 70 -- .../opendc/simulator/network/SimNetworkSwitch.java | 35 - .../simulator/network/SimNetworkSwitchVirtual.java | 107 --- .../opendc/simulator/network/SimNetworkLinkTest.kt | 91 -- .../opendc/simulator/network/SimNetworkSinkTest.kt | 153 ---- .../network/SimNetworkSwitchVirtualTest.kt | 77 -- .../org/opendc/simulator/network/TestSource.kt | 56 -- .../opendc-simulator-power/build.gradle.kts | 34 - .../java/org/opendc/simulator/power/SimPdu.java | 141 ---- .../org/opendc/simulator/power/SimPowerInlet.java | 53 -- .../org/opendc/simulator/power/SimPowerOutlet.java | 91 -- .../org/opendc/simulator/power/SimPowerSource.java | 71 -- .../java/org/opendc/simulator/power/SimUps.java | 137 --- .../org/opendc/simulator/power/SimPduTest.kt | 133 --- .../opendc/simulator/power/SimPowerSourceTest.kt | 155 ---- .../org/opendc/simulator/power/SimUpsTest.kt | 108 --- .../kotlin/org/opendc/simulator/power/TestInlet.kt | 49 -- 140 files changed, 4665 insertions(+), 13911 deletions(-) delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimAbstractMachine.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimBareMetalMachine.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachine.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachineContext.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMemory.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimNetworkInterface.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimProcessingUnit.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsu.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactories.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactory.java delete 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/cpu/CPUPowerModelsFactory.kt create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/cpu/CpuPowerModel.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/cpu/CpuPowerModels.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/cpu/SimCpu.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimNetworkAdapter.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimPeripheral.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisor.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisorCounters.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernor.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernorFactory.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernors.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingPolicy.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceMember.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.java delete 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/machine/PerformanceCounters.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/machine/SimMachine.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/machine/VirtualMachine.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/memory/Memory.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/Cpu.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/MachineModel.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/MemoryUnit.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/NetworkAdapter.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/ProcessingNode.java delete 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/models/CpuModel.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/models/MachineModel.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/models/MemoryUnit.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CPUPowerModelsFactory.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CpuPowerModel.java delete 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/power/SimPowerSource.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/SimPsu.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/ChainWorkload.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/CheckpointModel.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimFlopsWorkload.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimRuntimeWorkload.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTrace.java delete 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/SimTraceWorkload.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkloads.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/TraceFragment.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/TraceWorkload.java create mode 100644 opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/Workload.java delete mode 100644 opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernorTest.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernorTest.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernorTest.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernorTest.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PowerModelTest.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimChainWorkloadTest.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkloadTest.kt delete mode 100644 opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt create mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/Multiplexer.java create mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowConsumer.java create mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowEdge.java create mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowEngine.java create mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowGraph.java create mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowNode.java create mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowNodeQueue.java create mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowSupplier.java create mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowTimerQueue.java create mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/InvocationStack.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowEngine.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowGraph.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowGraphInternal.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowStage.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowStageLogic.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowStageQueue.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowTimerQueue.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/InHandler.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/InHandlers.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/InPort.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/Inlet.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/InvocationStack.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/OutHandler.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/OutHandlers.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/OutPort.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/Outlet.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/mux/FlowMultiplexer.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/mux/FlowMultiplexerFactory.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/mux/ForwardingFlowMultiplexer.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/mux/MaxMinFlowMultiplexer.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/sink/FlowSink.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/sink/SimpleFlowSink.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/source/EmptyFlowSource.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/source/FlowSource.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/source/RuntimeFlowSource.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/source/SimpleFlowSource.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/source/TraceFlowSource.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/util/FlowTransform.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/util/FlowTransformer.java delete mode 100644 opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/util/FlowTransforms.java create mode 100644 opendc-simulator/opendc-simulator-flow/src/test/kotlin/InvocationStackTest.kt delete mode 100644 opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/FlowEngineTest.kt delete mode 100644 opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/FlowTimerQueueTest.kt delete mode 100644 opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/InvocationStackTest.kt delete mode 100644 opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/ForwardingFlowMultiplexerTest.kt delete mode 100644 opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/MaxMinFlowMultiplexerTest.kt delete mode 100644 opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/sink/FlowSinkTest.kt delete mode 100644 opendc-simulator/opendc-simulator-network/build.gradle.kts delete mode 100644 opendc-simulator/opendc-simulator-network/src/main/java/org/opendc/simulator/network/SimNetworkLink.java delete mode 100644 opendc-simulator/opendc-simulator-network/src/main/java/org/opendc/simulator/network/SimNetworkPort.java delete mode 100644 opendc-simulator/opendc-simulator-network/src/main/java/org/opendc/simulator/network/SimNetworkSink.java delete mode 100644 opendc-simulator/opendc-simulator-network/src/main/java/org/opendc/simulator/network/SimNetworkSwitch.java delete mode 100644 opendc-simulator/opendc-simulator-network/src/main/java/org/opendc/simulator/network/SimNetworkSwitchVirtual.java delete mode 100644 opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkLinkTest.kt delete mode 100644 opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSinkTest.kt delete mode 100644 opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSwitchVirtualTest.kt delete mode 100644 opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/TestSource.kt delete mode 100644 opendc-simulator/opendc-simulator-power/build.gradle.kts delete mode 100644 opendc-simulator/opendc-simulator-power/src/main/java/org/opendc/simulator/power/SimPdu.java delete mode 100644 opendc-simulator/opendc-simulator-power/src/main/java/org/opendc/simulator/power/SimPowerInlet.java delete mode 100644 opendc-simulator/opendc-simulator-power/src/main/java/org/opendc/simulator/power/SimPowerOutlet.java delete mode 100644 opendc-simulator/opendc-simulator-power/src/main/java/org/opendc/simulator/power/SimPowerSource.java delete mode 100644 opendc-simulator/opendc-simulator-power/src/main/java/org/opendc/simulator/power/SimUps.java delete mode 100644 opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPduTest.kt delete mode 100644 opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPowerSourceTest.kt delete mode 100644 opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimUpsTest.kt delete mode 100644 opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/TestInlet.kt (limited to 'opendc-simulator') diff --git a/opendc-simulator/opendc-simulator-compute/build.gradle.kts b/opendc-simulator/opendc-simulator-compute/build.gradle.kts index 0ea0c252..246fa5e8 100644 --- a/opendc-simulator/opendc-simulator-compute/build.gradle.kts +++ b/opendc-simulator/opendc-simulator-compute/build.gradle.kts @@ -29,8 +29,6 @@ plugins { dependencies { api(projects.opendcSimulator.opendcSimulatorFlow) - api(projects.opendcSimulator.opendcSimulatorPower) - api(projects.opendcSimulator.opendcSimulatorNetwork) implementation(projects.opendcSimulator.opendcSimulatorCore) testImplementation(libs.slf4j.simple) 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 5975f944..8d8f4ef8 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 @@ -24,12 +24,13 @@ package org.opendc.simulator.compute import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch -import org.opendc.simulator.compute.kernel.SimHypervisor -import org.opendc.simulator.compute.model.Cpu -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.workload.SimTrace +import org.opendc.simulator.compute.old.SimBareMetalMachine +import org.opendc.simulator.compute.old.kernel.SimHypervisor +import org.opendc.simulator.compute.old.model.CpuModel +import org.opendc.simulator.compute.old.model.MachineModel +import org.opendc.simulator.compute.old.model.MemoryUnit +import org.opendc.simulator.compute.old.model.ProcessingNode +import org.opendc.simulator.compute.old.workload.SimTrace import org.opendc.simulator.flow2.FlowEngine import org.opendc.simulator.flow2.mux.FlowMultiplexerFactory import org.opendc.simulator.kotlin.runSimulation @@ -60,7 +61,7 @@ class SimMachineBenchmarks { MachineModel( // cpus List(cpuNode.coreCount) { - Cpu( + CpuModel( cpuNode, it, 1000.0, 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 deleted file mode 100644 index 3a9e35c1..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimAbstractMachine.java +++ /dev/null @@ -1,354 +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; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Consumer; -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; - -/** - * Abstract implementation of the {@link SimMachine} interface. - */ -public abstract class SimAbstractMachine implements SimMachine { - private final MachineModel model; - - private SimAbstractMachineContext 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, Consumer completion) { - if (activeContext != null) { - throw new IllegalStateException("A machine cannot run multiple workloads concurrently"); - } - - final SimAbstractMachineContext ctx = createContext(workload, new HashMap<>(meta), completion); - ctx.start(); - return ctx; - } - - @Override - public final void cancel() { - final SimAbstractMachineContext context = activeContext; - if (context != null) { - context.shutdown(); - } - } - - /** - * Construct a new {@link SimAbstractMachineContext} instance representing the active execution. - * - * @param workload The workload to start on the machine. - * @param meta The metadata to pass to the workload. - * @param completion A block that is invoked when the workload completes carrying an exception if thrown by the workload. - */ - protected abstract SimAbstractMachineContext createContext( - SimWorkload workload, Map meta, Consumer completion); - - /** - * Return the active {@link SimAbstractMachineContext} instance (if any). - */ - protected SimAbstractMachineContext getActiveContext() { - return activeContext; - } - - /** - * The execution context in which the workload runs. - */ - public abstract static class SimAbstractMachineContext implements SimMachineContext { - private final SimAbstractMachine machine; - private final SimWorkload workload; - private final Map meta; - private final Consumer completion; - private boolean isClosed; - private SimWorkload snapshot; - - /** - * Construct a new {@link SimAbstractMachineContext} 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. - * @param completion A block that is invoked when the workload completes carrying an exception if thrown by the workload. - */ - public SimAbstractMachineContext( - SimAbstractMachine machine, - SimWorkload workload, - Map meta, - Consumer completion) { - this.machine = machine; - this.workload = workload; - this.meta = meta; - this.completion = completion; - } - - @Override - public final Map getMeta() { - return meta; - } - - @Override - public void makeSnapshot(long now) { - this.snapshot = workload.getSnapshot(); - } - - @Override - public SimWorkload getSnapshot(long now) { - return this.snapshot; - } - - @Override - public void reset() { - final FlowGraph graph = getMemory().getInput().getGraph(); - - final Inlet inlet = getCpu().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 final void shutdown() { - shutdown(null); - } - - @Override - public final void shutdown(Exception cause) { - 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 e) { - if (cause == null) { - cause = e; - } else { - cause.addSuppressed(e); - } - } - - completion.accept(cause); - } - - /** - * Start this context. - */ - final void start() { - try { - machine.activeContext = this; - workload.onStart(this); - } catch (Exception cause) { - shutdown(cause); - } - } - - /** - * Run the stop procedures for the resources associated with the machine. - */ - protected void doCancel() { - reset(); - } - - @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 MemoryUnit memoryUnit; - - public Memory(FlowGraph graph, MemoryUnit memoryUnit) { - - this.memoryUnit = memoryUnit; - this.sink = new SimpleFlowSink(graph, (float) memoryUnit.getSize()); - } - - @Override - public double getCapacity() { - return sink.getCapacity(); - } - - @Override - public MemoryUnit getMemoryUnit() { - return memoryUnit; - } - - @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 deleted file mode 100644 index 6acc605e..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimBareMetalMachine.java +++ /dev/null @@ -1,280 +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; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -import org.opendc.simulator.compute.device.SimPeripheral; -import org.opendc.simulator.compute.model.Cpu; -import org.opendc.simulator.compute.model.MachineModel; -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, Consumer)} )}. - */ -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 SimCpu cpu; - - 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); - - this.cpu = new SimCpu(psu, model.getCpu(), 0); - 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 SimAbstractMachineContext context = (SimAbstractMachineContext) getActiveContext(); - - if (context == null) { - return 0.0; - } - - return cpu.getFrequency(); - } - - /** - * The CPU demand of the machine in MHz. - */ - public double getCpuDemand() { - final SimAbstractMachineContext context = (SimAbstractMachineContext) getActiveContext(); - - if (context == null) { - return 0.0; - } - - return cpu.getDemand(); - } - - /** - * The CPU usage of the machine in MHz. - */ - public double getCpuUsage() { - final SimAbstractMachineContext context = (SimAbstractMachineContext) getActiveContext(); - - if (context == null) { - return 0.0; - } - - return cpu.getSpeed(); - } - - @Override - protected SimAbstractMachine.SimAbstractMachineContext createContext( - SimWorkload workload, Map meta, Consumer completion) { - return new SimAbstractMachineContext(this, workload, meta, completion); - } - - /** - * The execution context for a {@link SimBareMetalMachine}. - */ - private static final class SimAbstractMachineContext extends SimAbstractMachine.SimAbstractMachineContext { - private final FlowGraph graph; - private final SimCpu cpu; - private final Memory memory; - private final List net; - private final List disk; - - private SimAbstractMachineContext( - SimBareMetalMachine machine, - SimWorkload workload, - Map meta, - Consumer completion) { - super(machine, workload, meta, completion); - - this.graph = machine.graph; - this.cpu = machine.cpu; - this.memory = machine.memory; - this.net = machine.net; - this.disk = machine.disk; - } - - @Override - public FlowGraph getGraph() { - return graph; - } - - @Override - public SimCpu getCpu() { - return cpu; - } - - @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 SimCpu implements SimProcessingUnit { - private final SimPsu psu; - private final Cpu cpuModel; - private final InPort port; - - private SimCpu(SimPsu psu, Cpu cpuModel, int id) { - this.psu = psu; - this.cpuModel = cpuModel; - this.port = psu.getCpuPower(id, cpuModel); - - this.port.pull((float) cpuModel.getTotalCapacity()); - } - - @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(cpuModel.getTotalCapacity(), frequency)); - psu.setCpuFrequency(port, frequency); - } - - @Override - public double getDemand() { - return port.getDemand(); - } - - @Override - public double getSpeed() { - return port.getRate(); - } - - @Override - public org.opendc.simulator.compute.model.Cpu getCpuModel() { - return cpuModel; - } - - @Override - public Inlet getInput() { - return port; - } - - @Override - public String toString() { - return "SimBareMetalMachine.Cpu[model=" + cpuModel + "]"; - } - } -} 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 deleted file mode 100644 index 1f86aa02..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachine.java +++ /dev/null @@ -1,61 +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; - -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -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. - * @param completion A block that is invoked when the workload completes carrying an exception if thrown by 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, Consumer completion); - - /** - * 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 deleted file mode 100644 index 887967fb..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachineContext.java +++ /dev/null @@ -1,96 +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; - -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -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, Consumer)}. - */ - Map getMeta(); - - /** - * Return the CPUs available on the machine. - */ - SimProcessingUnit getCpu(); - - /** - * 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(); - - /** - * Create a snapshot of the {@link SimWorkload} running on this machine. - * - * @throws UnsupportedOperationException if the workload does not support snapshotting. - */ - void makeSnapshot(long now); - - SimWorkload getSnapshot(long now); - - /** - * Reset all resources of the machine. - */ - void reset(); - - /** - * Shutdown the workload. - */ - void shutdown(); - - /** - * Shutdown the workload due to failure. - * - * @param cause The cause for shutting down the workload. - */ - void shutdown(Exception cause); -} 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 deleted file mode 100644 index 85027f28..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMemory.java +++ /dev/null @@ -1,41 +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; - -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. - */ - MemoryUnit getMemoryUnit(); -} 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 deleted file mode 100644 index 4b623e59..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimNetworkInterface.java +++ /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; - -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 deleted file mode 100644 index 213c3d4f..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimProcessingUnit.java +++ /dev/null @@ -1,62 +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; - -import org.opendc.simulator.compute.model.Cpu; -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. - */ - Cpu getCpuModel(); -} 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 deleted file mode 100644 index e7718604..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsu.java +++ /dev/null @@ -1,71 +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; - -import org.opendc.simulator.compute.model.Cpu; -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 getPowerDraw(); - - /** - * 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, Cpu 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 deleted file mode 100644 index 27327616..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactories.java +++ /dev/null @@ -1,214 +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; - -import java.time.InstantSource; -import org.jetbrains.annotations.NotNull; -import org.opendc.simulator.compute.model.Cpu; -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 getPowerDraw() { - return 0; - } - - @Override - public double getEnergyUsage() { - return 0; - } - - @Override - InPort getCpuPower(int id, Cpu 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 InstantSource clock; - - private double targetFreq; - private double totalUsage; - private long lastUpdate; - - private double powerDraw; - 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 getPowerDraw() { - return powerDraw; - } - - @Override - public double getEnergyUsage() { - updateEnergyUsage(clock.millis()); - return energyUsage; - } - - @Override - InPort getCpuPower(int id, Cpu model) { - targetFreq += model.getTotalCapacity(); - - 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); - powerDraw = 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 += powerDraw * 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 deleted file mode 100644 index 872e7016..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactory.java +++ /dev/null @@ -1,38 +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; - -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 deleted file mode 100644 index 341122dc..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimStorageInterface.java +++ /dev/null @@ -1,50 +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; - -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/cpu/CPUPowerModelsFactory.kt b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/cpu/CPUPowerModelsFactory.kt new file mode 100644 index 00000000..3600756b --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/cpu/CPUPowerModelsFactory.kt @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024 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.cpu + +// TODO: couple this correctly +public enum class CPUPowerModel { + Constant, + Sqrt, + Linear, + Square, + Cubic, +} + +public fun getPowerModel( + modelType: String, + power: Double, + maxPower: Double, + idlePower: Double, +): CpuPowerModel { + return when (modelType) { + "constant" -> CpuPowerModels.constant(power) + "sqrt" -> CpuPowerModels.sqrt(maxPower, idlePower) + "linear" -> CpuPowerModels.linear(maxPower, idlePower) + "square" -> CpuPowerModels.square(maxPower, idlePower) + "cubic" -> CpuPowerModels.cubic(maxPower, idlePower) + + else -> throw IllegalArgumentException("Unknown power modelType $modelType") + } +} + +public fun getPowerModel(modelType: String): CpuPowerModel { + return when (modelType) { + "constant" -> CpuPowerModels.constant(200.0) + "sqrt" -> CpuPowerModels.sqrt(350.0, 200.0) + "linear" -> CpuPowerModels.linear(350.0, 200.0) + "square" -> CpuPowerModels.square(350.0, 200.0) + "cubic" -> CpuPowerModels.cubic(350.0, 200.0) + + else -> throw IllegalArgumentException("Unknown power modelType $modelType") + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/cpu/CpuPowerModel.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/cpu/CpuPowerModel.java new file mode 100644 index 00000000..4323294e --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/cpu/CpuPowerModel.java @@ -0,0 +1,44 @@ +/* + * 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.cpu; + +import org.opendc.simulator.compute.machine.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); + + String getName(); + + default String getFullName() { + return getName(); + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/cpu/CpuPowerModels.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/cpu/CpuPowerModels.java new file mode 100644 index 00000000..b91bd7e2 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/cpu/CpuPowerModels.java @@ -0,0 +1,409 @@ +/* + * 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.cpu; + +import java.util.Arrays; + +/** + * A collection {@link CpuPowerModel} implementations. + */ +public class CpuPowerModels { + private CpuPowerModels() {} + + /** + * Construct a constant {@link CpuPowerModel}. + * + * @param power The power consumption of the host 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 host in W. + * @param idlePower The power draw of the host 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 host in W. + * @param idlePower The power draw of the host 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 host in W. + * @param idlePower The power draw of the host 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 host in W. + * @param idlePower The power draw of the host 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 host in W. + * @param idlePower The power draw of the host 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 host in W. + * @param idlePower The power draw of the host at its lowest utilization level in W. + * @param asymUtil A utilization level at which the host 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 + "]"; + } + + @Override + public String getName() { + return "ConstantPowerModel"; + } + } + + 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; + } + + // Clamps the provided utilization in the range of 0.0 and 1.0 + // This is done to avoid floating point errors + public double clampUtilization(double utilization) { + return Math.max(0.0, Math.min(1.0, utilization)); + } + + @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); + } + + @Override + public double computePower(double utilization) { + utilization = clampUtilization(utilization); + + return idlePower + factor * Math.sqrt(utilization); + } + + @Override + public String getName() { + return "SqrtPowerModel"; + } + + @Override + public String getFullName() { + return ("sqrtPowerModel-" + idlePower + "-" + maxPower); + } + } + + private static final class LinearPowerModel extends MaxIdlePowerModel { + private final double factor; + + LinearPowerModel(double maxPower, double idlePower) { + super(maxPower, idlePower); + this.factor = maxPower - idlePower; + } + + @Override + public double computePower(double utilization) { + utilization = clampUtilization(utilization); + + return idlePower + factor * utilization; + } + + @Override + public String getName() { + return "LinearPowerModel"; + } + + @Override + public String getFullName() { + return ("linearPowerModel-" + idlePower + "-" + maxPower); + } + } + + private static final class SquarePowerModel extends MaxIdlePowerModel { + private final double factor; + + SquarePowerModel(double maxPower, double idlePower) { + super(maxPower, idlePower); + this.factor = (maxPower - idlePower); + } + + @Override + public double computePower(double utilization) { + utilization = clampUtilization(utilization); + + return idlePower + factor * Math.pow(utilization, 2); + } + + @Override + public String getName() { + return "SquarePowerModel"; + } + + @Override + public String getFullName() { + return ("squarePowerModel-" + idlePower + "-" + maxPower); + } + } + + private static final class CubicPowerModel extends MaxIdlePowerModel { + private final double factor; + + CubicPowerModel(double maxPower, double idlePower) { + super(maxPower, idlePower); + this.factor = (maxPower - idlePower); + } + + @Override + public double computePower(double utilization) { + utilization = clampUtilization(utilization); + + return idlePower + factor * Math.pow(utilization, 3); + } + + @Override + public String getName() { + return "CubicPowerModel"; + } + + @Override + public String getFullName() { + return ("cubicPowerModel-" + idlePower + "-" + maxPower); + } + } + + 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 + + "]"; + } + + @Override + public String getName() { + return "MsePowerModel"; + } + } + + 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 + "]"; + } + + @Override + public String getName() { + return "AsymptoticPowerModel"; + } + } + + 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) + "]"; + } + + @Override + public String getName() { + return "InterpolationPowerModel"; + } + } + + 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 + "]"; + } + + @Override + public String getName() { + return "ZeroIdlePowerDecorator"; + } + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/cpu/SimCpu.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/cpu/SimCpu.java new file mode 100644 index 00000000..60c877e9 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/cpu/SimCpu.java @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2024 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.cpu; + +import org.opendc.simulator.compute.machine.PerformanceCounters; +import org.opendc.simulator.compute.models.CpuModel; +import org.opendc.simulator.engine.FlowConsumer; +import org.opendc.simulator.engine.FlowEdge; +import org.opendc.simulator.engine.FlowGraph; +import org.opendc.simulator.engine.FlowNode; +import org.opendc.simulator.engine.FlowSupplier; + +/** + * A {@link SimCpu} of a machine. + */ +public final class SimCpu extends FlowNode implements FlowSupplier, FlowConsumer { + private final CpuModel cpuModel; + + private final CpuPowerModel cpuPowerModel; + + private float currentCpuDemand = 0.0f; // cpu capacity demanded by the mux + private float currentCpuUtilization = 0.0f; + private float currentPowerDemand = 0.0f; // power demanded of the psu + private float currentCpuSupplied = 0.0f; // cpu capacity supplied to the mux + private float currentPowerSupplied = 0.0f; // cpu capacity supplied by the psu + + private float maxCapacity; + + private PerformanceCounters performanceCounters = new PerformanceCounters(); + private long lastCounterUpdate; + private final float cpuFrequencyInv; + + private FlowEdge muxEdge; + private FlowEdge psuEdge; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Basic Getters and Setters + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public double getFrequency() { + return cpuModel.getTotalCapacity(); + } + + public void setFrequency(double frequency) { + // Clamp the capacity of the CPU between [0.0, maxFreq] + frequency = Math.max(0, Math.min(this.maxCapacity, frequency)); + // psu.setCpuFrequency(muxInPort, frequency); + } + + @Override + public float getCapacity() { + return maxCapacity; + } + + public PerformanceCounters getPerformanceCounters() { + return performanceCounters; + } + + public double getPowerDraw() { + return this.currentPowerSupplied; + } + + public double getDemand() { + return this.currentCpuDemand; + } + + public double getSpeed() { + return this.currentCpuSupplied; + } + + public CpuModel getCpuModel() { + return cpuModel; + } + + @Override + public String toString() { + return "SimBareMetalMachine.Cpu[model=" + cpuModel + "]"; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public SimCpu(FlowGraph graph, CpuModel cpuModel, int id) { + super(graph); + this.cpuModel = cpuModel; + this.maxCapacity = this.cpuModel.getTotalCapacity(); + + // TODO: connect this to the front-end + this.cpuPowerModel = CpuPowerModels.linear(400, 200); + + this.lastCounterUpdate = graph.getEngine().getClock().millis(); + + this.cpuFrequencyInv = 1 / this.maxCapacity; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // FlowNode related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public long onUpdate(long now) { + updateCounters(now); + + // Calculate Power Demand and send to PSU + // TODO: look at the float / double thing + float powerDemand = (float) this.cpuPowerModel.computePower((double) this.currentCpuUtilization); + + if (powerDemand != this.currentPowerDemand) { + this.pushDemand(this.psuEdge, powerDemand); + this.currentPowerDemand = powerDemand; + } + + // Calculate the amount of cpu this can provide + // TODO: This should be based on the provided power + float cpuSupply = this.currentCpuDemand; + + if (cpuSupply != this.currentCpuSupplied) { + this.pushSupply(this.muxEdge, cpuSupply); + this.currentCpuSupplied = cpuSupply; + } + + return Long.MAX_VALUE; + } + + public void updateCounters() { + this.updateCounters(this.clock.millis()); + } + + /** + * Update the performance counters of the CPU. + * + * @param now The timestamp at which to update the counter. + */ + public void updateCounters(long now) { + long lastUpdate = this.lastCounterUpdate; + this.lastCounterUpdate = now; + long delta = now - lastUpdate; + + if (delta > 0) { + float demand = this.currentCpuDemand; + float rate = this.currentCpuSupplied; + float capacity = this.maxCapacity; + + final float factor = this.cpuFrequencyInv * delta; + + this.performanceCounters.addCpuActiveTime(Math.round(rate * factor)); + this.performanceCounters.addCpuIdleTime(Math.round((capacity - rate) * factor)); + this.performanceCounters.addCpuStealTime(Math.round((demand - rate) * factor)); + } + + this.performanceCounters.setCpuDemand(this.currentCpuDemand); + this.performanceCounters.setCpuSupply(this.currentCpuSupplied); + this.performanceCounters.setCpuCapacity(this.maxCapacity); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // FlowGraph Related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Push new demand to the psu + */ + @Override + public void pushDemand(FlowEdge supplierEdge, float newPowerDemand) { + this.psuEdge.pushDemand(newPowerDemand); + } + + /** + * Push updated supply to the mux + */ + @Override + public void pushSupply(FlowEdge consumerEdge, float newCpuSupply) { + updateCounters(); + this.currentCpuSupplied = newCpuSupply; + this.muxEdge.pushSupply(newCpuSupply); + } + + /** + * Handle new demand coming in from the mux + */ + @Override + public void handleDemand(FlowEdge consumerEdge, float newCpuDemand) { + if (newCpuDemand == this.currentCpuDemand) { + return; + } + + updateCounters(); + this.currentCpuDemand = newCpuDemand; + this.currentCpuUtilization = this.currentCpuDemand / this.maxCapacity; + + this.invalidate(); + } + + /** + * Handle updated supply from the psu + */ + @Override + public void handleSupply(FlowEdge supplierEdge, float newPowerSupply) { + // TODO: Implement this + updateCounters(); + this.currentPowerSupplied = newPowerSupply; + + this.invalidate(); + } + + /** + * Add a connection to the mux + */ + @Override + public void addConsumerEdge(FlowEdge consumerEdge) { + this.muxEdge = consumerEdge; + } + + /** + * Add a connection to the psu + */ + @Override + public void addSupplierEdge(FlowEdge supplierEdge) { + this.psuEdge = supplierEdge; + } + + /** + * Remove the connection to the mux + */ + @Override + public void removeConsumerEdge(FlowEdge consumerEdge) { + this.muxEdge = null; + this.invalidate(); + } + + /** + * Remove the connection to the psu + */ + @Override + public void removeSupplierEdge(FlowEdge supplierEdge) { + this.psuEdge = null; + this.invalidate(); + } +} 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 deleted file mode 100644 index 1c16ceff..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimNetworkAdapter.java +++ /dev/null @@ -1,36 +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.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 deleted file mode 100644 index 40bd268b..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimPeripheral.java +++ /dev/null @@ -1,33 +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.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 deleted file mode 100644 index 42750b0f..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisor.java +++ /dev/null @@ -1,933 +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; - -import java.time.InstantSource; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.SplittableRandom; -import java.util.function.Consumer; -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.Cpu; -import org.opendc.simulator.compute.model.MachineModel; -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 SimHyperVisorContext activeContext; - private final ArrayList vms = new ArrayList<>(); - private final HvCounters counters = new HvCounters(); - - @Override - public void setOffset(long now) {} - - /** - * 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"); - } - - SimVirtualMachine vm = new SimVirtualMachine(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. - ((SimVirtualMachine) machine).close(); - } - } - - /** - * Return the CPU capacity of the hypervisor in MHz. - */ - public double getCpuCapacity() { - final SimHyperVisorContext context = activeContext; - - if (context == null) { - return 0.0; - } - - return context.previousCapacity; - } - - /** - * The CPU demand of the hypervisor in MHz. - */ - public double getCpuDemand() { - final SimHyperVisorContext context = activeContext; - - if (context == null) { - return 0.0; - } - - return context.previousDemand; - } - - /** - * The CPU usage of the hypervisor in MHz. - */ - public double getCpuUsage() { - final SimHyperVisorContext 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 SimHyperVisorContext context = activeContext; - if (context == null) { - return false; - } - - final FlowMultiplexer multiplexer = context.multiplexer; - return (multiplexer.getMaxInputs() - multiplexer.getInputCount()) >= 1; - } - - @Override - public void onStart(SimMachineContext ctx) { - final SimHyperVisorContext context = - new SimHyperVisorContext(ctx, muxFactory, scalingGovernorFactory, counters); - context.start(); - activeContext = context; - } - - @Override - public void onStop(SimMachineContext ctx) { - final SimHyperVisorContext context = activeContext; - if (context != null) { - activeContext = null; - context.stop(); - } - } - - @Override - public void makeSnapshot(long now) { - throw new UnsupportedOperationException("Unable to snapshot hypervisor"); - } - - @Override - public SimWorkload getSnapshot() { - throw new UnsupportedOperationException("Unable to snapshot hypervisor"); - } - - @Override - public void createCheckpointModel() { - throw new UnsupportedOperationException("Unable to create a checkpointing system for a hypervisor"); - } - - @Override - public long getCheckpointInterval() { - return -1; - } - - @Override - public long getCheckpointDuration() { - return -1; - } - - @Override - public double getCheckpointIntervalScaling() { - return -1; - } - - /** - * The context which carries the state when the hypervisor is running on a machine. - */ - private static final class SimHyperVisorContext implements FlowStageLogic { - private final SimMachineContext ctx; - private final FlowMultiplexer multiplexer; - private final FlowStage stage; - private final ScalingGovernor scalingGovernor; - private final InstantSource clock; - private final HvCounters counters; - - private long lastCounterUpdate; - private final double d; - private float previousDemand; - private float previousRate; - private float previousCapacity; - - private SimHyperVisorContext( - 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(); - - final SimProcessingUnit cpu = ctx.getCpu(); - - if (scalingGovernorFactory != null) { - this.scalingGovernor = scalingGovernorFactory.newGovernor(new ScalingPolicyImpl(cpu)); - } else { - this.scalingGovernor = null; - } - - this.d = 1 / cpu.getFrequency(); - } - - /** - * Start the hypervisor on a new machine. - */ - void start() { - final FlowGraph graph = ctx.getGraph(); - final FlowMultiplexer multiplexer = this.multiplexer; - - graph.connect(multiplexer.newOutput(), ctx.getCpu().getInput()); - - if (this.scalingGovernor != null) { - this.scalingGovernor.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 ScalingGovernor scalingGovernors = this.scalingGovernor; - - 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 (scalingGovernor != null) { - scalingGovernor.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.getCpuModel().getTotalCapacity(); - } - } - - /** - * A virtual machine running on the hypervisor. - */ - public class SimVirtualMachine extends SimAbstractMachine { - private boolean isClosed; - private final VmCounters counters = new VmCounters(this); - - private SimVirtualMachine(MachineModel model) { - super(model); - } - - public SimHypervisorCounters getCounters() { - return counters; - } - - public double getCpuDemand() { - final VmContext context = (VmContext) getActiveContext(); - - if (context == null) { - return 0.0; - } - - return context.previousDemand; - } - - public double getCpuUsage() { - final VmContext context = (VmContext) getActiveContext(); - - if (context == null) { - return 0.0; - } - - return context.usage; - } - - 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 SimAbstractMachineContext createContext( - SimWorkload workload, Map meta, Consumer completion) { - if (isClosed) { - throw new IllegalStateException("Virtual machine does not exist anymore"); - } - - final SimHyperVisorContext context = activeContext; - if (context == null) { - throw new IllegalStateException("Hypervisor is inactive"); - } - - return new VmContext( - context, - this, - random, - interferenceDomain, - counters, - SimHypervisor.this.counters, - workload, - meta, - completion); - } - - @Override - public SimAbstractMachineContext getActiveContext() { - return super.getActiveContext(); - } - - void close() { - if (isClosed) { - return; - } - - isClosed = true; - cancel(); - } - } - - /** - * A {@link SimAbstractMachine.SimAbstractMachineContext} for a virtual machine instance. - */ - private static final class VmContext extends SimAbstractMachine.SimAbstractMachineContext - implements FlowStageLogic { - private final SimHyperVisorContext simHyperVisorContext; - 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 InstantSource clock; - - private final VCpu cpu; - 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( - SimHyperVisorContext simHyperVisorContext, - SimVirtualMachine machine, - SplittableRandom random, - VmInterferenceDomain interferenceDomain, - VmCounters vmCounters, - HvCounters hvCounters, - SimWorkload workload, - Map meta, - Consumer completion) { - super(machine, workload, meta, completion); - - this.simHyperVisorContext = simHyperVisorContext; - this.random = random; - this.vmCounters = vmCounters; - this.hvCounters = hvCounters; - this.clock = simHyperVisorContext.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 = simHyperVisorContext.ctx.getGraph(); - final FlowStage stage = graph.newStage(this); - this.stage = stage; - this.lastUpdate = clock.millis(); - this.lastCounterUpdate = clock.millis(); - - final FlowMultiplexer multiplexer = simHyperVisorContext.multiplexer; - this.multiplexer = multiplexer; - - final MachineModel model = machine.getModel(); - final Cpu cpuModel = model.getCpu(); - final Inlet[] muxInlets = new Inlet[1]; - - this.muxInlets = muxInlets; - - final Inlet muxInlet = multiplexer.newInput(); - muxInlets[0] = muxInlet; - - final InPort input = stage.getInlet("cpu"); - final OutPort output = stage.getOutlet("mux"); - - final Handler handler = new Handler(this, input, output); - input.setHandler(handler); - output.setHandler(handler); - - this.cpu = new VCpu(cpuModel, input); - - graph.connect(output, muxInlet); - - this.d = 1 / cpuModel.getTotalCapacity(); - - 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; // time between updates - - 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; // time between divided by total capacity - 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 SimProcessingUnit getCpu() { - return cpu; - } - - @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) - simHyperVisorContext.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 Cpu model; - private final InPort input; - - private VCpu(Cpu model, InPort input) { - this.model = model; - this.input = input; - - input.pull((float) model.getTotalCapacity()); - } - - @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 Cpu getCpuModel() { - 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 SimHyperVisorContext context = activeContext; - - if (context != null) { - context.updateCounters(); - } - } - } - - /** - * Implementation of {@link SimHypervisorCounters} for the virtual machine. - */ - private static class VmCounters implements SimHypervisorCounters { - private final SimVirtualMachine vm; - private long cpuActiveTime; - private long cpuIdleTime; - private long cpuStealTime; - private long cpuLostTime; - - private VmCounters(SimVirtualMachine 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 deleted file mode 100644 index fc77e9d6..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisorCounters.java +++ /dev/null @@ -1,53 +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; - -/** - * 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/cpufreq/ScalingGovernor.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernor.java deleted file mode 100644 index 69a371e1..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernor.java +++ /dev/null @@ -1,46 +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.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 deleted file mode 100644 index 97a49879..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernorFactory.java +++ /dev/null @@ -1,33 +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.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 deleted file mode 100644 index 2b10ae59..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernors.java +++ /dev/null @@ -1,190 +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.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 deleted file mode 100644 index 0cdb7a0b..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingPolicy.java +++ /dev/null @@ -1,56 +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.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 deleted file mode 100644 index cc671379..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.java +++ /dev/null @@ -1,136 +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.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 deleted file mode 100644 index 64cd5077..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceMember.java +++ /dev/null @@ -1,177 +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.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 deleted file mode 100644 index e2093266..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.java +++ /dev/null @@ -1,185 +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.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 deleted file mode 100644 index 3f0c0a88..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceProfile.java +++ /dev/null @@ -1,60 +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. - */ -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/machine/PerformanceCounters.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/machine/PerformanceCounters.java new file mode 100644 index 00000000..b1e30e5c --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/machine/PerformanceCounters.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2024 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.machine; + +public class PerformanceCounters { + private long cpuActiveTime = 0; + private long cpuIdleTime = 0; + private long cpuStealTime = 0; + private long cpuLostTime = 0; + + private float cpuCapacity = 0.0f; + private float cpuDemand = 0.0f; + private float cpuSupply = 0.0f; + + public long getCpuActiveTime() { + return cpuActiveTime; + } + + public void setCpuActiveTime(long cpuActiveTime) { + this.cpuActiveTime = cpuActiveTime; + } + + public void addCpuActiveTime(long cpuActiveTime) { + this.cpuActiveTime += cpuActiveTime; + } + + public long getCpuIdleTime() { + return cpuIdleTime; + } + + public void setCpuIdleTime(long cpuIdleTime) { + this.cpuIdleTime = cpuIdleTime; + } + + public void addCpuIdleTime(long cpuIdleTime) { + this.cpuIdleTime += cpuIdleTime; + } + + public long getCpuStealTime() { + return cpuStealTime; + } + + public void setCpuStealTime(long cpuStealTime) { + this.cpuStealTime = cpuStealTime; + } + + public void addCpuStealTime(long cpuStealTime) { + this.cpuStealTime += cpuStealTime; + } + + public long getCpuLostTime() { + return cpuLostTime; + } + + public void setCpuLostTime(long cpuLostTime) { + this.cpuLostTime = cpuLostTime; + } + + public float getCpuCapacity() { + return cpuCapacity; + } + + public void setCpuCapacity(float cpuCapacity) { + this.cpuCapacity = cpuCapacity; + } + + public float getCpuDemand() { + return cpuDemand; + } + + public void setCpuDemand(float cpuDemand) { + this.cpuDemand = cpuDemand; + } + + public float getCpuSupply() { + return cpuSupply; + } + + public void setCpuSupply(float cpuSupply) { + this.cpuSupply = cpuSupply; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/machine/SimMachine.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/machine/SimMachine.java new file mode 100644 index 00000000..00a69efe --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/machine/SimMachine.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2024 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.machine; + +import java.time.InstantSource; +import java.util.function.Consumer; +import org.opendc.simulator.Multiplexer; +import org.opendc.simulator.compute.cpu.CpuPowerModel; +import org.opendc.simulator.compute.cpu.SimCpu; +import org.opendc.simulator.compute.memory.Memory; +import org.opendc.simulator.compute.models.MachineModel; +import org.opendc.simulator.compute.power.SimPsu; +import org.opendc.simulator.compute.workload.SimWorkload; +import org.opendc.simulator.compute.workload.Workload; +import org.opendc.simulator.engine.FlowGraph; + +/** + * A machine that is able to execute {@link SimWorkload} objects. + */ +public class SimMachine { + private final MachineModel machineModel; + private final FlowGraph graph; + + private final InstantSource clock; + + private SimCpu cpu; + private Multiplexer cpuMux; + private SimPsu psu; + private Memory memory; + + private Consumer completion; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Basic Getters and Setters + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public PerformanceCounters getPerformanceCounters() { + return this.cpu.getPerformanceCounters(); + } + + public MachineModel getMachineModel() { + return machineModel; + } + + public FlowGraph getGraph() { + return graph; + } + + public InstantSource getClock() { + return clock; + } + + public SimCpu getCpu() { + return cpu; + } + + public Multiplexer getCpuMux() { + return cpuMux; + } + + public Memory getMemory() { + return memory; + } + + public SimPsu getPsu() { + return psu; + } + + /** + * Return the CPU capacity of the hypervisor in MHz. + */ + public double getCpuCapacity() { + return 0.0; + } + + /** + * The CPU demand of the hypervisor in MHz. + */ + public double getCpuDemand() { + return 0.0; + } + + /** + * The CPU usage of the hypervisor in MHz. + */ + public double getCpuUsage() { + return 0.0; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public SimMachine( + FlowGraph graph, MachineModel machineModel, CpuPowerModel cpuPowerModel, Consumer completion) { + this.graph = graph; + this.machineModel = machineModel; + this.clock = graph.getEngine().getClock(); + + // Create the psu and cpu and connect them + this.psu = new SimPsu(graph); + this.cpu = new SimCpu(graph, this.machineModel.getCpu(), 0); + + graph.addEdge(this.cpu, this.psu); + + this.memory = new Memory(graph, this.machineModel.getMemory()); + + // Create a Multiplexer and add the cpu as supplier + this.cpuMux = new Multiplexer(this.graph); + graph.addEdge(this.cpuMux, this.cpu); + + this.completion = completion; + } + + public void shutdown() { + shutdown(null); + } + + /** + * Close all related hardware + */ + public void shutdown(Exception cause) { + this.graph.removeNode(this.psu); + this.psu = null; + + this.graph.removeNode(this.cpu); + this.cpu = null; + + this.graph.removeNode(this.cpuMux); + this.cpuMux = null; + + this.memory = null; + + this.completion.accept(cause); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Workload related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Determine whether the specified machine characterized by model can fit on this hypervisor at this + * moment. + * TODO: This currently alwasy returns True, maybe remove? + */ + public boolean canFit(MachineModel model) { + return true; + } + + /** + * Create a Virtual Machine, and start the given workload on it. + * + * @param workload + * @param completion + * @return + */ + public VirtualMachine startWorkload(Workload workload, Consumer completion) { + final VirtualMachine vm = new VirtualMachine(this); + + vm.startWorkload(workload, completion); + + return vm; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/machine/VirtualMachine.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/machine/VirtualMachine.java new file mode 100644 index 00000000..3bc3d2b4 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/machine/VirtualMachine.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2024 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.machine; + +import java.util.function.Consumer; +import org.opendc.simulator.compute.cpu.SimCpu; +import org.opendc.simulator.compute.workload.SimWorkload; +import org.opendc.simulator.compute.workload.Workload; +import org.opendc.simulator.engine.FlowConsumer; +import org.opendc.simulator.engine.FlowEdge; +import org.opendc.simulator.engine.FlowGraph; +import org.opendc.simulator.engine.FlowNode; +import org.opendc.simulator.engine.FlowSupplier; + +/* + A virtual Machine created to run a single workload +*/ +public class VirtualMachine extends FlowNode implements FlowConsumer, FlowSupplier { + private SimMachine machine; + + private SimWorkload activeWorkload; + + private long lastUpdate; + private final double d; + + private FlowEdge cpuEdge; // The edge to the cpu + private FlowEdge workloadEdge; // The edge to the workload + + private float cpuDemand; + private float cpuSupply; + private float cpuCapacity; + + private PerformanceCounters performanceCounters = new PerformanceCounters(); + + private Consumer completion; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Basic Getters and Setters + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public PerformanceCounters getPerformanceCounters() { + return performanceCounters; + } + + public SimWorkload getActiveWorkload() { + return activeWorkload; + } + + public float getDemand() { + return cpuDemand; + } + + public void setDemand(float demand) { + this.cpuDemand = demand; + } + + public float getCpuCapacity() { + return cpuCapacity; + } + + public void setCpuCapacity(float cpuCapacity) { + this.cpuCapacity = cpuCapacity; + } + + public FlowGraph getGraph() { + return this.parentGraph; + } + + public SimCpu getCpu() { + return machine.getCpu(); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public VirtualMachine(SimMachine machine) { + super(machine.getGraph()); + this.machine = machine; + this.clock = this.machine.getClock(); + + this.parentGraph = machine.getGraph(); + this.parentGraph.addEdge(this, this.machine.getCpuMux()); + + this.lastUpdate = clock.millis(); + this.lastUpdate = clock.millis(); + + this.d = 1 / machine.getCpu().getFrequency(); + } + + public void shutdown() { + this.shutdown(null); + } + + public void shutdown(Exception cause) { + if (this.nodeState == NodeState.CLOSED) { + return; + } + + super.closeNode(); + + this.activeWorkload = null; + this.performanceCounters = null; + + this.completion.accept(cause); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Workload related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public void startWorkload(Workload workload, Consumer completion) { + this.completion = completion; + this.activeWorkload = workload.startWorkload(this, this.clock.millis()); + } + + public void updateCounters(long now) { + long lastUpdate = this.lastUpdate; + this.lastUpdate = now; + long delta = now - lastUpdate; + + if (delta > 0) { + final double factor = this.d * delta; + + this.performanceCounters.addCpuActiveTime(Math.round(this.cpuSupply * factor)); + this.performanceCounters.setCpuIdleTime(Math.round((this.cpuCapacity - this.cpuSupply) * factor)); + this.performanceCounters.addCpuStealTime(Math.round((this.cpuDemand - this.cpuSupply) * factor)); + } + + this.performanceCounters.setCpuDemand(this.cpuDemand); + this.performanceCounters.setCpuSupply(this.cpuSupply); + this.performanceCounters.setCpuCapacity(this.cpuCapacity); + } + + @Override + public long onUpdate(long now) { + updateCounters(now); + + return Long.MAX_VALUE; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // FlowGraph Related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Add an edge to the workload + * TODO: maybe add a check if there is already an edge + */ + @Override + public void addConsumerEdge(FlowEdge consumerEdge) { + this.workloadEdge = consumerEdge; + } + + /** + * Add an edge to the cpuMux + * TODO: maybe add a check if there is already an edge + */ + @Override + public void addSupplierEdge(FlowEdge supplierEdge) { + this.cpuEdge = supplierEdge; + } + + /** + * Push demand to the cpuMux if the demand has changed + **/ + @Override + public void pushDemand(FlowEdge supplierEdge, float newDemand) { + this.cpuEdge.pushDemand(newDemand); + } + + /** + * Push supply to the workload if the supply has changed + **/ + @Override + public void pushSupply(FlowEdge consumerEdge, float newSupply) { + this.workloadEdge.pushDemand(newSupply); + } + + /** + * Handle new demand from the workload by sending it through to the cpuMux + **/ + @Override + public void handleDemand(FlowEdge consumerEdge, float newDemand) { + if (this.cpuDemand == newDemand) { + return; + } + + updateCounters(this.clock.millis()); + this.cpuDemand = newDemand; + + pushDemand(this.cpuEdge, newDemand); + } + + /** + * Handle a new supply pushed by the cpuMux by sending it through to the workload + **/ + @Override + public void handleSupply(FlowEdge supplierEdge, float newCpuSupply) { + if (newCpuSupply == this.cpuSupply) { + return; + } + + updateCounters(this.clock.millis()); + this.cpuSupply = newCpuSupply; + + pushSupply(this.workloadEdge, newCpuSupply); + } + + @Override + public void removeConsumerEdge(FlowEdge consumerEdge) { + this.workloadEdge = null; + this.shutdown(); + } + + @Override + public float getCapacity() { + return this.cpuCapacity; + } + + @Override + public void removeSupplierEdge(FlowEdge supplierEdge) { + this.cpuEdge = null; + this.shutdown(); + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/memory/Memory.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/memory/Memory.java new file mode 100644 index 00000000..2656a99a --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/memory/Memory.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024 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.memory; + +import org.opendc.simulator.compute.models.MemoryUnit; +import org.opendc.simulator.engine.FlowGraph; + +/** + * The [SimMemory] implementation for a machine. + */ +public final class Memory { + // private final SimpleFlowSink sink; + private final MemoryUnit memoryUnit; + + public Memory(FlowGraph graph, MemoryUnit memoryUnit) { + + this.memoryUnit = memoryUnit; + // TODO: Fix this + // this.sink = new SimpleFlowSink(graph, (float) memoryUnit.getSize()); + } + + public double getCapacity() { + // return sink.getCapacity(); + return 0.0f; + } + + public MemoryUnit getMemoryUnit() { + return memoryUnit; + } + + @Override + public String toString() { + return "SimAbstractMachine.Memory"; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/Cpu.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/Cpu.java deleted file mode 100644 index c319ae1a..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/Cpu.java +++ /dev/null @@ -1,137 +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.model; - -import java.util.Objects; - -/** - * A single logical compute unit of processor node, either virtual or physical. - */ -public final class Cpu { - private final int id; - private final int coreCount; - private final double coreSpeed; - private final double totalCapacity; - - private final String vendor; - private final String modelName; - private final String arch; - - /** - * Construct a {@link Cpu} instance. - * - * @param id The identifier of the CPU core within the processing node. - * @param coreCount The number of cores present in the CPU - * @param coreSpeed The speed of a single core - * @param vendor The vendor of the CPU - * @param modelName The name of the CPU - * @param arch The architecture of the CPU - */ - public Cpu(int id, int coreCount, double coreSpeed, String vendor, String modelName, String arch) { - this.id = id; - this.coreCount = coreCount; - this.coreSpeed = coreSpeed; - this.totalCapacity = coreSpeed * coreCount; - this.vendor = vendor; - this.modelName = modelName; - this.arch = arch; - } - - public Cpu(int id, int coreCount, double coreSpeed) { - this(id, coreCount, coreSpeed, "unkown", "unkown", "unkown"); - } - - /** - * Return the identifier of the CPU core within the processing node. - */ - public int getId() { - return id; - } - - /** - * Return the number of logical CPUs in the processor node. - */ - public int getCoreCount() { - return coreCount; - } - - /** - * Return the clock rate of a single core of the CPU MHz. - */ - public double getCoreSpeed() { - return coreSpeed; - } - - /** - * Return the clock rate of the CPU in MHz. - */ - public double getTotalCapacity() { - return totalCapacity; - } - - /** - * 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; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Cpu that = (Cpu) o; - return id == that.id - && Double.compare(that.totalCapacity, totalCapacity) == 0 - && Double.compare(that.coreSpeed, coreSpeed) == 0 - && Objects.equals(vendor, that.vendor) - && Objects.equals(modelName, that.modelName) - && Objects.equals(arch, that.arch); - } - - @Override - public int hashCode() { - return Objects.hash(id, coreCount, coreSpeed, totalCapacity, vendor, modelName, arch); - } - - @Override - public String toString() { - return "ProcessingUnit[" + "id= " + id + ", coreCount= " + coreCount + ", coreSpeed= " + coreSpeed - + ", frequency= " + totalCapacity + ", vendor= " + vendor + ", modelName= " + modelName + ", arch= " - + arch + "]"; - } -} 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 deleted file mode 100644 index e4019dac..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/MachineModel.java +++ /dev/null @@ -1,139 +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.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 Cpu cpu; - private final MemoryUnit memory; - private final List net; - private final List storage; - - /** - * Construct a {@link MachineModel} instance. - * - * @param cpu The cpu 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(Cpu cpu, MemoryUnit memory, Iterable net, Iterable storage) { - this.cpu = cpu; - - this.memory = memory; - - this.net = new ArrayList<>(); - net.forEach(this.net::add); - - this.storage = new ArrayList<>(); - storage.forEach(this.storage::add); - } - - public MachineModel(Cpu cpu, MemoryUnit memory) { - this(cpu, memory, Collections.emptyList(), Collections.emptyList()); - } - - /** - * Construct a {@link MachineModel} instance. - * A list of the same cpus, are automatically converted to a single CPU with the number of cores of - * all cpus in the list combined. - * - * @param cpus The list of processing units available to the image. - * @param memory The list of memory units available to the image. - */ - public MachineModel( - List cpus, MemoryUnit memory, Iterable net, Iterable storage) { - - this( - new Cpu( - cpus.get(0).getId(), - cpus.get(0).getCoreCount() * cpus.size(), - cpus.get(0).getCoreSpeed(), - cpus.get(0).getVendor(), - cpus.get(0).getModelName(), - cpus.get(0).getArchitecture()), - memory, - net, - storage); - } - - public MachineModel(List cpus, MemoryUnit memory) { - this(cpus, memory, Collections.emptyList(), Collections.emptyList()); - } - - /** - * Return the processing units of this machine. - */ - public Cpu getCpu() { - return this.cpu; - } - - /** - * Return the memory units of this machine. - */ - public MemoryUnit getMemory() { - return 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 cpu.equals(that.cpu) - && memory.equals(that.memory) - && net.equals(that.net) - && storage.equals(that.storage); - } - - @Override - public int hashCode() { - return Objects.hash(cpu, memory, net, storage); - } - - @Override - public String toString() { - return "MachineModel[cpus=" + cpu + ",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 deleted file mode 100644 index dbd3f89a..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/MemoryUnit.java +++ /dev/null @@ -1,100 +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.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 MiBs. - */ - 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 deleted file mode 100644 index ff3daa40..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/NetworkAdapter.java +++ /dev/null @@ -1,88 +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.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 deleted file mode 100644 index 01a87b96..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/ProcessingNode.java +++ /dev/null @@ -1,100 +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.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/StorageDevice.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/StorageDevice.java deleted file mode 100644 index 549ccc7e..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/StorageDevice.java +++ /dev/null @@ -1,112 +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.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/models/CpuModel.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/models/CpuModel.java new file mode 100644 index 00000000..88e17941 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/models/CpuModel.java @@ -0,0 +1,137 @@ +/* + * 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.models; + +import java.util.Objects; + +/** + * A single logical compute unit of processor node, either virtual or physical. + */ +public final class CpuModel { + private final int id; + private final int coreCount; + private final float coreSpeed; + private final float totalCapacity; + + private final String vendor; + private final String modelName; + private final String arch; + + /** + * Construct a {@link CpuModel} instance. + * + * @param id The identifier of the CPU core within the processing node. + * @param coreCount The number of cores present in the CPU + * @param coreSpeed The speed of a single core + * @param vendor The vendor of the CPU + * @param modelName The name of the CPU + * @param arch The architecture of the CPU + */ + public CpuModel(int id, int coreCount, float coreSpeed, String vendor, String modelName, String arch) { + this.id = id; + this.coreCount = coreCount; + this.coreSpeed = coreSpeed; + this.totalCapacity = coreSpeed * coreCount; + this.vendor = vendor; + this.modelName = modelName; + this.arch = arch; + } + + public CpuModel(int id, int coreCount, float coreSpeed) { + this(id, coreCount, coreSpeed, "unkown", "unkown", "unkown"); + } + + /** + * Return the identifier of the CPU core within the processing node. + */ + public int getId() { + return id; + } + + /** + * Return the number of logical CPUs in the processor node. + */ + public int getCoreCount() { + return coreCount; + } + + /** + * Return the clock rate of a single core of the CPU MHz. + */ + public float getCoreSpeed() { + return coreSpeed; + } + + /** + * Return the clock rate of the CPU in MHz. + */ + public float getTotalCapacity() { + return totalCapacity; + } + + /** + * 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; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CpuModel that = (CpuModel) o; + return id == that.id + && Double.compare(that.totalCapacity, totalCapacity) == 0 + && Double.compare(that.coreSpeed, coreSpeed) == 0 + && Objects.equals(vendor, that.vendor) + && Objects.equals(modelName, that.modelName) + && Objects.equals(arch, that.arch); + } + + @Override + public int hashCode() { + return Objects.hash(id, coreCount, coreSpeed, totalCapacity, vendor, modelName, arch); + } + + @Override + public String toString() { + return "ProcessingUnit[" + "id= " + id + ", coreCount= " + coreCount + ", coreSpeed= " + coreSpeed + + ", frequency= " + totalCapacity + ", vendor= " + vendor + ", modelName= " + modelName + ", arch= " + + arch + "]"; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/models/MachineModel.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/models/MachineModel.java new file mode 100644 index 00000000..d6d139d7 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/models/MachineModel.java @@ -0,0 +1,98 @@ +/* + * 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.models; + +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 CpuModel cpuModel; + private final MemoryUnit memory; + + /** + * Construct a {@link MachineModel} instance. + * + * @param cpuModel The cpu available to the image. + * @param memory The list of memory units available to the image. + */ + public MachineModel(CpuModel cpuModel, MemoryUnit memory) { + this.cpuModel = cpuModel; + this.memory = memory; + } + + /** + * Construct a {@link MachineModel} instance. + * A list of the same cpus, are automatically converted to a single CPU with the number of cores of + * all cpus in the list combined. + * + * @param cpus The list of processing units available to the image. + * @param memory The list of memory units available to the image. + */ + public MachineModel(List cpus, MemoryUnit memory) { + + this( + new CpuModel( + cpus.get(0).getId(), + cpus.get(0).getCoreCount() * cpus.size(), + cpus.get(0).getCoreSpeed(), + cpus.get(0).getVendor(), + cpus.get(0).getModelName(), + cpus.get(0).getArchitecture()), + memory); + } + + /** + * Return the processing units of this machine. + */ + public CpuModel getCpu() { + return this.cpuModel; + } + + /** + * Return the memory units of this machine. + */ + public MemoryUnit getMemory() { + return memory; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MachineModel that = (MachineModel) o; + return cpuModel.equals(that.cpuModel) && memory.equals(that.memory); + } + + @Override + public int hashCode() { + return Objects.hash(cpuModel, memory); + } + + @Override + public String toString() { + return "MachineModel[cpus=" + cpuModel + ",memory=" + memory + "]"; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/models/MemoryUnit.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/models/MemoryUnit.java new file mode 100644 index 00000000..c3af2bcd --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/models/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.models; + +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 MemoryUnit} 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 MiBs. + */ + 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/power/CPUPowerModelsFactory.kt b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CPUPowerModelsFactory.kt deleted file mode 100644 index 2c64944c..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CPUPowerModelsFactory.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2024 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 - -// TODO: couple this correctly -public enum class CPUPowerModel { - Constant, - Sqrt, - Linear, - Square, - Cubic, -} - -public fun getPowerModel( - modelType: String, - power: Double, - maxPower: Double, - idlePower: Double, -): CpuPowerModel { - return when (modelType) { - "constant" -> CpuPowerModels.constant(power) - "sqrt" -> CpuPowerModels.sqrt(maxPower, idlePower) - "linear" -> CpuPowerModels.linear(maxPower, idlePower) - "square" -> CpuPowerModels.square(maxPower, idlePower) - "cubic" -> CpuPowerModels.cubic(maxPower, idlePower) - - else -> throw IllegalArgumentException("Unknown power modelType $modelType") - } -} - -public fun getPowerModel(modelType: String): CpuPowerModel { - return when (modelType) { - "constant" -> CpuPowerModels.constant(200.0) - "sqrt" -> CpuPowerModels.sqrt(350.0, 200.0) - "linear" -> CpuPowerModels.linear(350.0, 200.0) - "square" -> CpuPowerModels.square(350.0, 200.0) - "cubic" -> CpuPowerModels.cubic(350.0, 200.0) - - else -> throw IllegalArgumentException("Unknown power modelType $modelType") - } -} 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 deleted file mode 100644 index 73f9357d..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CpuPowerModel.java +++ /dev/null @@ -1,44 +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.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); - - String getName(); - - default String getFullName() { - return getName(); - } -} 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 deleted file mode 100644 index 4e62e67f..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CpuPowerModels.java +++ /dev/null @@ -1,409 +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.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 of the host 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 host in W. - * @param idlePower The power draw of the host 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 host in W. - * @param idlePower The power draw of the host 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 host in W. - * @param idlePower The power draw of the host 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 host in W. - * @param idlePower The power draw of the host 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 host in W. - * @param idlePower The power draw of the host 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 host in W. - * @param idlePower The power draw of the host at its lowest utilization level in W. - * @param asymUtil A utilization level at which the host 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 + "]"; - } - - @Override - public String getName() { - return "ConstantPowerModel"; - } - } - - 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; - } - - // Clamps the provided utilization in the range of 0.0 and 1.0 - // This is done to avoid floating point errors - public double clampUtilization(double utilization) { - return Math.max(0.0, Math.min(1.0, utilization)); - } - - @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); - } - - @Override - public double computePower(double utilization) { - utilization = clampUtilization(utilization); - - return idlePower + factor * Math.sqrt(utilization); - } - - @Override - public String getName() { - return "SqrtPowerModel"; - } - - @Override - public String getFullName() { - return ("sqrtPowerModel-" + idlePower + "-" + maxPower); - } - } - - private static final class LinearPowerModel extends MaxIdlePowerModel { - private final double factor; - - LinearPowerModel(double maxPower, double idlePower) { - super(maxPower, idlePower); - this.factor = maxPower - idlePower; - } - - @Override - public double computePower(double utilization) { - utilization = clampUtilization(utilization); - - return idlePower + factor * utilization; - } - - @Override - public String getName() { - return "LinearPowerModel"; - } - - @Override - public String getFullName() { - return ("linearPowerModel-" + idlePower + "-" + maxPower); - } - } - - private static final class SquarePowerModel extends MaxIdlePowerModel { - private final double factor; - - SquarePowerModel(double maxPower, double idlePower) { - super(maxPower, idlePower); - this.factor = (maxPower - idlePower); - } - - @Override - public double computePower(double utilization) { - utilization = clampUtilization(utilization); - - return idlePower + factor * Math.pow(utilization, 2); - } - - @Override - public String getName() { - return "SquarePowerModel"; - } - - @Override - public String getFullName() { - return ("squarePowerModel-" + idlePower + "-" + maxPower); - } - } - - private static final class CubicPowerModel extends MaxIdlePowerModel { - private final double factor; - - CubicPowerModel(double maxPower, double idlePower) { - super(maxPower, idlePower); - this.factor = (maxPower - idlePower); - } - - @Override - public double computePower(double utilization) { - utilization = clampUtilization(utilization); - - return idlePower + factor * Math.pow(utilization, 3); - } - - @Override - public String getName() { - return "CubicPowerModel"; - } - - @Override - public String getFullName() { - return ("cubicPowerModel-" + idlePower + "-" + maxPower); - } - } - - 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 - + "]"; - } - - @Override - public String getName() { - return "MsePowerModel"; - } - } - - 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 + "]"; - } - - @Override - public String getName() { - return "AsymptoticPowerModel"; - } - } - - 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) + "]"; - } - - @Override - public String getName() { - return "InterpolationPowerModel"; - } - } - - 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 + "]"; - } - - @Override - public String getName() { - return "ZeroIdlePowerDecorator"; - } - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/SimPowerSource.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/SimPowerSource.java new file mode 100644 index 00000000..9b4d6a33 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/SimPowerSource.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2024 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.time.InstantSource; +import org.opendc.simulator.compute.cpu.SimCpu; +import org.opendc.simulator.engine.FlowEdge; +import org.opendc.simulator.engine.FlowGraph; +import org.opendc.simulator.engine.FlowNode; +import org.opendc.simulator.engine.FlowSupplier; + +/** + * A {@link SimPsu} implementation that estimates the power consumption based on CPU usage. + */ +public final class SimPowerSource extends FlowNode implements FlowSupplier { + private final InstantSource clock; + + private long lastUpdate; + + private float powerDemand = 0.0f; + private float powerSupplied = 0.0f; + private float totalEnergyUsage = 0.0f; + + private FlowEdge cpuEdge; + + private float capacity = Long.MAX_VALUE; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Basic Getters and Setters + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Determine whether the InPort is connected to a {@link SimCpu}. + * + * @return true if the InPort is connected to an OutPort, false otherwise. + */ + public boolean isConnected() { + return cpuEdge != null; + } + + /** + * 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 double getPowerDemand() { + return this.powerDemand; + } + + /** + * Return the instantaneous power usage of the machine (in W) measured at the InPort of the power supply. + */ + public float getPowerDraw() { + return this.powerSupplied; + } + + /** + * Return the cumulated energy usage of the machine (in J) measured at the InPort of the powers supply. + */ + public float getEnergyUsage() { + updateCounters(); + return totalEnergyUsage; + } + + @Override + public float getCapacity() { + return this.capacity; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public SimPowerSource(FlowGraph graph) { + super(graph); + + this.clock = graph.getEngine().getClock(); + + lastUpdate = graph.getEngine().getClock().millis(); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // FlowNode related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public long onUpdate(long now) { + updateCounters(); + float powerSupply = this.powerDemand; + + if (powerSupply != this.powerSupplied) { + this.pushSupply(this.cpuEdge, powerSupply); + } + + return Long.MAX_VALUE; + } + + public void updateCounters() { + updateCounters(clock.millis()); + } + + /** + * Calculate the energy usage up until now. + */ + public void updateCounters(long now) { + long lastUpdate = this.lastUpdate; + this.lastUpdate = now; + + long duration = now - lastUpdate; + if (duration > 0) { + // Compute the energy usage of the machine + this.totalEnergyUsage += (float) (this.powerSupplied * duration * 0.001); + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // FlowGraph Related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void handleDemand(FlowEdge consumerEdge, float newPowerDemand) { + if (newPowerDemand == this.powerDemand) { + return; + } + + this.powerDemand = newPowerDemand; + this.invalidate(); + } + + @Override + public void pushSupply(FlowEdge consumerEdge, float newSupply) { + if (newSupply == this.powerSupplied) { + return; + } + + this.powerSupplied = newSupply; + consumerEdge.pushSupply(newSupply); + } + + @Override + public void addConsumerEdge(FlowEdge consumerEdge) { + this.cpuEdge = consumerEdge; + } + + @Override + public void removeConsumerEdge(FlowEdge consumerEdge) { + this.cpuEdge = null; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/SimPsu.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/SimPsu.java new file mode 100644 index 00000000..8f0fb130 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/SimPsu.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2024 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.cpu.SimCpu; +import org.opendc.simulator.engine.FlowConsumer; +import org.opendc.simulator.engine.FlowEdge; +import org.opendc.simulator.engine.FlowGraph; +import org.opendc.simulator.engine.FlowNode; +import org.opendc.simulator.engine.FlowSupplier; + +/** + * A {@link SimPsu} implementation that estimates the power consumption based on CPU usage. + */ +public final class SimPsu extends FlowNode implements FlowSupplier, FlowConsumer { + private long lastUpdate; + + private float powerDemand = 0.0f; + private float powerSupplied = 0.0f; + private float totalEnergyUsage = 0.0f; + + private FlowEdge cpuEdge; + private FlowEdge powerEdge; + + private float capacity = Long.MAX_VALUE; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Basic Getters and Setters + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Determine whether the InPort is connected to a {@link SimCpu}. + * + * @return true if the InPort is connected to an OutPort, false otherwise. + */ + public boolean isConnected() { + return cpuEdge != null; + } + + /** + * 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 double getPowerDemand() { + return this.powerDemand; + } + + /** + * Return the instantaneous power usage of the machine (in W) measured at the InPort of the power supply. + */ + public float getPowerDraw() { + return this.powerSupplied; + } + + /** + * Return the cumulated energy usage of the machine (in J) measured at the InPort of the powers supply. + */ + public float getEnergyUsage() { + updateCounters(); + return totalEnergyUsage; + } + + @Override + public float getCapacity() { + return this.capacity; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public SimPsu(FlowGraph graph) { + super(graph); + + lastUpdate = this.clock.millis(); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // FlowNode related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public long onUpdate(long now) { + updateCounters(); + float powerSupply = this.powerDemand; + + if (powerSupply != this.powerSupplied) { + this.pushSupply(this.cpuEdge, powerSupply); + } + + return Long.MAX_VALUE; + } + + public void updateCounters() { + updateCounters(clock.millis()); + } + + /** + * Calculate the energy usage up until now. + */ + public void updateCounters(long now) { + long lastUpdate = this.lastUpdate; + this.lastUpdate = now; + + long duration = now - lastUpdate; + if (duration > 0) { + // Compute the energy usage of the psu + this.totalEnergyUsage += (float) (this.powerSupplied * duration * 0.001); + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // FlowGraph Related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void pushDemand(FlowEdge supplierEdge, float newDemand) { + if (newDemand == this.powerDemand) { + return; + } + + this.powerDemand = newDemand; + powerEdge.pushSupply(newDemand); + } + + @Override + public void pushSupply(FlowEdge consumerEdge, float newSupply) { + if (newSupply == this.powerSupplied) { + return; + } + + this.powerSupplied = newSupply; + cpuEdge.pushSupply(newSupply); + } + + @Override + public void handleDemand(FlowEdge consumerEdge, float newPowerDemand) { + if (newPowerDemand == this.powerDemand) { + return; + } + + this.powerDemand = newPowerDemand; + this.invalidate(); + } + + @Override + public void handleSupply(FlowEdge supplierEdge, float newPowerSupply) { + if (newPowerSupply == this.powerSupplied) { + return; + } + + this.powerSupplied = newPowerSupply; + this.invalidate(); + } + + @Override + public void addConsumerEdge(FlowEdge consumerEdge) { + this.cpuEdge = consumerEdge; + } + + @Override + public void addSupplierEdge(FlowEdge supplierEdge) { + this.powerEdge = supplierEdge; + } + + @Override + public void removeConsumerEdge(FlowEdge consumerEdge) { + this.cpuEdge = null; + } + + @Override + public void removeSupplierEdge(FlowEdge supplierEdge) { + this.powerEdge = null; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/ChainWorkload.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/ChainWorkload.java new file mode 100644 index 00000000..78e8b5d4 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/ChainWorkload.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024 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.ArrayList; +import org.opendc.simulator.engine.FlowSupplier; + +public class ChainWorkload implements Workload { + private ArrayList workloads; + private final long checkpointInterval; + private final long checkpointDuration; + private final double checkpointIntervalScaling; + + public ChainWorkload( + ArrayList workloads, + long checkpointInterval, + long checkpointDuration, + double checkpointIntervalScaling) { + this.workloads = workloads; + this.checkpointInterval = checkpointInterval; + this.checkpointDuration = checkpointDuration; + this.checkpointIntervalScaling = checkpointIntervalScaling; + } + + public ArrayList getWorkloads() { + return workloads; + } + + public long getCheckpointInterval() { + return checkpointInterval; + } + + public long getCheckpointDuration() { + return checkpointDuration; + } + + public double getCheckpointIntervalScaling() { + return checkpointIntervalScaling; + } + + public void removeWorkloads(int numberOfWorkloads) { + if (numberOfWorkloads <= 0) { + return; + } + this.workloads.subList(0, numberOfWorkloads).clear(); + } + + @Override + public SimWorkload startWorkload(FlowSupplier supplier, long now) { + return new SimChainWorkload(supplier, this, now); + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/CheckpointModel.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/CheckpointModel.java new file mode 100644 index 00000000..723c450d --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/CheckpointModel.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2024 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; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// CheckPoint Model +// TODO: Move this to a separate file +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +import java.time.InstantSource; +import org.jetbrains.annotations.NotNull; +import org.opendc.simulator.engine.FlowGraph; +import org.opendc.simulator.engine.FlowNode; + +public class CheckpointModel extends FlowNode { + private SimWorkload simWorkload; + private long checkpointInterval; + private final long checkpointDuration; + private double checkpointIntervalScaling; + private FlowGraph graph; + + private long startOfInterval; + + public CheckpointModel(@NotNull SimWorkload simWorkload) { + super(simWorkload.getGraph()); + + this.checkpointInterval = simWorkload.getCheckpointInterval(); + this.checkpointDuration = simWorkload.getCheckpointDuration(); + this.checkpointIntervalScaling = simWorkload.getCheckpointIntervalScaling(); + this.simWorkload = simWorkload; + + this.graph = simWorkload.getGraph(); + + InstantSource clock = graph.getEngine().getClock(); + + this.startOfInterval = clock.millis(); + } + + @Override + public long onUpdate(long now) { + if (this.simWorkload == null) { + return Long.MAX_VALUE; + } + + long passedTime = now - startOfInterval; + long remainingTime = this.checkpointInterval - passedTime; + + // Interval not completed + if (remainingTime > 0) { + return now + remainingTime; + } + + simWorkload.makeSnapshot(now); + + // start new fragment + this.startOfInterval = now - passedTime; + + // Scale the interval time between checkpoints based on the provided scaling + this.checkpointInterval = (long) (this.checkpointInterval * this.checkpointIntervalScaling); + + return now + this.checkpointInterval + this.checkpointDuration; + } + + public void start() { + this.invalidate(); + } + + public void close() { + this.closeNode(); + + this.simWorkload = null; + this.graph = null; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimChainWorkload.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimChainWorkload.java index 1dcb3674..7f1cf060 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimChainWorkload.java +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimChainWorkload.java @@ -22,59 +22,46 @@ package org.opendc.simulator.compute.workload; -import java.time.InstantSource; -import java.util.List; -import java.util.Map; -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.flow2.FlowGraph; -import org.opendc.simulator.flow2.FlowStage; -import org.opendc.simulator.flow2.FlowStageLogic; +import java.util.LinkedList; +import org.opendc.simulator.engine.FlowEdge; +import org.opendc.simulator.engine.FlowNode; +import org.opendc.simulator.engine.FlowSupplier; /** - * A {@link SimWorkload} that composes two {@link SimWorkload}s. + * A {@link SimChainWorkload} that composes multiple {@link SimWorkload}s. */ -final class SimChainWorkload implements SimWorkload { - private final SimWorkload[] workloads; - private int activeWorkloadIndex; +final class SimChainWorkload extends SimWorkload implements FlowSupplier { + private final LinkedList workloads; + private int workloadIndex; - private SimChainWorkloadContext activeContext; + private SimWorkload activeWorkload; + private float demand = 0.0f; + private float supply = 0.0f; + + private FlowEdge workloadEdge; + private FlowEdge machineEdge; + + private float capacity = 0; private long checkpointInterval = 0; private long checkpointDuration = 0; - private double checkpointIntervalScaling = 1.0; - private CheckPointModel checkpointModel; - private SimChainWorkload snapshot; + private CheckpointModel checkpointModel; - /** - * Construct a {@link SimChainWorkload} instance. - * - * @param workloads The workloads to chain. - * @param activeWorkloadIndex The index of the active workload. - */ - SimChainWorkload(SimWorkload[] workloads, int activeWorkloadIndex) { - this.workloads = workloads; + private ChainWorkload snapshot; - if (this.workloads.length > 1) { - checkpointInterval = this.workloads[1].getCheckpointInterval(); - checkpointDuration = this.workloads[1].getCheckpointDuration(); - checkpointIntervalScaling = this.workloads[1].getCheckpointIntervalScaling(); - } + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Basic Getters and Setters + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - this.activeWorkloadIndex = activeWorkloadIndex; + @Override + public float getCapacity() { + return this.capacity; } - /** - * Construct a {@link SimChainWorkload} instance. - * - * @param workloads The workloads to chain. - */ - SimChainWorkload(SimWorkload... workloads) { - this(workloads, 0); + @Override + public ChainWorkload getSnapshot() { + return this.snapshot; } @Override @@ -92,270 +79,202 @@ final class SimChainWorkload implements SimWorkload { return checkpointIntervalScaling; } - @Override - public void setOffset(long now) { - for (SimWorkload workload : this.workloads) { - workload.setOffset(now); - } - } + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - @Override - public void onStart(SimMachineContext ctx) { - final SimWorkload[] workloads = this.workloads; - final int activeWorkloadIndex = this.activeWorkloadIndex; + SimChainWorkload(FlowSupplier supplier, ChainWorkload workload, long now) { + super(((FlowNode) supplier).getGraph()); - if (activeWorkloadIndex >= workloads.length) { - return; - } + this.snapshot = workload; + + this.parentGraph = ((FlowNode) supplier).getGraph(); + this.parentGraph.addEdge(this, supplier); - final SimChainWorkloadContext context = new SimChainWorkloadContext(ctx); - activeContext = context; + this.clock = this.parentGraph.getEngine().getClock(); + this.workloads = new LinkedList<>(workload.getWorkloads()); + this.checkpointInterval = workload.getCheckpointInterval(); + this.checkpointDuration = workload.getCheckpointDuration(); + this.checkpointIntervalScaling = workload.getCheckpointIntervalScaling(); if (checkpointInterval > 0) { this.createCheckpointModel(); - this.checkpointModel.start(); } - tryThrow(context.doStart(workloads[activeWorkloadIndex])); + this.workloadIndex = -1; + + this.onStart(); } - @Override - public void onStop(SimMachineContext ctx) { - final SimWorkload[] workloads = this.workloads; - final int activeWorkloadIndex = this.activeWorkloadIndex; + public Workload getNextWorkload() { + this.workloadIndex++; + return workloads.pop(); + } - if (activeWorkloadIndex >= workloads.length) { + // TODO: Combine with Constructor + public void onStart() { + if (this.workloads.isEmpty()) { return; } - final SimChainWorkloadContext context = activeContext; - activeContext = null; - - if (this.checkpointModel != null) { - this.checkpointModel.stop(); + // Create and start a checkpoint model if initiated + if (checkpointInterval > 0) { + this.checkpointModel.start(); } - tryThrow(context.doStop(workloads[activeWorkloadIndex])); + this.activeWorkload = this.getNextWorkload().startWorkload(this, this.clock.millis()); } @Override - public void makeSnapshot(long now) { - final int activeWorkloadIndex = this.activeWorkloadIndex; - final SimWorkload[] workloads = this.workloads; - final SimWorkload[] newWorkloads = new SimWorkload[workloads.length - activeWorkloadIndex]; + public long onUpdate(long now) { + return Long.MAX_VALUE; + } - for (int i = 0; i < newWorkloads.length; i++) { - workloads[activeWorkloadIndex + i].makeSnapshot(now); - newWorkloads[i] = workloads[activeWorkloadIndex + i].getSnapshot(); + @Override + public void stopWorkload() { + if (this.checkpointModel != null) { + this.checkpointModel.close(); + this.checkpointModel = null; } - this.snapshot = new SimChainWorkload(newWorkloads, 0); - } + if (this.activeWorkload != null) { + this.activeWorkload.stopWorkload(); + this.activeWorkload = null; + } - @Override - public SimChainWorkload getSnapshot() { - return this.snapshot; + this.closeNode(); } + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Checkpoint related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + @Override public void createCheckpointModel() { - this.checkpointModel = new CheckPointModel( - activeContext, this, this.checkpointInterval, this.checkpointDuration, this.checkpointIntervalScaling); + this.checkpointModel = new CheckpointModel(this); } - private class CheckPointModel implements FlowStageLogic { - private SimChainWorkload workload; - private long checkpointInterval; - private long checkpointDuration; - private double checkpointIntervalScaling; - private FlowStage stage; - - private long startOfInterval; - private Boolean firstCheckPoint = true; - - CheckPointModel( - SimChainWorkloadContext context, - SimChainWorkload workload, - long checkpointInterval, - long checkpointDuration, - double checkpointIntervalScaling) { - this.checkpointInterval = checkpointInterval; - this.checkpointDuration = checkpointDuration; - this.checkpointIntervalScaling = checkpointIntervalScaling; - this.workload = workload; - - this.stage = context.getGraph().newStage(this); - - InstantSource clock = this.stage.getGraph().getEngine().getClock(); - - this.startOfInterval = clock.millis(); - } - - @Override - public long onUpdate(FlowStage ctx, long now) { - long passedTime = now - startOfInterval; - long remainingTime = this.checkpointInterval - passedTime; - - if (!this.firstCheckPoint) { - remainingTime += this.checkpointDuration; - } - - // Interval not completed - if (remainingTime > 0) { - return now + remainingTime; - } - - workload.makeSnapshot(now); - if (firstCheckPoint) { - this.firstCheckPoint = false; - } + @Override + public void makeSnapshot(long now) { - // Scale the interval time between checkpoints based on the provided scaling - this.checkpointInterval = (long) (this.checkpointInterval * this.checkpointIntervalScaling); + this.snapshot.removeWorkloads(this.workloadIndex); + this.workloadIndex = 0; - return now + this.checkpointInterval + this.checkpointDuration; - } + activeWorkload.makeSnapshot(now); + } - public void start() { - this.stage.sync(); - } + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // FlowGraph Related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - public void stop() { - this.stage.close(); - } + /** + * Add connection to the active workload + * + * @param consumerEdge + */ + @Override + public void addConsumerEdge(FlowEdge consumerEdge) { + this.workloadEdge = consumerEdge; } /** - * A {@link SimMachineContext} that intercepts the shutdown calls. + * Add Connection to the cpuMux + * @param supplierEdge */ - private class SimChainWorkloadContext implements SimMachineContext { - private final SimMachineContext ctx; - private SimWorkload snapshot; - - private SimChainWorkloadContext(SimMachineContext ctx) { - this.ctx = ctx; - } - - @Override - public FlowGraph getGraph() { - return ctx.getGraph(); - } - - @Override - public Map getMeta() { - return ctx.getMeta(); - } + @Override + public void addSupplierEdge(FlowEdge supplierEdge) { + this.machineEdge = supplierEdge; + this.capacity = supplierEdge.getCapacity(); + } - @Override - public SimProcessingUnit getCpu() { - return ctx.getCpu(); - } + /** + * Push demand to the cpuMux + * + * @param supplierEdge + * @param newDemand + */ + @Override + public void pushDemand(FlowEdge supplierEdge, float newDemand) { + this.machineEdge.pushDemand(newDemand); + } - @Override - public SimMemory getMemory() { - return ctx.getMemory(); - } + /** + * Push supply to the workload + * + * @param consumerEdge + * @param newSupply + */ + @Override + public void pushSupply(FlowEdge consumerEdge, float newSupply) { + this.workloadEdge.pushSupply(newSupply); + } - @Override - public List getNetworkInterfaces() { - return ctx.getNetworkInterfaces(); + /** + * Handle new demand coming from the workload + * + * @param consumerEdge + * @param newDemand + */ + @Override + public void handleDemand(FlowEdge consumerEdge, float newDemand) { + if (newDemand == this.demand) { + return; } - @Override - public List getStorageInterfaces() { - return ctx.getStorageInterfaces(); - } + this.demand = newDemand; + this.pushDemand(this.machineEdge, newDemand); + } - @Override - public void makeSnapshot(long now) { - final SimWorkload workload = workloads[activeWorkloadIndex]; - this.snapshot = workload.getSnapshot(); + /** + * Handle new supply coming from the cpuMux + * + * @param supplierEdge + * @param newSupply + */ + @Override + public void handleSupply(FlowEdge supplierEdge, float newSupply) { + if (newSupply == this.supply) { + return; } - @Override - public SimWorkload getSnapshot(long now) { - this.makeSnapshot(now); - - return this.snapshot; - } + this.pushSupply(this.machineEdge, newSupply); + } - @Override - public void reset() { - ctx.reset(); + /** + * Handle the removal of the workload. + * If there is a next workload available, start this workload + * Otherwise, close this SimChainWorkload + * + * @param consumerEdge + */ + @Override + public void removeConsumerEdge(FlowEdge consumerEdge) { + if (this.workloadEdge == null) { + return; } - @Override - public void shutdown() { - shutdown(null); - } + // Remove the connection to the active workload + this.activeWorkload = null; + this.workloadEdge = null; - @Override - public void shutdown(Exception cause) { - final SimWorkload[] workloads = SimChainWorkload.this.workloads; - final int activeWorkloadIndex = ++SimChainWorkload.this.activeWorkloadIndex; - - final Exception stopException = doStop(workloads[activeWorkloadIndex - 1]); - if (cause == null) { - cause = stopException; - } else if (stopException != null) { - cause.addSuppressed(stopException); - } - - if (stopException == null && activeWorkloadIndex < workloads.length) { - ctx.reset(); - - final Exception startException = doStart(workloads[activeWorkloadIndex]); - - if (startException == null) { - return; - } else if (cause == null) { - cause = startException; - } else { - cause.addSuppressed(startException); - } - } - - if (SimChainWorkload.this.checkpointModel != null) { - SimChainWorkload.this.checkpointModel.stop(); - } - ctx.shutdown(cause); + // Start next workload + if (!this.workloads.isEmpty()) { + this.activeWorkload = getNextWorkload().startWorkload(this, this.clock.millis()); + return; } - /** - * Start the specified workload. - * - * @return The {@link Exception} that occurred while starting the workload or null if the workload - * started successfully. - */ - private Exception doStart(SimWorkload workload) { - try { - workload.onStart(this); - } catch (Exception cause) { - final Exception stopException = doStop(workload); - if (stopException != null) { - cause.addSuppressed(stopException); - } - return cause; - } - - return null; - } + this.stopWorkload(); + } - /** - * Stop the specified workload. - * - * @return The {@link Exception} that occurred while stopping the workload or null if the workload - * stopped successfully. - */ - private Exception doStop(SimWorkload workload) { - try { - workload.onStop(this); - } catch (Exception cause) { - return cause; - } - - return null; - } + /** + * Handle the removal of the connection to the cpuMux + * When this happens, close the SimChainWorkload + * + * @param supplierEdge + */ + @Override + public void removeSupplierEdge(FlowEdge supplierEdge) { + this.stopWorkload(); } @SuppressWarnings("unchecked") 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 deleted file mode 100644 index 5311fa38..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimFlopsWorkload.java +++ /dev/null @@ -1,179 +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.workload; - -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; - private SimFlopsWorkload snapshot; - - /** - * Construct a new {@link SimFlopsWorkload}. - * - * @param flops The number of floating point operations to perform for this task in MFLOPs. - * @param utilization The CPU utilization of the workload. - */ - 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; - this.remainingAmount = flops; - } - - @Override - public long getCheckpointInterval() { - return -1; - } - ; - - @Override - public long getCheckpointDuration() { - return -1; - } - - @Override - public double getCheckpointIntervalScaling() { - return -1; - } - ; - - @Override - public void setOffset(long now) {} - - @Override - public void onStart(SimMachineContext ctx) { - this.ctx = ctx; - - final FlowGraph graph = ctx.getGraph(); - final FlowStage stage = graph.newStage(this); - this.stage = stage; - - final SimProcessingUnit cpu = ctx.getCpu(); - final OutPort[] outputs = new OutPort[1]; - this.outputs = outputs; - - final OutPort output = stage.getOutlet("cpu"); - - graph.connect(output, cpu.getInput()); - outputs[0] = 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 void makeSnapshot(long now) { - final FlowStage stage = this.stage; - if (stage != null) { - stage.sync(); - } - - this.snapshot = new SimFlopsWorkload((long) remainingAmount, utilization); - } - - @Override - public SimFlopsWorkload getSnapshot() { - this.makeSnapshot(0); - - return this.snapshot; - } - - @Override - public void createCheckpointModel() {} - - @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; - } - - @Override - public String toString() { - return "SimFlopsWorkload[FLOPs=" + flops + ",utilization=" + utilization + "]"; - } -} 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 deleted file mode 100644 index be4cc2f5..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimRuntimeWorkload.java +++ /dev/null @@ -1,227 +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.workload; - -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 long duration; - private final double utilization; - - private SimMachineContext ctx; - private FlowStage stage; - private OutPort[] outputs; - - private long remainingDuration; - private long lastUpdate; - - private long checkpointDuration; // How long does it take to make a checkpoint? - private long checkpointInterval; // How long to wait until a new checkpoint is made? - private double checkpointIntervalScaling; - private long totalChecks; - private SimRuntimeWorkload snapshot; - - public SimRuntimeWorkload(long duration, double utilization) { - this(duration, utilization, 0, 0); - // 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.checkpointTime = 0L; - // this.checkpointWait = 0L; - // this.duration = duration; - // - // this.utilization = utilization; - // this.remainingDuration = duration; - } - - /** - * Construct a new {@link SimRuntimeWorkload}. - * - * @param duration The duration of the workload in milliseconds. - * @param utilization The CPU utilization of the workload. - */ - public SimRuntimeWorkload(long duration, double utilization, long checkpointInterval, long checkpointDuration) { - 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.checkpointDuration = checkpointDuration; - this.checkpointInterval = checkpointInterval; - this.duration = duration; - - if (this.checkpointInterval > 0) { - // Determine the number of checkpoints that need to be made during the workload - // If the total duration is divisible by the wait time between checkpoints, we can remove the last - // checkpoint - int to_remove = ((this.duration % this.checkpointInterval == 0) ? 1 : 0); - this.totalChecks = this.duration / this.checkpointInterval - to_remove; - this.duration += (this.checkpointDuration * totalChecks); - } - - this.utilization = utilization; - this.remainingDuration = duration; - } - - @Override - public long getCheckpointInterval() { - return checkpointInterval; - } - - @Override - public long getCheckpointDuration() { - return checkpointDuration; - } - - @Override - public double getCheckpointIntervalScaling() { - return checkpointIntervalScaling; - } - - @Override - public void setOffset(long now) {} - - @Override - public void onStart(SimMachineContext ctx) { - this.ctx = ctx; - - final FlowGraph graph = ctx.getGraph(); - final FlowStage stage = graph.newStage(this); - this.stage = stage; - - final OutPort[] outputs = new OutPort[1]; - this.outputs = outputs; - - final SimProcessingUnit cpu = ctx.getCpu(); - final OutPort output = stage.getOutlet("cpu"); - - graph.connect(output, cpu.getInput()); - outputs[0] = 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 void makeSnapshot(long now) { - System.out.printf("SimRuntimeWorkload -> makeSnapshot(%d)%n", now); - - final FlowStage stage = this.stage; - if (stage != null) { - stage.sync(); - } - - var remaining_time = this.remainingDuration; - - if (this.checkpointInterval > 0) { - // Calculate last checkpoint - var total_check_time = this.checkpointInterval + this.checkpointDuration; - var processed_time = this.duration - this.remainingDuration; - var processed_checks = (int) (processed_time / total_check_time); - var processed_time_last_check = - (processed_checks * total_check_time); // The processed time after the last checkpoint - - remaining_time = this.duration - - processed_time_last_check; // The remaining duration to process after last checkpoint - var remaining_checks = (int) (remaining_time / total_check_time); - remaining_time -= (remaining_checks * checkpointDuration); - } else { - remaining_time = duration; - } - - this.snapshot = - new SimRuntimeWorkload(remaining_time, utilization, this.checkpointInterval, this.checkpointDuration); - } - - @Override - public SimRuntimeWorkload getSnapshot() { - System.out.println("SimRuntimeWorkload -> getSnapshot()"); - - return this.snapshot; - } - - @Override - public void createCheckpointModel() {} - - @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 (delta == 0 && this.ctx == null) { - // This means the workload has been terminated - // But, has not executed to completion - return Long.MAX_VALUE; - } - - 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 deleted file mode 100644 index b8445a9c..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTrace.java +++ /dev/null @@ -1,413 +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.workload; - -import java.util.ArrayDeque; -import java.util.Iterator; -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 ArrayDeque fragments; - /** - * Construct a {@link SimTrace} instance. - * - */ - private SimTrace(ArrayDeque fragments) { - if (fragments.isEmpty()) { - throw new IllegalArgumentException("No Fragments found for the Trace"); - } - this.fragments = fragments; - } - - /** - * Construct a {@link SimWorkload} for this trace. - * - * // * @param offset The offset for the timestamps. - */ - public SimWorkload createWorkload(long start) { - return createWorkload(start, 0, 0, 1); - } - - /** - * Construct a {@link SimWorkload} for this trace. - * - * // * @param offset The offset for the timestamps. - */ - public SimWorkload createWorkload( - long start, long checkpointInterval, long checkpointDuration, double checkpointIntervalScaling) { - return new Workload(start, fragments, checkpointInterval, checkpointDuration, checkpointIntervalScaling); - } - - // /** - // * Create a new {@link Builder} instance with a default initial capacity. - // */ - public static Builder builder() { - return new Builder(); - } - - /** - * 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(); - - for (SimTraceFragment fragment : fragments) { - builder.add(fragment.duration(), fragment.cpuUsage(), fragment.coreCount()); - } - - 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(); - - for (SimTraceFragment fragment : fragments) { - builder.add(fragment.duration(), fragment.cpuUsage(), fragment.coreCount()); - } - - return builder.build(); - } - - /** - * Builder class for a {@link SimTrace}. - */ - public static final class Builder { - private final ArrayDeque fragments; - - private boolean isBuilt; - - /** - * Construct a new {@link Builder} instance. - */ - private Builder() { - this.fragments = new ArrayDeque<>(); - } - - /** - * Add a fragment to the trace. - * - * @param duration 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 duration, double usage, int cores) { - if (isBuilt) { - recreate(); - } - - fragments.add(new SimTraceFragment(duration, usage, cores)); - } - - /** - * Build the {@link SimTrace} instance. - */ - public SimTrace build() { - isBuilt = true; - return new SimTrace(fragments); - } - - /** - * 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; - this.fragments.clear(); - } - } - - /** - * Implementation of {@link SimWorkload} that executes a trace. - */ - private static class Workload implements SimWorkload { - private WorkloadStageLogic logic; - - private long offset; - - private final long start; - private ArrayDeque fragments; - - private long checkpointInterval; // How long to wait until a new checkpoint is made - private long checkpointDuration; // How long does it take to make a checkpoint - private double checkpointIntervalScaling; - private SimWorkload snapshot; - - private Workload( - long start, - ArrayDeque fragments, - long checkpointInterval, - long checkpointDuration, - double checkpointIntervalScaling) { - this.start = start; - this.checkpointInterval = checkpointInterval; - this.checkpointDuration = checkpointDuration; - this.checkpointIntervalScaling = checkpointIntervalScaling; - - this.fragments = fragments; - - this.snapshot = this; - } - - @Override - public long getCheckpointInterval() { - return checkpointInterval; - } - - @Override - public long getCheckpointDuration() { - return checkpointDuration; - } - - @Override - public double getCheckpointIntervalScaling() { - return checkpointIntervalScaling; - } - - @Override - public void setOffset(long now) { - this.offset = now; - } - - @Override - public void onStart(SimMachineContext ctx) { - final WorkloadStageLogic logic; - logic = new SingleWorkloadLogic(ctx, offset, fragments.iterator()); - this.logic = logic; - } - - @Override - public void onStop(SimMachineContext ctx) { - final WorkloadStageLogic logic = this.logic; - - if (logic != null) { - this.logic = null; - logic.getStage().close(); - } - } - - @Override - public void makeSnapshot(long now) { - final WorkloadStageLogic logic = this.logic; - final ArrayDeque newFragments = this.fragments; - - if (logic != null) { - int index = logic.getIndex(); - - if (index == 0 && (logic.getPassedTime(now) == 0)) { - this.snapshot = this; - return; - } - - // Remove all finished fragments - for (int i = 0; i < index; i++) { - newFragments.removeFirst(); - } - } else { - return; - } - - // Reduce the current Fragment to a fragment with the remaining time. - SimTraceFragment currentFragment = newFragments.pop(); - long passedTime = logic.getPassedTime(now); - long remainingTime = currentFragment.duration() - passedTime; - - if (remainingTime > 0) { - SimTraceFragment newFragment = - new SimTraceFragment(remainingTime, currentFragment.cpuUsage(), currentFragment.coreCount()); - - newFragments.addFirst(newFragment); - } - - // Add snapshot Fragment - // TODO: improve CPUUsage and coreCount here - SimTraceFragment snapshotFragment = new SimTraceFragment(checkpointDuration, 123456, 1); - newFragments.addFirst(snapshotFragment); - - // Update the logic - this.logic.updateFragments(newFragments.iterator(), now); - - // remove the snapshot Fragment and update fragments - newFragments.removeFirst(); - this.fragments = newFragments; - - this.snapshot = new Workload( - start, this.fragments, checkpointInterval, checkpointDuration, checkpointIntervalScaling); - } - - @Override - public SimWorkload getSnapshot() { - return this.snapshot; - } - - @Override - public void createCheckpointModel() {} - } - - /** - * 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(); - - long getPassedTime(long now); - - void updateFragments(Iterator newFragments, long offset); - - /** - * Return the current index of the workload. - */ - int getIndex(); - } - - /** - * 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 = 0; - - private final SimMachineContext ctx; - - private Iterator fragments; - private SimTraceFragment currentFragment; - private long startOffFragment; - - private SingleWorkloadLogic(SimMachineContext ctx, long offset, Iterator fragments) { - this.ctx = ctx; - - this.fragments = fragments; - - final FlowGraph graph = ctx.getGraph(); - stage = graph.newStage(this); - - final SimProcessingUnit cpu = ctx.getCpu(); - final OutPort output = stage.getOutlet("cpu"); - this.output = output; - - graph.connect(output, cpu.getInput()); - - // Start the first Fragment - this.currentFragment = this.fragments.next(); - this.output.push((float) currentFragment.cpuUsage()); - this.startOffFragment = offset; - } - - public long getPassedTime(long now) { - return now - this.startOffFragment; - } - - @Override - public void updateFragments(Iterator newFragments, long offset) { - this.fragments = newFragments; - - // Start the first Fragment - this.currentFragment = this.fragments.next(); - this.output.push((float) currentFragment.cpuUsage()); - this.startOffFragment = offset; - - this.index = -1; - - this.stage.invalidate(); - } - - @Override - public long onUpdate(FlowStage ctx, long now) { - long passedTime = getPassedTime(now); - long duration = this.currentFragment.duration(); - - // The current Fragment has not yet been finished, continue - if (passedTime < duration) { - return now + (duration - passedTime); - } - - // Loop through fragments until the passed time is filled. - // We need a while loop to account for skipping of fragments. - while (passedTime >= duration) { - if (!this.fragments.hasNext()) { - return doStop(ctx); - } - - passedTime = passedTime - duration; - - // get next Fragment - this.index++; - currentFragment = this.fragments.next(); - duration = currentFragment.duration(); - } - - // start new fragment - this.startOffFragment = now - passedTime; - - // Change the cpu Usage to the new Fragment - this.output.push((float) currentFragment.cpuUsage()); - - // Return the time when the current fragment will complete - return this.startOffFragment + duration; - } - - @Override - public FlowStage getStage() { - return stage; - } - - @Override - public int getIndex() { - return index; - } - - /** - * 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; - } - } -} 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 deleted file mode 100644 index 374e9732..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTraceFragment.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2024 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; - -public record SimTraceFragment(long duration, double cpuUsage, int coreCount) { - - public SimTraceFragment(long start, long duration, double cpuUsage, int coreCount) { - this(duration, cpuUsage, coreCount); - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTraceWorkload.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTraceWorkload.java new file mode 100644 index 00000000..b6f98344 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTraceWorkload.java @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2024 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.LinkedList; +import org.opendc.simulator.engine.FlowConsumer; +import org.opendc.simulator.engine.FlowEdge; +import org.opendc.simulator.engine.FlowGraph; +import org.opendc.simulator.engine.FlowNode; +import org.opendc.simulator.engine.FlowSupplier; + +public class SimTraceWorkload extends SimWorkload implements FlowConsumer { + private LinkedList remainingFragments; + private int fragmentIndex; + + private TraceFragment currentFragment; + private long startOfFragment; + + private FlowEdge machineEdge; + private float currentDemand; + private float currentSupply; + + private long checkpointInterval; + private long checkpointDuration; + private double checkpointIntervalScaling; + + private TraceWorkload snapshot; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Basic Getters and Setters + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public long getPassedTime(long now) { + return now - this.startOfFragment; + } + + public TraceWorkload getSnapshot() { + return snapshot; + } + + @Override + long getCheckpointInterval() { + return 0; + } + + @Override + long getCheckpointDuration() { + return 0; + } + + @Override + double getCheckpointIntervalScaling() { + return 0; + } + + public TraceFragment getNextFragment() { + this.currentFragment = this.remainingFragments.pop(); + this.fragmentIndex++; + + return this.currentFragment; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public SimTraceWorkload(FlowSupplier supplier, TraceWorkload workload, long now) { + super(((FlowNode) supplier).getGraph()); + + this.snapshot = workload; + this.checkpointInterval = workload.getCheckpointInterval(); + this.checkpointDuration = workload.getCheckpointDuration(); + this.checkpointIntervalScaling = workload.getCheckpointIntervalScaling(); + this.remainingFragments = new LinkedList<>(workload.getFragments()); + this.fragmentIndex = 0; + + final FlowGraph graph = ((FlowNode) supplier).getGraph(); + graph.addEdge(this, supplier); + + this.currentFragment = this.getNextFragment(); + pushDemand(machineEdge, (float) this.currentFragment.cpuUsage()); + this.startOfFragment = now; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Fragment related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public long onUpdate(long now) { + long passedTime = getPassedTime(now); + long duration = this.currentFragment.duration(); + + // The current Fragment has not yet been finished, continue + if (passedTime < duration) { + return now + (duration - passedTime); + } + + // Loop through fragments until the passed time is filled. + // We need a while loop to account for skipping of fragments. + while (passedTime >= duration) { + if (this.remainingFragments.isEmpty()) { + this.stopWorkload(); + return Long.MAX_VALUE; + } + + passedTime = passedTime - duration; + + // get next Fragment + currentFragment = this.getNextFragment(); + duration = currentFragment.duration(); + } + + // start new fragment + this.startOfFragment = now - passedTime; + + // Change the cpu Usage to the new Fragment + pushDemand(machineEdge, (float) this.currentFragment.cpuUsage()); + + // Return the time when the current fragment will complete + return this.startOfFragment + duration; + } + + @Override + public void stopWorkload() { + this.closeNode(); + + this.machineEdge = null; + this.remainingFragments = null; + this.currentFragment = null; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Checkpoint related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * SimTraceWorkload does not make a checkpoint, checkpointing is handled by SimChainWorkload + * TODO: Maybe add checkpoint models for SimTraceWorkload + */ + @Override + void createCheckpointModel() {} + + /** + * Create a new snapshot based on the current status of the workload. + * @param now + */ + public void makeSnapshot(long now) { + + // Check if fragments is empty + + // Get remaining time of current fragment + long passedTime = getPassedTime(now); + long remainingTime = currentFragment.duration() - passedTime; + + // Create a new fragment based on the current fragment and remaining duration + TraceFragment newFragment = + new TraceFragment(remainingTime, currentFragment.cpuUsage(), currentFragment.coreCount()); + + // Alter the snapshot by removing finished fragments + this.snapshot.removeFragments(this.fragmentIndex); + this.snapshot.addFirst(newFragment); + + this.remainingFragments.addFirst(newFragment); + + // Create and add a fragment for processing the snapshot process + // TODO: improve the implementation of cpuUsage and coreCount + TraceFragment snapshotFragment = new TraceFragment(this.checkpointDuration, 123456, 1); + this.remainingFragments.addFirst(snapshotFragment); + + this.fragmentIndex = -1; + this.currentFragment = getNextFragment(); + pushDemand(this.machineEdge, (float) this.currentFragment.cpuUsage()); + this.startOfFragment = now; + + this.invalidate(); + } + + /** + * Update the Fragments that are being used by the SimTraceWorkload + * @param newFragments + * @param offset + */ + public void updateFragments(LinkedList newFragments, long offset) { + this.remainingFragments = newFragments; + + // Start the first Fragment + this.currentFragment = this.remainingFragments.pop(); + pushDemand(this.machineEdge, (float) this.currentFragment.cpuUsage()); + this.startOfFragment = offset; + + this.invalidate(); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // FlowGraph Related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle updates in supply from the Virtual Machine + * + * @param supplierEdge + * @param newSupply + */ + @Override + public void handleSupply(FlowEdge supplierEdge, float newSupply) { + if (newSupply == this.currentSupply) { + return; + } + + this.currentSupply = newSupply; + } + + /** + * Push a new demand to the Virtual Machine + * + * @param supplierEdge + * @param newDemand + */ + @Override + public void pushDemand(FlowEdge supplierEdge, float newDemand) { + if (newDemand == this.currentDemand) { + return; + } + + this.currentDemand = newDemand; + this.machineEdge.pushDemand(newDemand); + } + + /** + * Add the connection to the Virtual Machine + * + * @param supplierEdge + */ + @Override + public void addSupplierEdge(FlowEdge supplierEdge) { + this.machineEdge = supplierEdge; + } + + /** + * Handle the removal of the connection to the Virtual Machine + * When the connection to the Virtual Machine is removed, the SimTraceWorkload is removed + * + * @param supplierEdge + */ + @Override + public void removeSupplierEdge(FlowEdge supplierEdge) { + this.stopWorkload(); + } +} 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 index f4f3ff58..b5c89941 100644 --- 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 @@ -22,7 +22,9 @@ package org.opendc.simulator.compute.workload; -import org.opendc.simulator.compute.SimMachineContext; +import org.opendc.simulator.engine.FlowConsumer; +import org.opendc.simulator.engine.FlowGraph; +import org.opendc.simulator.engine.FlowNode; /** * A model that characterizes the runtime behavior of some particular workload. @@ -31,35 +33,33 @@ import org.opendc.simulator.compute.SimMachineContext; * 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 { +public abstract class SimWorkload extends FlowNode implements FlowConsumer { /** - * This method is invoked when the workload is started. + * Construct a new {@link FlowNode} instance. * - * @param ctx The execution context in which the machine runs. + * @param parentGraph The {@link FlowGraph} this stage belongs to. */ - void onStart(SimMachineContext ctx); + public SimWorkload(FlowGraph parentGraph) { + super(parentGraph); + } /** * This method is invoked when the workload is stopped. - * - * @param ctx The execution context in which the machine runs. */ - void onStop(SimMachineContext ctx); + public abstract void stopWorkload(); /** * Create a snapshot of this workload. */ - void makeSnapshot(long now); - - SimWorkload getSnapshot(); + public abstract void makeSnapshot(long now); - void createCheckpointModel(); + public abstract Workload getSnapshot(); - long getCheckpointInterval(); + abstract void createCheckpointModel(); - long getCheckpointDuration(); + abstract long getCheckpointInterval(); - double getCheckpointIntervalScaling(); + abstract long getCheckpointDuration(); - void setOffset(long now); + abstract double getCheckpointIntervalScaling(); } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkloads.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkloads.java deleted file mode 100644 index 34202945..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkloads.java +++ /dev/null @@ -1,82 +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.workload; - -import java.time.Duration; - -/** - * Helper methods for constructing {@link SimWorkload}s. - */ -public class SimWorkloads { - private SimWorkloads() {} - - /** - * Create a {@link SimWorkload} that executes a specified number of floating point operations (FLOPs) at the given - * utilization. - * - * @param flops The number of floating point operations to perform for this task in MFLOPs. - * @param utilization The CPU utilization of the workload. - */ - public static SimWorkload flops(long flops, double utilization) { - return new SimFlopsWorkload(flops, utilization); - } - - /** - * Create a {@link SimWorkload} that consumes the CPU resources for a specified duration at the given utilization. - * - * @param duration The duration of the workload in milliseconds. - * @param utilization The CPU utilization of the workload. - */ - public static SimWorkload runtime(long duration, double utilization) { - return runtime(duration, utilization, 0, 0); - } - - /** - * Create a {@link SimWorkload} that consumes the CPU resources for a specified duration at the given utilization. - * - * @param duration The duration of the workload in milliseconds. - * @param utilization The CPU utilization of the workload. - */ - public static SimWorkload runtime( - long duration, double utilization, long checkpointInterval, long checkpointDuration) { - return new SimRuntimeWorkload(duration, utilization, checkpointInterval, checkpointDuration); - } - - /** - * Create a {@link SimWorkload} that consumes the CPU resources for a specified duration at the given utilization. - * - * @param duration The duration of the workload. - * @param utilization The CPU utilization of the workload. - */ - public static SimWorkload runtime( - Duration duration, double utilization, long checkpointInterval, long checkpointDuration) { - return runtime(duration.toMillis(), utilization, checkpointInterval, checkpointDuration); - } - - /** - * Chain the specified workloads into a single {@link SimWorkload}. - */ - public static SimWorkload chain(SimWorkload... workloads) { - return new SimChainWorkload(workloads); - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/TraceFragment.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/TraceFragment.java new file mode 100644 index 00000000..550c2135 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/TraceFragment.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024 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; + +public record TraceFragment(long duration, double cpuUsage, int coreCount) { + + public TraceFragment(long start, long duration, double cpuUsage, int coreCount) { + this(duration, cpuUsage, coreCount); + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/TraceWorkload.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/TraceWorkload.java new file mode 100644 index 00000000..115689df --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/TraceWorkload.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2024 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.ArrayList; +import java.util.List; +import org.opendc.simulator.engine.FlowSupplier; + +public class TraceWorkload implements Workload { + private ArrayList fragments; + private final long checkpointInterval; + private final long checkpointDuration; + private final double checkpointIntervalScaling; + + public TraceWorkload( + ArrayList fragments, + long checkpointInterval, + long checkpointDuration, + double checkpointIntervalScaling) { + this.fragments = fragments; + this.checkpointInterval = checkpointInterval; + this.checkpointDuration = checkpointDuration; + this.checkpointIntervalScaling = checkpointIntervalScaling; + } + + public ArrayList getFragments() { + return fragments; + } + + @Override + public long getCheckpointInterval() { + return checkpointInterval; + } + + @Override + public long getCheckpointDuration() { + return checkpointDuration; + } + + @Override + public double getCheckpointIntervalScaling() { + return checkpointIntervalScaling; + } + + public void removeFragments(int numberOfFragments) { + if (numberOfFragments <= 0) { + return; + } + this.fragments.subList(0, numberOfFragments).clear(); + } + + public void addFirst(TraceFragment fragment) { + this.fragments.add(0, fragment); + } + + @Override + public SimWorkload startWorkload(FlowSupplier supplier, long now) { + return new SimTraceWorkload(supplier, this, now); + } + + public static Builder builder() { + return builder(0L, 0L, 0L); + } + + public static Builder builder(long checkpointInterval, long checkpointDuration, double checkpointIntervalScaling) { + return new Builder(checkpointInterval, checkpointDuration, checkpointIntervalScaling); + } + + /** + * Construct a {@link TraceWorkload} from the specified fragments. + * + * @param fragments The array of fragments to construct the trace from. + */ + public static TraceWorkload ofFragments(TraceFragment... fragments) { + final Builder builder = builder(); + + for (TraceFragment fragment : fragments) { + builder.add(fragment.duration(), fragment.cpuUsage(), fragment.coreCount()); + } + + return builder.build(); + } + + /** + * Construct a {@link TraceWorkload} from the specified fragments. + * + * @param fragments The fragments to construct the trace from. + */ + public static TraceWorkload ofFragments(List fragments) { + final Builder builder = builder(); + + for (TraceFragment fragment : fragments) { + builder.add(fragment.duration(), fragment.cpuUsage(), fragment.coreCount()); + } + + return builder.build(); + } + + public static final class Builder { + private final ArrayList fragments; + private final long checkpointInterval; + private final long checkpointDuration; + private final double checkpointIntervalScaling; + + /** + * Construct a new {@link Builder} instance. + */ + private Builder(long checkpointInterval, long checkpointDuration, double checkpointIntervalScaling) { + this.fragments = new ArrayList<>(); + this.checkpointInterval = checkpointInterval; + this.checkpointDuration = checkpointDuration; + this.checkpointIntervalScaling = checkpointIntervalScaling; + } + + /** + * Add a fragment to the trace. + * + * @param duration 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 duration, double usage, int cores) { + fragments.add(0, new TraceFragment(duration, usage, cores)); + } + + /** + * Build the {@link TraceWorkload} instance. + */ + public TraceWorkload build() { + return new TraceWorkload( + this.fragments, this.checkpointInterval, this.checkpointDuration, this.checkpointIntervalScaling); + } + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/Workload.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/Workload.java new file mode 100644 index 00000000..cd34921a --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/Workload.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 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.engine.FlowSupplier; + +public interface Workload { + + long getCheckpointInterval(); + + long getCheckpointDuration(); + + double getCheckpointIntervalScaling(); + + SimWorkload startWorkload(FlowSupplier supplier, long now); +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/Coroutines.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/Coroutines.kt index 63af2048..ad69a3d6 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/Coroutines.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/Coroutines.kt @@ -23,12 +23,13 @@ package org.opendc.simulator.compute import kotlinx.coroutines.suspendCancellableCoroutine -import org.opendc.simulator.compute.workload.SimWorkload +import org.opendc.simulator.compute.machine.SimMachine +import org.opendc.simulator.compute.workload.TraceWorkload import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException /** - * Run the specified [SimWorkload] on this machine and suspend execution util [workload] has finished. + * Run the specified [SimWorkloadNew] on this machine and suspend execution util [workload] has finished. * * @param workload The workload to start on the machine. * @param meta The metadata to pass to the workload. @@ -36,13 +37,13 @@ import kotlin.coroutines.resumeWithException * @throws IllegalStateException if a workload is already active on the machine or if the machine is closed. */ public suspend fun SimMachine.runWorkload( - workload: SimWorkload, + workload: TraceWorkload, meta: Map = emptyMap(), ) { return suspendCancellableCoroutine { cont -> - cont.invokeOnCancellation { this@runWorkload.cancel() } + cont.invokeOnCancellation { this@runWorkload.shutdown() } - startWorkload(workload, meta) { cause -> + startWorkload(workload) { cause -> if (cause != null) cont.resumeWithException(cause) else cont.resume(Unit) } } 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 be6d289c..2b6a922e 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 @@ -22,33 +22,17 @@ package org.opendc.simulator.compute -import io.mockk.every -import io.mockk.mockk -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 import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.opendc.simulator.compute.device.SimNetworkAdapter -import org.opendc.simulator.compute.model.Cpu -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.power.CpuPowerModels -import org.opendc.simulator.compute.workload.SimTrace -import org.opendc.simulator.compute.workload.SimWorkload -import org.opendc.simulator.compute.workload.SimWorkloads -import org.opendc.simulator.flow2.FlowEngine -import org.opendc.simulator.flow2.source.SimpleFlowSource +import org.opendc.simulator.compute.cpu.CpuPowerModels +import org.opendc.simulator.compute.machine.SimMachine +import org.opendc.simulator.compute.models.CpuModel +import org.opendc.simulator.compute.models.MachineModel +import org.opendc.simulator.compute.models.MemoryUnit +import org.opendc.simulator.compute.workload.TraceWorkload +import org.opendc.simulator.engine.FlowEngine import org.opendc.simulator.kotlin.runSimulation -import org.opendc.simulator.network.SimNetworkSink -import org.opendc.simulator.power.SimPowerSource import java.util.concurrent.ThreadLocalRandom /** @@ -61,529 +45,390 @@ class SimMachineTest { fun setUp() { machineModel = MachineModel( - Cpu( + CpuModel( 0, 2, - 1000.0, + 1000.0f, "Intel", "Xeon", "amd64", ), MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000 * 4), - listOf(NetworkAdapter("Mellanox", "ConnectX-5", 25000.0)), - listOf(StorageDevice("Samsung", "EVO", 1000.0, 250.0, 250.0)), ) } // @Test - fun testFlopsWorkload() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - machine.runWorkload(SimWorkloads.flops(2_000, 1.0)) - - // Two cores execute 1000 MFlOps per second (1000 ms) - assertEquals(1000, timeSource.millis()) - } +// fun testFlopsWorkload() = +// runSimulation { +// val engine = FlowEngine.create(dispatcher) +// val graph = engine.newGraph() +// +// val machine = +// SimBareMetalMachine.create( +// graph, +// machineModel, +// ) +// +// machine.runWorkload(SimWorkloads.flops(2_000, 1.0)) +// +// // Two cores execute 1000 MFlOps per second (1000 ms) +// assertEquals(1000, timeSource.millis()) +// } @Test fun testTraceWorkload() = runSimulation { val random = ThreadLocalRandom.current() - val builder = SimTrace.builder() - repeat(1000000) { -// val timestamp = it.toLong() * 1000 -// val deadline = timestamp + 1000 + val builder = TraceWorkload.builder() + repeat(100) { builder.add(1000, random.nextDouble(0.0, 4500.0), 1) } - val trace = builder.build() - - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) + val traceWorkload = builder.build() - machine.runWorkload(trace.createWorkload(0)) - - // Two cores execute 1000 MFlOps per second (1000 ms) - assertEquals(1000000000, timeSource.millis()) - } - -// @Test - fun testDualSocketMachine() = - runSimulation { val engine = FlowEngine.create(dispatcher) val graph = engine.newGraph() - - val cpuNode = machineModel.cpu - val machineModel = - MachineModel( - List(cpuNode.coreCount * 2) { - Cpu( - it, - cpuNode.coreCount, - 1000.0, - ) - }, - MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000 * 4), - ) - val machine = - SimBareMetalMachine.create( + val simMachine = + SimMachine( graph, machineModel, - ) - - machine.runWorkload(SimWorkloads.flops(2_000, 1.0)) - - // Two sockets with two cores execute 2000 MFlOps per second (500 ms) - assertEquals(500, timeSource.millis()) - } - - @Test - fun testPower() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - SimPsuFactories.simple(CpuPowerModels.linear(100.0, 50.0)), - ) - val source = SimPowerSource(graph, 1000.0f) - source.connect(machine.psu) - - coroutineScope { - launch { machine.runWorkload(SimWorkloads.flops(2_000, 1.0)) } - - yield() - assertAll( - { assertEquals(100.0, machine.psu.powerDraw) }, - { assertEquals(100.0f, source.powerDraw) }, - ) - } - } - - @Test - fun testCapacityClamp() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - machine.runWorkload( - object : SimWorkload { - override fun onStart(ctx: SimMachineContext) { - val cpu = ctx.cpu - - cpu.frequency = (cpu.cpuModel.totalCapacity + 1000.0) - assertEquals(cpu.cpuModel.totalCapacity, cpu.frequency) - cpu.frequency = -1.0 - assertEquals(0.0, cpu.frequency) - - ctx.shutdown() - } - - override fun setOffset(now: Long) {} - - override fun onStop(ctx: SimMachineContext) {} - - override fun makeSnapshot(now: Long) { - } - - override fun getSnapshot(): SimWorkload = this - - override fun createCheckpointModel() {} - - override fun getCheckpointInterval(): Long { - return -1 - } - - override fun getCheckpointDuration(): Long { - return -1 - } - - override fun getCheckpointIntervalScaling(): Double { - return -1.0 - } - }, - ) - } - - @Test - fun testMemory() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - 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.shutdown() - } - - override fun setOffset(now: Long) {} - - override fun onStop(ctx: SimMachineContext) {} - - override fun makeSnapshot(now: Long) {} - - override fun getSnapshot(): SimWorkload = this - - override fun createCheckpointModel() {} - - override fun getCheckpointInterval(): Long { - return -1 - } - - override fun getCheckpointDuration(): Long { - return -1 - } - - override fun getCheckpointIntervalScaling(): Double { - return -1.0 - } - }, - ) - } - - @Test - fun testMemoryUsage() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - machine.runWorkload( - object : SimWorkload { - override fun onStart(ctx: SimMachineContext) { - val source = SimpleFlowSource(ctx.graph, ctx.memory.capacity.toFloat(), 1.0f) { ctx.shutdown() } - ctx.graph.connect(source.output, ctx.memory.input) - } - - override fun setOffset(now: Long) {} - - override fun onStop(ctx: SimMachineContext) {} - - override fun makeSnapshot(now: Long) { - } - - override fun getSnapshot(): SimWorkload = this - - override fun createCheckpointModel() {} - - override fun getCheckpointInterval(): Long { - return -1 - } - - override fun getCheckpointDuration(): Long { - return -1 - } - - override fun getCheckpointIntervalScaling(): Double { - return -1.0 - } - }, - ) - - assertEquals(1000, timeSource.millis()) - } - - @Test - fun testNetUsage() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - val adapter = (machine.peripherals[0] as SimNetworkAdapter) - adapter.connect(SimNetworkSink(graph, adapter.bandwidth.toFloat())) - - machine.runWorkload( - object : SimWorkload { - override fun onStart(ctx: SimMachineContext) { - 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 setOffset(now: Long) {} - - override fun onStop(ctx: SimMachineContext) {} - - override fun makeSnapshot(now: Long) { - } - - override fun getSnapshot(): SimWorkload = this - - override fun createCheckpointModel() {} - - override fun getCheckpointInterval(): Long { - return -1 - } - - override fun getCheckpointDuration(): Long { - return -1 - } - - override fun getCheckpointIntervalScaling(): Double { - return -1.0 - } - }, - ) - - assertEquals(40, timeSource.millis()) - } - - @Test - fun testDiskReadUsage() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - machine.runWorkload( - object : SimWorkload { - override fun onStart(ctx: SimMachineContext) { - 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 setOffset(now: Long) {} - - override fun onStop(ctx: SimMachineContext) {} - - override fun makeSnapshot(now: Long) {} - - override fun getSnapshot(): SimWorkload = this - - override fun createCheckpointModel() {} - - override fun getCheckpointInterval(): Long { - return -1 - } - - override fun getCheckpointDuration(): Long { - return -1 - } - - override fun getCheckpointIntervalScaling(): Double { - return -1.0 - } - }, - ) - - assertEquals(4000, timeSource.millis()) - } - - @Test - fun testDiskWriteUsage() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - machine.runWorkload( - object : SimWorkload { - override fun onStart(ctx: SimMachineContext) { - 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 setOffset(now: Long) {} - - override fun onStop(ctx: SimMachineContext) {} - - override fun makeSnapshot(now: Long) {} - - override fun getSnapshot(): SimWorkload = this - - override fun createCheckpointModel() {} - - override fun getCheckpointInterval(): Long { - return -1 - } - - override fun getCheckpointDuration(): Long { - return -1 - } - - override fun getCheckpointIntervalScaling(): Double { - return -1.0 - } - }, - ) - - assertEquals(4000, timeSource.millis()) - } - - @Test - fun testCancellation() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - try { - coroutineScope { - launch { machine.runWorkload(SimWorkloads.flops(2_000, 1.0)) } - cancel() - } - } catch (_: CancellationException) { - // Ignore - } - - assertEquals(0, timeSource.millis()) - } - - @Test - fun testConcurrentRuns() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - coroutineScope { - launch { - machine.runWorkload(SimWorkloads.flops(2_000, 1.0)) + CpuPowerModels.constant(0.0), + ) { cause -> } - assertThrows { - machine.runWorkload(SimWorkloads.flops(2_000, 1.0)) + val virtualMachine = + simMachine.startWorkload(traceWorkload) { cause -> + assertEquals(100000, timeSource.millis()) } - } - } - - @Test - fun testCatchStartFailure() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - val workload = mockk() - every { workload.onStart(any()) } throws IllegalStateException() - - assertThrows { machine.runWorkload(workload) } - } - - @Test - fun testCatchStopFailure() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - val workload = mockk() - every { workload.onStart(any()) } answers { (it.invocation.args[0] as SimMachineContext).shutdown() } - every { workload.onStop(any()) } throws IllegalStateException() - - assertThrows { machine.runWorkload(workload) } - } - - @Test - fun testCatchShutdownFailure() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - val workload = mockk() - every { workload.onStart(any()) } answers { (it.invocation.args[0] as SimMachineContext).shutdown(IllegalStateException()) } - assertThrows { machine.runWorkload(workload) } + // Two cores execute 1000 MFlOps per second (1000 ms) } - @Test - fun testCatchNestedFailure() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - val workload = mockk() - every { workload.onStart(any()) } answers { (it.invocation.args[0] as SimMachineContext).shutdown(IllegalStateException()) } - every { workload.onStop(any()) } throws IllegalStateException() - - val exc = assertThrows { machine.runWorkload(workload) } - assertEquals(1, exc.cause!!.suppressedExceptions.size) - } +// @Test +// fun testDualSocketMachine() = +// runSimulation { +// val engine = FlowEngine.create(dispatcher) +// val graph = engine.newGraph() +// +// val cpuNode = machineModel.cpu +// val machineModel = +// MachineModel( +// List(cpuNode.coreCount * 2) { +// CpuModel( +// it, +// cpuNode.coreCount, +// 1000.0, +// ) +// }, +// MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000 * 4), +// ) +// val machine = +// SimBareMetalMachine.create( +// graph, +// machineModel, +// CpuPowerModels.constant(0.0) +// ) +// +// machine.runWorkload(SimWorkloads.flops(2_000, 1.0)) +// +// // Two sockets with two cores execute 2000 MFlOps per second (500 ms) +// assertEquals(500, timeSource.millis()) +// } +// +// // @Test +// // fun testPower() = +// // runSimulation { +// // val engine = FlowEngine.create(dispatcher) +// // val graph = engine.newGraph() +// // val machine = +// // SimBareMetalMachine.create( +// // graph, +// // machineModel, +// // CpuPowerModels.linear(100.0, 50.0), +// // ) +// // val source = SimPowerSource(graph, 1000.0f) +// // source.connect(machine.psu) +// // +// // coroutineScope { +// // launch { machine.runWorkload(SimWorkloads.flops(2_000, 1.0)) } +// // +// // yield() +// // assertAll( +// // { assertEquals(100.0, machine.psu.powerDraw) }, +// // { assertEquals(100.0f, source.powerDraw) }, +// // ) +// // } +// // } +// +// @Test +// fun testCapacityClamp() = +// runSimulation { +// val engine = FlowEngine.create(dispatcher) +// val graph = engine.newGraph() +// +// val machine = +// SimBareMetalMachine.create( +// graph, +// machineModel, +// CpuPowerModels.constant(0.0) +// ) +// +// machine.runWorkload( +// object : SimWorkload { +// override fun onStart(ctx: SimMachineContext) { +// val cpu = ctx.cpu +// +// cpu.frequency = (cpu.cpuModel.totalCapacity + 1000.0) +// assertEquals(cpu.cpuModel.totalCapacity, cpu.frequency) +// cpu.frequency = -1.0 +// assertEquals(0.0, cpu.frequency) +// +// ctx.shutdown() +// } +// +// override fun setOffset(now: Long) {} +// +// override fun onStop(ctx: SimMachineContext) {} +// +// override fun makeSnapshot(now: Long) { +// } +// +// override fun getSnapshot(): SimWorkload = this +// +// override fun createCheckpointModel() {} +// +// override fun getCheckpointInterval(): Long { +// return -1 +// } +// +// override fun getCheckpointDuration(): Long { +// return -1 +// } +// +// override fun getCheckpointIntervalScaling(): Double { +// return -1.0 +// } +// }, +// ) +// } +// +// @Test +// fun testMemory() = +// runSimulation { +// val engine = FlowEngine.create(dispatcher) +// val graph = engine.newGraph() +// +// val machine = +// SimBareMetalMachine.create( +// graph, +// machineModel, +// CpuPowerModels.constant(0.0) +// ) +// +// machine.runWorkload( +// object : SimWorkload { +// override fun onStart(ctx: SimMachineContext) { +// assertEquals(32_000 * 4.0, ctx.memory.capacity) +// ctx.shutdown() +// } +// +// override fun setOffset(now: Long) {} +// +// override fun onStop(ctx: SimMachineContext) {} +// +// override fun makeSnapshot(now: Long) {} +// +// override fun getSnapshot(): SimWorkload = this +// +// override fun createCheckpointModel() {} +// +// override fun getCheckpointInterval(): Long { +// return -1 +// } +// +// override fun getCheckpointDuration(): Long { +// return -1 +// } +// +// override fun getCheckpointIntervalScaling(): Double { +// return -1.0 +// } +// }, +// ) +// } +// +// @Test +// fun testMemoryUsage() = +// runSimulation { +// val engine = FlowEngine.create(dispatcher) +// val graph = engine.newGraph() +// +// val machine = +// SimBareMetalMachine.create( +// graph, +// machineModel, +// CpuPowerModels.constant(0.0) +// ) +// +// machine.runWorkload( +// object : SimWorkload { +// override fun onStart(ctx: SimMachineContext) { +// val source = SimpleFlowSource(ctx.graph, ctx.memory.capacity.toFloat(), 1.0f) { ctx.shutdown() } +// ctx.graph.connect(source.output, ctx.memory.input) +// } +// +// override fun setOffset(now: Long) {} +// +// override fun onStop(ctx: SimMachineContext) {} +// +// override fun makeSnapshot(now: Long) { +// } +// +// override fun getSnapshot(): SimWorkload = this +// +// override fun createCheckpointModel() {} +// +// override fun getCheckpointInterval(): Long { +// return -1 +// } +// +// override fun getCheckpointDuration(): Long { +// return -1 +// } +// +// override fun getCheckpointIntervalScaling(): Double { +// return -1.0 +// } +// }, +// ) +// +// assertEquals(1000, timeSource.millis()) +// } +// +// @Test +// fun testCancellation() = +// runSimulation { +// val engine = FlowEngine.create(dispatcher) +// val graph = engine.newGraph() +// +// val machine = +// SimBareMetalMachine.create( +// graph, +// machineModel, +// CpuPowerModels.constant(0.0) +// ) +// +// try { +// coroutineScope { +// launch { machine.runWorkload(SimWorkloads.flops(2_000, 1.0)) } +// cancel() +// } +// } catch (_: CancellationException) { +// // Ignore +// } +// +// assertEquals(0, timeSource.millis()) +// } +// +// @Test +// fun testConcurrentRuns() = +// runSimulation { +// val engine = FlowEngine.create(dispatcher) +// val graph = engine.newGraph() +// +// val machine = +// SimBareMetalMachine.create( +// graph, +// machineModel, +// CpuPowerModels.constant(0.0) +// ) +// +// coroutineScope { +// launch { +// machine.runWorkload(SimWorkloads.flops(2_000, 1.0)) +// } +// +// assertThrows { +// machine.runWorkload(SimWorkloads.flops(2_000, 1.0)) +// } +// } +// } +// +// @Test +// fun testCatchStartFailure() = +// runSimulation { +// val engine = FlowEngine.create(dispatcher) +// val graph = engine.newGraph() +// +// val machine = +// SimBareMetalMachine.create( +// graph, +// machineModel, +// CpuPowerModels.constant(0.0) +// ) +// +// val workload = mockk() +// every { workload.onStart(any()) } throws IllegalStateException() +// +// assertThrows { machine.runWorkload(workload) } +// } +// +// @Test +// fun testCatchStopFailure() = +// runSimulation { +// val engine = FlowEngine.create(dispatcher) +// val graph = engine.newGraph() +// +// val machine = +// SimBareMetalMachine.create( +// graph, +// machineModel, +// CpuPowerModels.constant(0.0) +// ) +// +// val workload = mockk() +// every { workload.onStart(any()) } answers { (it.invocation.args[0] as SimMachineContext).shutdown() } +// every { workload.onStop(any()) } throws IllegalStateException() +// +// assertThrows { machine.runWorkload(workload) } +// } +// +// @Test +// fun testCatchShutdownFailure() = +// runSimulation { +// val engine = FlowEngine.create(dispatcher) +// val graph = engine.newGraph() +// +// val machine = +// SimBareMetalMachine.create( +// graph, +// machineModel, +// CpuPowerModels.constant(0.0) +// ) +// +// val workload = mockk() +// every { workload.onStart(any()) } answers { (it.invocation.args[0] as SimMachineContext).shutdown(IllegalStateException()) } +// +// assertThrows { machine.runWorkload(workload) } +// } +// +// @Test +// fun testCatchNestedFailure() = +// runSimulation { +// val engine = FlowEngine.create(dispatcher) +// val graph = engine.newGraph() +// +// val machine = +// SimBareMetalMachine.create( +// graph, +// machineModel, +// CpuPowerModels.constant(0.0) +// ) +// +// val workload = mockk() +// every { workload.onStart(any()) } answers { (it.invocation.args[0] as SimMachineContext).shutdown(IllegalStateException()) } +// every { workload.onStop(any()) } throws IllegalStateException() +// +// val exc = assertThrows { machine.runWorkload(workload) } +// assertEquals(1, exc.cause!!.suppressedExceptions.size) +// } } 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 deleted file mode 100644 index 6cebc46f..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt +++ /dev/null @@ -1,269 +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 kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.yield -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.BeforeEach -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.ScalingGovernors -import org.opendc.simulator.compute.kernel.interference.VmInterferenceModel -import org.opendc.simulator.compute.model.Cpu -import org.opendc.simulator.compute.model.MachineModel -import org.opendc.simulator.compute.model.MemoryUnit -import org.opendc.simulator.compute.runWorkload -import org.opendc.simulator.compute.workload.SimTrace -import org.opendc.simulator.compute.workload.SimTraceFragment -import org.opendc.simulator.flow2.FlowEngine -import org.opendc.simulator.flow2.mux.FlowMultiplexerFactory -import org.opendc.simulator.kotlin.runSimulation -import java.util.SplittableRandom - -/** - * Test suite for the [SimHypervisor] class. - */ -internal class SimFairShareHypervisorTest { - private lateinit var model: MachineModel - - @BeforeEach - fun setUp() { - model = - MachineModel( - Cpu( - 0, - 1, - 3200.0, - "Intel", - "Xeon", - "amd64", - ), - // memory - MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000 * 4), - ) - } - - /** - * Test overcommitting of resources via the hypervisor with a single VM. - */ - @Test - fun testOvercommittedSingle() = - runSimulation { - val duration = 5 * 60L - val workloadA = - 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(dispatcher) - 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) - vm.runWorkload(workloadA) - - yield() - machine.cancel() - - assertAll( - { assertEquals(319781, hypervisor.counters.cpuActiveTime, "Active time does not match") }, - { assertEquals(880219, hypervisor.counters.cpuIdleTime, "Idle time does not match") }, - { assertEquals(28125, hypervisor.counters.cpuStealTime, "Steal time does not match") }, - { assertEquals(1200000, timeSource.millis()) { "Current time is correct" } }, - ) - } - - /** - * Test overcommitting of resources via the hypervisor with two VMs. - */ - @Test - fun testOvercommittedDual() = - runSimulation { - val duration = 5 * 60L - val workloadA = - 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 = - 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) - - val engine = FlowEngine.create(dispatcher) - 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 { - launch { - val vm = hypervisor.newMachine(model) - vm.runWorkload(workloadA) - hypervisor.removeMachine(vm) - } - val vm = hypervisor.newMachine(model) - vm.runWorkload(workloadB) - hypervisor.removeMachine(vm) - } - yield() - machine.cancel() - yield() - - assertAll( - { assertEquals(329250, hypervisor.counters.cpuActiveTime, "Active time does not match") }, - { assertEquals(870750, hypervisor.counters.cpuIdleTime, "Idle time does not match") }, - { assertEquals(318750, hypervisor.counters.cpuStealTime, "Steal time does not match") }, - { assertEquals(1200000, timeSource.millis()) }, - ) - } - - @Test - fun testMultipleCPUs() = - runSimulation { - val model = - MachineModel( - Cpu( - 0, - 2, - 3200.0, - "Intel", - "Xeon", - "amd64", - ), - // memory - MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000 * 4), - ) - - val engine = FlowEngine.create(dispatcher) - 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) } - } - - machine.cancel() - } - - @Test - fun testInterference() = - runSimulation { - val model = - MachineModel( - Cpu( - 0, - 2, - 3200.0, - "Intel", - "Xeon", - "amd64", - ), - // memory - MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000 * 4), - ) - - val interferenceModel = - VmInterferenceModel.builder() - .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.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create(graph, model) - val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(0L)) - - val duration = 5 * 60L - val workloadA = - 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 = - 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) - } - - coroutineScope { - launch { - val vm = hypervisor.newMachine(model) - vm.runWorkload(workloadA, meta = mapOf("interference-model" to interferenceModel.getProfile("a")!!)) - hypervisor.removeMachine(vm) - } - val vm = hypervisor.newMachine(model) - vm.runWorkload(workloadB, meta = mapOf("interference-model" to interferenceModel.getProfile("b")!!)) - hypervisor.removeMachine(vm) - } - - machine.cancel() - } -} 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 deleted file mode 100644 index b4ae372c..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt +++ /dev/null @@ -1,234 +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 kotlinx.coroutines.launch -import kotlinx.coroutines.yield -import org.junit.jupiter.api.Assertions.assertAll -import org.junit.jupiter.api.Assertions.assertDoesNotThrow -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertFalse -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.opendc.simulator.compute.SimBareMetalMachine -import org.opendc.simulator.compute.model.Cpu -import org.opendc.simulator.compute.model.MachineModel -import org.opendc.simulator.compute.model.MemoryUnit -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.SimWorkloads -import org.opendc.simulator.flow2.FlowEngine -import org.opendc.simulator.flow2.mux.FlowMultiplexerFactory -import org.opendc.simulator.kotlin.runSimulation -import java.util.SplittableRandom - -/** - * A test suite for a space-shared [SimHypervisor]. - */ -internal class SimSpaceSharedHypervisorTest { - private lateinit var machineModel: MachineModel - - @BeforeEach - fun setUp() { - machineModel = - MachineModel( - Cpu( - 0, - 1, - 3200.0, - "Intel", - "Xeon", - "amd64", - ), - // memory - MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000 * 4), - ) - } - - /** - * Test a trace workload. - */ - @Test - fun testTrace() = - runSimulation { - val duration = 5 * 60L - val workloadA = - 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(dispatcher) - 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) - vm.runWorkload(workloadA) - yield() - - hypervisor.removeMachine(vm) - machine.cancel() - - assertEquals(5 * 60L * 4000, timeSource.millis()) { "Took enough time" } - } - - /** - * Test runtime workload on hypervisor. - */ - @Test - fun testRuntimeWorkload() = - runSimulation { - val duration = 5 * 60L * 1000 - val workload = SimWorkloads.runtime(duration, 1.0) - val engine = FlowEngine.create(dispatcher) - 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(workload) - hypervisor.removeMachine(vm) - - machine.cancel() - - assertEquals(duration, timeSource.millis()) { "Took enough time" } - } - - /** - * Test FLOPs workload on hypervisor. - */ - @Test - fun testFlopsWorkload() = - runSimulation { - val duration = 5 * 60L * 1000 - val workload = SimWorkloads.flops((duration * 3.2).toLong(), 1.0) - val engine = FlowEngine.create(dispatcher) - 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(workload) - machine.cancel() - - assertEquals(duration, timeSource.millis()) { "Took enough time" } - } - - /** - * Test two workloads running sequentially. - */ - @Test - fun testTwoWorkloads() = - runSimulation { - val duration = 5 * 60L * 1000 - val engine = FlowEngine.create(dispatcher) - 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(SimWorkloads.runtime(duration, 1.0)) - hypervisor.removeMachine(vm) - - yield() - - val vm2 = hypervisor.newMachine(machineModel) - vm2.runWorkload(SimWorkloads.runtime(duration, 1.0)) - hypervisor.removeMachine(vm2) - - machine.cancel() - - assertEquals(duration * 2, timeSource.millis()) { "Took enough time" } - } - - /** - * Test concurrent workloads on the machine. - */ - @Test - fun testConcurrentWorkloadFails() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - 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) - launch { vm.runWorkload(SimWorkloads.runtime(10_000, 1.0)) } - yield() - - assertAll( - { assertFalse(hypervisor.canFit(machineModel)) }, - { assertThrows { hypervisor.newMachine(machineModel) } }, - ) - - machine.cancel() - vm.cancel() - } - - /** - * Test concurrent workloads on the machine. - */ - @Test - fun testConcurrentWorkloadSucceeds() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create(graph, machineModel) - val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) - - launch { machine.runWorkload(hypervisor) } - yield() - - hypervisor.removeMachine(hypervisor.newMachine(machineModel)) - - assertAll( - { assertTrue(hypervisor.canFit(machineModel)) }, - { assertDoesNotThrow { hypervisor.newMachine(machineModel) } }, - ) - - machine.cancel() - } -} 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 deleted file mode 100644 index 4a930df6..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernorTest.kt +++ /dev/null @@ -1,94 +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 io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import org.junit.jupiter.api.Test - -/** - * Test suite for the conservative [ScalingGovernor]. - */ -internal class ConservativeScalingGovernorTest { - @Test - fun testSetStartLimitWithoutPStates() { - val cpuCapacity = 4100.0 - val minSpeed = cpuCapacity / 2 - val defaultThreshold = 0.8 - val defaultStepSize = 0.05 * cpuCapacity - val governor = ScalingGovernors.conservative(defaultThreshold) - - val policy = mockk(relaxUnitFun = true) - every { policy.max } returns cpuCapacity - every { policy.min } returns minSpeed - - var target = 0.0 - every { policy.target } answers { target } - every { policy.target = any() } propertyType Double::class answers { target = value } - - val logic = governor.newGovernor(policy) - logic.onStart() - logic.onLimit(0.5) - - // Upwards scaling - logic.onLimit(defaultThreshold + 0.2) - - // Downwards scaling - logic.onLimit(defaultThreshold + 0.1) - - verify(exactly = 2) { policy.target = minSpeed } - verify(exactly = 1) { policy.target = minSpeed + defaultStepSize } - } - - @Test - fun testSetStartLimitWithPStatesAndParams() { - val firstPState = 1000.0 - val cpuCapacity = 4100.0 - val minSpeed = firstPState - val threshold = 0.5 - val stepSize = 0.02 * cpuCapacity - val governor = ScalingGovernors.conservative(threshold, stepSize) - - val policy = mockk(relaxUnitFun = true) - every { policy.max } returns cpuCapacity - every { policy.min } returns firstPState - - var target = 0.0 - every { policy.target } answers { target } - every { policy.target = any() } propertyType Double::class answers { target = value } - - val logic = governor.newGovernor(policy) - logic.onStart() - logic.onLimit(0.5) - - // Upwards scaling - logic.onLimit(threshold + 0.2) - - // Downwards scaling - logic.onLimit(threshold + 0.1) - - verify(exactly = 2) { policy.target = minSpeed } - verify(exactly = 1) { policy.target = minSpeed + stepSize } - } -} 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 deleted file mode 100644 index d6a7090b..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernorTest.kt +++ /dev/null @@ -1,78 +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 io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import org.junit.jupiter.api.Test - -/** - * Test suite for the on-demand [ScalingGovernor]. - */ -internal class OnDemandScalingGovernorTest { - @Test - fun testSetStartLimitWithoutPStates() { - val cpuCapacity = 4100.0 - val minSpeed = cpuCapacity / 2 - val defaultThreshold = 0.8 - val governor = ScalingGovernors.ondemand(defaultThreshold) - - val policy = mockk(relaxUnitFun = true) - every { policy.min } returns minSpeed - every { policy.max } returns cpuCapacity - - val logic = governor.newGovernor(policy) - logic.onStart() - verify(exactly = 1) { policy.target = minSpeed } - - logic.onLimit(0.5) - verify(exactly = 1) { policy.target = minSpeed + 0.5 * (cpuCapacity - minSpeed) / 100 } - - logic.onLimit(defaultThreshold) - verify(exactly = 1) { policy.target = cpuCapacity } - } - - @Test - fun testSetStartLimitWithPStatesAndParams() { - val firstPState = 1000.0 - val cpuCapacity = 4100.0 - val threshold = 0.5 - val governor = ScalingGovernors.ondemand(threshold) - - val policy = mockk(relaxUnitFun = true) - every { policy.max } returns cpuCapacity - every { policy.min } returns firstPState - - val logic = governor.newGovernor(policy) - - logic.onStart() - verify(exactly = 1) { policy.target = firstPState } - - logic.onLimit(0.1) - verify(exactly = 1) { policy.target = firstPState + 0.1 * (cpuCapacity - firstPState) / 100 } - - logic.onLimit(threshold) - verify(exactly = 1) { policy.target = cpuCapacity } - } -} 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 deleted file mode 100644 index f03f41fe..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernorTest.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 - -import io.mockk.every -import io.mockk.spyk -import io.mockk.verify -import org.junit.jupiter.api.Test - -/** - * Test suite for the [PerformanceScalingGovernor] - */ -internal class PerformanceScalingGovernorTest { - @Test - fun testSetStartLimit() { - val policy = spyk() - val logic = ScalingGovernors.performance().newGovernor(policy) - - every { policy.max } returns 4100.0 - - logic.onStart() - verify(exactly = 1) { policy.target = 4100.0 } - - logic.onLimit(0.0) - verify(exactly = 1) { policy.target = 4100.0 } - - logic.onLimit(1.0) - verify(exactly = 1) { policy.target = 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 deleted file mode 100644 index 4cee8199..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernorTest.kt +++ /dev/null @@ -1,72 +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 io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import org.junit.jupiter.api.Test - -/** - * Test suite for the [PowerSaveScalingGovernor] - */ -internal class PowerSaveScalingGovernorTest { - @Test - fun testSetStartLimitWithoutPStates() { - val cpuCapacity = 4100.0 - val minSpeed = cpuCapacity / 2 - val policy = mockk(relaxUnitFun = true) - val logic = ScalingGovernors.powerSave().newGovernor(policy) - - every { policy.max } returns cpuCapacity - every { policy.min } returns minSpeed - - logic.onStart() - - logic.onLimit(0.0) - verify(exactly = 1) { policy.target = minSpeed } - - logic.onLimit(1.0) - verify(exactly = 1) { policy.target = minSpeed } - } - - @Test - fun testSetStartLimitWithPStates() { - val cpuCapacity = 4100.0 - val firstPState = 1000.0 - val policy = mockk(relaxUnitFun = true) - val logic = ScalingGovernors.powerSave().newGovernor(policy) - - every { policy.max } returns cpuCapacity - every { policy.min } returns firstPState - - logic.onStart() - verify(exactly = 1) { policy.target = firstPState } - - logic.onLimit(0.0) - verify(exactly = 1) { policy.target = firstPState } - - logic.onLimit(1.0) - verify(exactly = 1) { policy.target = firstPState } - } -} 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 deleted file mode 100644 index e3bea821..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PowerModelTest.kt +++ /dev/null @@ -1,180 +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.junit.jupiter.api.Assertions.assertAll -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.Arguments -import org.junit.jupiter.params.provider.MethodSource -import java.util.stream.Stream -import kotlin.math.pow - -internal class PowerModelTest { - private val epsilon = 10.0.pow(-3) - private val cpuUtil = 0.9 - - @ParameterizedTest - @MethodSource("machinePowerModelArgs") - fun `compute power consumption given CPU loads`( - powerModel: CpuPowerModel, - expectedPowerConsumption: Double, - ) { - val computedPowerConsumption = powerModel.computePower(cpuUtil) - assertEquals(expectedPowerConsumption, computedPowerConsumption, epsilon) - } - - @ParameterizedTest - @MethodSource("machinePowerModelArgs") - fun `ignore idle power when computing power consumptions`( - powerModel: CpuPowerModel, - expectedPowerConsumption: Double, - ) { - val zeroPowerModel = CpuPowerModels.zeroIdle(powerModel) - - assertAll( - { assertEquals(expectedPowerConsumption, zeroPowerModel.computePower(cpuUtil), epsilon) }, - { assertEquals(0.0, zeroPowerModel.computePower(0.0)) }, - ) - } - - @Test - fun `compute power draw by the SPEC benchmark model`() { - 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)) }, - { assertEquals(58.4 + (98 - 58.4) / 5, powerModel.computePower(0.02)) }, - { assertEquals(98.0, powerModel.computePower(0.1)) }, - { assertEquals(140.0, powerModel.computePower(0.5)) }, - { assertEquals(189.0, powerModel.computePower(0.8)) }, - { assertEquals(189.0 + 0.7 * 10 * (205 - 189) / 10, powerModel.computePower(0.87)) }, - { assertEquals(205.0, powerModel.computePower(0.9)) }, - { assertEquals(222.0, powerModel.computePower(1.0)) }, - ) - } - - @Test - fun `test linear model`() { - val powerModel = CpuPowerModels.linear(400.0, 200.0) - - assertAll( - { assertEquals(200.0, powerModel.computePower(-0.1)) }, - { assertEquals(200.0, powerModel.computePower(0.0)) }, - { assertEquals(220.0, powerModel.computePower(0.1)) }, - { assertEquals(240.0, powerModel.computePower(0.2)) }, - { assertEquals(260.0, powerModel.computePower(0.3)) }, - { assertEquals(280.0, powerModel.computePower(0.4)) }, - { assertEquals(300.0, powerModel.computePower(0.5)) }, - { assertEquals(320.0, powerModel.computePower(0.6)) }, - { assertEquals(340.0, powerModel.computePower(0.7)) }, - { assertEquals(360.0, powerModel.computePower(0.8)) }, - { assertEquals(380.0, powerModel.computePower(0.9)) }, - { assertEquals(400.0, powerModel.computePower(1.0)) }, - { assertEquals(400.0, powerModel.computePower(1.1)) }, - ) - } - - @Test - fun `test sqrt model`() { - val powerModel = CpuPowerModels.sqrt(400.0, 200.0) - - assertAll( - { assertEquals(200.0, powerModel.computePower(-1.0), 1.0) }, - { assertEquals(200.0, powerModel.computePower(0.0), 1.0) }, - { assertEquals(263.0, powerModel.computePower(0.1), 1.0) }, - { assertEquals(289.0, powerModel.computePower(0.2), 1.0) }, - { assertEquals(309.0, powerModel.computePower(0.3), 1.0) }, - { assertEquals(326.0, powerModel.computePower(0.4), 1.0) }, - { assertEquals(341.0, powerModel.computePower(0.5), 1.0) }, - { assertEquals(354.0, powerModel.computePower(0.6), 1.0) }, - { assertEquals(367.0, powerModel.computePower(0.7), 1.0) }, - { assertEquals(378.0, powerModel.computePower(0.8), 1.0) }, - { assertEquals(389.0, powerModel.computePower(0.9), 1.0) }, - { assertEquals(400.0, powerModel.computePower(1.0), 1.0) }, - { assertEquals(400.0, powerModel.computePower(1.1), 1.0) }, - ) - } - - @Test - fun `test square model`() { - val powerModel = CpuPowerModels.square(400.0, 200.0) - - assertAll( - { assertEquals(200.0, powerModel.computePower(-1.0), 1.0) }, - { assertEquals(200.0, powerModel.computePower(0.0), 1.0) }, - { assertEquals(202.0, powerModel.computePower(0.1), 1.0) }, - { assertEquals(208.0, powerModel.computePower(0.2), 1.0) }, - { assertEquals(218.0, powerModel.computePower(0.3), 1.0) }, - { assertEquals(232.0, powerModel.computePower(0.4), 1.0) }, - { assertEquals(250.0, powerModel.computePower(0.5), 1.0) }, - { assertEquals(272.0, powerModel.computePower(0.6), 1.0) }, - { assertEquals(298.0, powerModel.computePower(0.7), 1.0) }, - { assertEquals(328.0, powerModel.computePower(0.8), 1.0) }, - { assertEquals(362.0, powerModel.computePower(0.9), 1.0) }, - { assertEquals(400.0, powerModel.computePower(1.0), 1.0) }, - { assertEquals(400.0, powerModel.computePower(1.1), 1.0) }, - ) - } - - @Test - fun `test cubic model`() { - val powerModel = CpuPowerModels.cubic(400.0, 200.0) - - assertAll( - { assertEquals(200.0, powerModel.computePower(-1.0), 1.0) }, - { assertEquals(200.0, powerModel.computePower(0.0), 1.0) }, - { assertEquals(200.0, powerModel.computePower(0.1), 1.0) }, - { assertEquals(201.0, powerModel.computePower(0.2), 1.0) }, - { assertEquals(205.0, powerModel.computePower(0.3), 1.0) }, - { assertEquals(212.0, powerModel.computePower(0.4), 1.0) }, - { assertEquals(225.0, powerModel.computePower(0.5), 1.0) }, - { assertEquals(243.0, powerModel.computePower(0.6), 1.0) }, - { assertEquals(268.0, powerModel.computePower(0.7), 1.0) }, - { assertEquals(302.0, powerModel.computePower(0.8), 1.0) }, - { assertEquals(345.0, powerModel.computePower(0.9), 1.0) }, - { assertEquals(400.0, powerModel.computePower(1.0), 1.0) }, - { assertEquals(400.0, powerModel.computePower(1.1), 1.0) }, - ) - } - - @Suppress("unused") - private companion object { - @JvmStatic - fun machinePowerModelArgs(): Stream = - Stream.of( - 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/SimChainWorkloadTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimChainWorkloadTest.kt deleted file mode 100644 index 582635fc..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimChainWorkloadTest.kt +++ /dev/null @@ -1,310 +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.workload - -import io.mockk.every -import io.mockk.mockk -import io.mockk.spyk -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.opendc.simulator.compute.SimBareMetalMachine -import org.opendc.simulator.compute.SimMachineContext -import org.opendc.simulator.compute.model.Cpu -import org.opendc.simulator.compute.model.MachineModel -import org.opendc.simulator.compute.model.MemoryUnit -import org.opendc.simulator.compute.runWorkload -import org.opendc.simulator.flow2.FlowEngine -import org.opendc.simulator.kotlin.runSimulation - -/** - * Test suite for the [SimChainWorkload] class. - */ -class SimChainWorkloadTest { - private lateinit var machineModel: MachineModel - - @BeforeEach - fun setUp() { - machineModel = - MachineModel( - Cpu( - 0, - 2, - 1000.0, - "Intel", - "Xeon", - "amd64", - ), - // memory - MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000 * 4), - ) - } - - @Test - fun testMultipleWorkloads() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - val workload = - SimWorkloads.chain( - SimWorkloads.runtime(1000, 1.0, 0L, 0L), - SimWorkloads.runtime(1000, 1.0, 0L, 0L), - ) - - machine.runWorkload(workload) - - assertEquals(2000, timeSource.millis()) - } - - @Test - fun testStartFailure() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - val workloadA = mockk() - every { workloadA.onStart(any()) } throws IllegalStateException("Staged") - every { workloadA.onStop(any()) } returns Unit - - val workload = - SimWorkloads.chain( - workloadA, - SimWorkloads.runtime(1000, 1.0, 0L, 0L), - ) - - assertThrows { machine.runWorkload(workload) } - - assertEquals(0, timeSource.millis()) - } - -// @Test - fun testStartFailureSecond() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - val workloadA = mockk() - every { workloadA.onStart(any()) } throws IllegalStateException("Staged") - every { workloadA.onStop(any()) } returns Unit - - val workload = - SimWorkloads.chain( - SimWorkloads.runtime(1000, 1.0), - workloadA, - SimWorkloads.runtime(1000, 1.0), - ) - - assertThrows { machine.runWorkload(workload) } - - assertEquals(1000, timeSource.millis()) - } - - @Test - fun testStopFailure() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - val workloadA = spyk(SimRuntimeWorkload(1000, 1.0)) - every { workloadA.onStop(any()) } throws IllegalStateException("Staged") - - val workload = - SimWorkloads.chain( - workloadA, - SimWorkloads.runtime(1000, 1.0), - ) - - assertThrows { machine.runWorkload(workload) } - - assertEquals(1000, timeSource.millis()) - } - - @Test - fun testStopFailureSecond() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - val workloadA = spyk(SimRuntimeWorkload(1000, 1.0)) - every { workloadA.onStop(any()) } throws IllegalStateException("Staged") - - val workload = - SimWorkloads.chain( - SimWorkloads.runtime(1000, 1.0), - workloadA, - SimWorkloads.runtime(1000, 1.0), - ) - - assertThrows { machine.runWorkload(workload) } - - assertEquals(2000, timeSource.millis()) - } - -// @Test - fun testStartAndStopFailure() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - val workloadA = mockk() - every { workloadA.onStart(any()) } throws IllegalStateException() - every { workloadA.onStop(any()) } throws IllegalStateException() - - val workload = - SimWorkloads.chain( - SimRuntimeWorkload(1000, 1.0), - workloadA, - ) - - val exc = assertThrows { machine.runWorkload(workload) } - - assertEquals(2, exc.cause!!.suppressedExceptions.size) - assertEquals(1000, timeSource.millis()) - } - -// @Test - fun testShutdownAndStopFailure() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - val workloadA = mockk() - every { workloadA.onStart(any()) } answers { (it.invocation.args[0] as SimMachineContext).shutdown(IllegalStateException()) } - every { workloadA.onStop(any()) } throws IllegalStateException() - - val workload = - SimWorkloads.chain( - SimRuntimeWorkload(1000, 1.0), - workloadA, - ) - - val exc = assertThrows { machine.runWorkload(workload) } - - assertEquals(1, exc.cause!!.suppressedExceptions.size) - assertEquals(1000, timeSource.millis()) - } - -// @Test - fun testShutdownAndStartFailure() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - val workloadA = mockk(relaxUnitFun = true) - every { workloadA.onStart(any()) } answers { (it.invocation.args[0] as SimMachineContext).shutdown(IllegalStateException()) } - - val workloadB = mockk(relaxUnitFun = true) - every { workloadB.onStart(any()) } throws IllegalStateException() - - val workload = - SimWorkloads.chain( - SimRuntimeWorkload(1000, 1.0), - workloadA, - workloadB, - ) - - val exc = assertThrows { machine.runWorkload(workload) } - assertEquals(1, exc.cause!!.suppressedExceptions.size) - assertEquals(1000, timeSource.millis()) - } - - @Test - fun testSnapshot() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = SimBareMetalMachine.create(graph, machineModel) - val workload = - SimWorkloads.chain( - SimWorkloads.runtime(1000, 1.0), - SimWorkloads.runtime(1000, 1.0), - ) - - val job = launch { machine.runWorkload(workload) } - delay(500L) - - workload.makeSnapshot(500L) - val snapshot = workload.getSnapshot() - - job.join() - - assertEquals(2000, timeSource.millis()) - - machine.runWorkload(snapshot) - - assertEquals(4000, timeSource.millis()) - } -} 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 deleted file mode 100644 index edbc0571..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkloadTest.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.workload - -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows - -/** - * Test suite for [SimFlopsWorkload] class. - */ -class SimFlopsWorkloadTest { - @Test - fun testFlopsNonNegative() { - assertThrows("FLOPs must be non-negative") { - SimFlopsWorkload(-1, 1.0) - } - } - - @Test - fun testUtilizationNonZero() { - assertThrows("Utilization cannot be zero") { - SimFlopsWorkload(1, 0.0) - } - } - - @Test - fun testUtilizationPositive() { - assertThrows("Utilization cannot be negative") { - SimFlopsWorkload(1, -1.0) - } - } - - @Test - fun testUtilizationNotLargerThanOne() { - assertThrows("Utilization cannot be larger than one") { - SimFlopsWorkload(1, 2.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 deleted file mode 100644 index a53f6c65..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt +++ /dev/null @@ -1,160 +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 kotlinx.coroutines.delay -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.opendc.simulator.compute.SimBareMetalMachine -import org.opendc.simulator.compute.model.Cpu -import org.opendc.simulator.compute.model.MachineModel -import org.opendc.simulator.compute.model.MemoryUnit -import org.opendc.simulator.compute.runWorkload -import org.opendc.simulator.flow2.FlowEngine -import org.opendc.simulator.kotlin.runSimulation - -/** - * Test suite for the [SimTraceWorkloadTest] class. - */ -class SimTraceWorkloadTest { - private lateinit var machineModel: MachineModel - - @BeforeEach - fun setUp() { - machineModel = - MachineModel( - Cpu( - 0, - 2, - 1000.0, - "Intel", - "Xeon", - "amd64", - ), - // memory - MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000 * 4), - ) - } - - @Test - fun testSmoke() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - 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), - ).createWorkload(0) - - machine.runWorkload(workload) - - assertEquals(4000, timeSource.millis()) - } - -// @Test // fixme: Fix delayed start and enable test - fun testOffset() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - 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), - ).createWorkload(1000) - - machine.runWorkload(workload) - - assertEquals(5000, timeSource.millis()) // fixme: should be 5000 but this is 4000 for now to make all tests succeed - } - - @Test - fun testSkipFragment() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - 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), - ).createWorkload(0) - - delay(1000L) - machine.runWorkload(workload) - - assertEquals(4000, timeSource.millis()) - } - - @Test - fun testZeroCores() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val machine = - SimBareMetalMachine.create( - graph, - machineModel, - ) - - 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), - ).createWorkload(0) - - machine.runWorkload(workload) - - assertEquals(4000, timeSource.millis()) - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/jmh/kotlin/org/opendc/simulator/flow2/FlowBenchmarks.kt b/opendc-simulator/opendc-simulator-flow/src/jmh/kotlin/org/opendc/simulator/flow2/FlowBenchmarks.kt index 6bf9c2a2..0ab051a4 100644 --- a/opendc-simulator/opendc-simulator-flow/src/jmh/kotlin/org/opendc/simulator/flow2/FlowBenchmarks.kt +++ b/opendc-simulator/opendc-simulator-flow/src/jmh/kotlin/org/opendc/simulator/flow2/FlowBenchmarks.kt @@ -93,8 +93,8 @@ class FlowBenchmarks { val sinkA = SimpleFlowSink(graph, 3000.0f) val sinkB = SimpleFlowSink(graph, 3000.0f) - graph.connect(switch.newOutput(), sinkA.input) - graph.connect(switch.newOutput(), sinkB.input) + graph.connect(switch.newOutPort(), sinkA.input) + graph.connect(switch.newOutPort(), sinkB.input) val source = TraceFlowSource(graph, trace) graph.connect(source.output, switch.newInput()) @@ -111,8 +111,8 @@ class FlowBenchmarks { val sinkA = SimpleFlowSink(graph, 3000.0f) val sinkB = SimpleFlowSink(graph, 3000.0f) - graph.connect(switch.newOutput(), sinkA.input) - graph.connect(switch.newOutput(), sinkB.input) + graph.connect(switch.newOutPort(), sinkA.input) + graph.connect(switch.newOutPort(), sinkB.input) repeat(3) { launch { diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/Multiplexer.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/Multiplexer.java new file mode 100644 index 00000000..0af2499a --- /dev/null +++ b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/Multiplexer.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2024 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; + +import java.util.ArrayList; +import java.util.Arrays; +import org.opendc.simulator.engine.FlowConsumer; +import org.opendc.simulator.engine.FlowEdge; +import org.opendc.simulator.engine.FlowGraph; +import org.opendc.simulator.engine.FlowNode; +import org.opendc.simulator.engine.FlowSupplier; + +public class Multiplexer extends FlowNode implements FlowSupplier, FlowConsumer { + private ArrayList consumerEdges = new ArrayList<>(); + private FlowEdge supplierEdge; + + private ArrayList demands = new ArrayList<>(); // What is demanded by the consumers + private ArrayList supplies = new ArrayList<>(); // What is supplied to the consumers + + private float totalDemand; // The total demand of all the consumers + private float totalSupply; // The total supply from the supplier + private float capacity; // What is the max capacity + + public Multiplexer(FlowGraph graph) { + super(graph); + } + + public float getTotalDemand() { + return totalDemand; + } + + public float getTotalSupply() { + return totalSupply; + } + + public float getCapacity() { + return capacity; + } + + public long onUpdate(long now) { + + if (this.totalDemand > this.capacity) { + redistributeSupply(this.consumerEdges, this.supplies, this.capacity); + } else { + for (int i = 0; i < this.demands.size(); i++) { + this.supplies.set(i, this.demands.get(i)); + } + } + + float totalSupply = 0; + for (int i = 0; i < this.consumerEdges.size(); i++) { + this.pushSupply(this.consumerEdges.get(i), this.supplies.get(i)); + totalSupply += this.supplies.get(i); + } + + // Only update supplier if supply has changed + if (this.totalSupply != totalSupply) { + this.totalSupply = totalSupply; + + pushDemand(this.supplierEdge, this.totalSupply); + } + + return Long.MAX_VALUE; + } + + private static float redistributeSupply( + ArrayList consumerEdges, ArrayList supplies, float capacity) { + final long[] consumers = new long[consumerEdges.size()]; + + for (int i = 0; i < consumers.length; i++) { + FlowEdge consumer = consumerEdges.get(i); + + if (consumer == null) { + break; + } + + consumers[i] = ((long) Float.floatToRawIntBits(consumer.getDemand()) << 32) | (i & 0xFFFFFFFFL); + } + Arrays.sort(consumers); + + float availableCapacity = capacity; + int inputSize = consumers.length; + + for (int i = 0; i < inputSize; i++) { + long v = consumers[i]; + int slot = (int) v; + float d = Float.intBitsToFloat((int) (v >> 32)); + + if (d == 0.0) { + continue; + } + + float availableShare = availableCapacity / (inputSize - i); + float r = Math.min(d, availableShare); + + supplies.set(slot, r); // Update the rates + availableCapacity -= r; + } + + // Return the used capacity + return capacity - availableCapacity; + } + + /** + * Add a new consumer. + * Set its demand and supply to 0.0 + */ + @Override + public void addConsumerEdge(FlowEdge consumerEdge) { + this.consumerEdges.add(consumerEdge); + this.demands.add(0f); + this.supplies.add(0f); + } + + @Override + public void addSupplierEdge(FlowEdge supplierEdge) { + this.supplierEdge = supplierEdge; + this.capacity = supplierEdge.getCapacity(); + this.totalSupply = 0; + } + + @Override + public void removeConsumerEdge(FlowEdge consumerEdge) { + int idx = this.consumerEdges.indexOf(consumerEdge); + + if (idx == -1) { + return; + } + + this.totalDemand -= consumerEdge.getDemand(); + + this.consumerEdges.remove(idx); + this.demands.remove(idx); + this.supplies.remove(idx); + + this.invalidate(); + } + + @Override + public void removeSupplierEdge(FlowEdge supplierEdge) { + this.supplierEdge = null; + this.capacity = 0; + this.totalSupply = 0; + } + + @Override + public void handleDemand(FlowEdge consumerEdge, float newDemand) { + int idx = consumerEdges.indexOf(consumerEdge); + + if (idx == -1) { + System.out.println("Error (Multiplexer): Demand pushed by an unknown consumer"); + return; + } + + float prevDemand = demands.get(idx); + demands.set(idx, newDemand); + + this.totalDemand += (newDemand - prevDemand); + } + + @Override + public void handleSupply(FlowEdge supplierEdge, float newSupply) { + if (newSupply == this.totalSupply) { + return; + } + + this.totalSupply = newSupply; + } + + @Override + public void pushDemand(FlowEdge supplierEdge, float newDemand) { + this.supplierEdge.pushDemand(newDemand); + } + + @Override + public void pushSupply(FlowEdge consumerEdge, float newSupply) { + int idx = consumerEdges.indexOf(consumerEdge); + + if (idx == -1) { + System.out.println("Error (Multiplexer): pushing supply to an unknown consumer"); + } + + if (newSupply == supplies.get(idx)) { + return; + } + + supplies.set(idx, newSupply); + consumerEdge.pushSupply(newSupply); + } +} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowConsumer.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowConsumer.java new file mode 100644 index 00000000..7ba5dea7 --- /dev/null +++ b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowConsumer.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024 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.engine; + +public interface FlowConsumer { + + void handleSupply(FlowEdge supplierEdge, float newSupply); + + void pushDemand(FlowEdge supplierEdge, float newDemand); + + void addSupplierEdge(FlowEdge supplierEdge); + + void removeSupplierEdge(FlowEdge supplierEdge); +} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowEdge.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowEdge.java new file mode 100644 index 00000000..0edc9e68 --- /dev/null +++ b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowEdge.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2024 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.engine; + +/** + * An edge that connects two FlowStages. + * A connection between FlowStages always consist of a FlowStage that demands + * something, and a FlowStage that Delivers something + * For instance, this could be the connection between a workload, and its machine + */ +public class FlowEdge { + private FlowConsumer consumer; + private FlowSupplier supplier; + + private float demand = 0.0f; + private float supply = 0.0f; + + private float capacity; + + public FlowEdge(FlowConsumer consumer, FlowSupplier supplier) { + if (!(consumer instanceof FlowNode)) { + throw new IllegalArgumentException("Flow consumer is not a FlowNode"); + } + if (!(supplier instanceof FlowNode)) { + throw new IllegalArgumentException("Flow consumer is not a FlowNode"); + } + + this.consumer = consumer; + this.supplier = supplier; + + this.capacity = supplier.getCapacity(); + + this.consumer.addSupplierEdge(this); + this.supplier.addConsumerEdge(this); + } + + public void close() { + if (this.consumer != null) { + this.consumer.removeSupplierEdge(this); + this.consumer = null; + } + + if (this.supplier != null) { + this.supplier.removeConsumerEdge(this); + this.supplier = null; + } + } + + public FlowConsumer getConsumer() { + return consumer; + } + + public FlowSupplier getSupplier() { + return supplier; + } + + public float getCapacity() { + return capacity; + } + + public float getDemand() { + return this.demand; + } + + public float getSupply() { + return this.supply; + } + + /** + * Push new demand from the Consumer to the Supplier + */ + public void pushDemand(float newDemand) { + if (newDemand == this.demand) { + return; + } + + this.demand = newDemand; + this.supplier.handleDemand(this, newDemand); + ((FlowNode) this.supplier).invalidate(); + } + + /** + * Push new supply from the Supplier to the Consumer + */ + public void pushSupply(float newSupply) { + if (newSupply == this.supply) { + return; + } + + this.supply = newSupply; + this.consumer.handleSupply(this, newSupply); + ((FlowNode) this.consumer).invalidate(); + } +} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowEngine.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowEngine.java new file mode 100644 index 00000000..10af7c51 --- /dev/null +++ b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowEngine.java @@ -0,0 +1,204 @@ +/* + * 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.engine; + +import java.time.Clock; +import java.time.InstantSource; +import kotlin.coroutines.CoroutineContext; +import org.opendc.common.Dispatcher; + +/** + * A {@link FlowEngine} simulates a generic flow network. + *

+ * The engine centralizes the scheduling logic of state updates of flow connections, allowing update propagation + * to happen more efficiently. and overall, reducing the work necessary to transition into a steady state. + */ +public final class FlowEngine implements Runnable { + /** + * The queue of {@link FlowNode} updates that are scheduled for immediate execution. + */ + private final FlowNodeQueue queue = new FlowNodeQueue(256); + + /** + * A priority queue containing the {@link FlowNode} updates to be scheduled in the future. + */ + private final FlowTimerQueue timerQueue = new FlowTimerQueue(256); + + /** + * The stack of engine invocations to occur in the future. + */ + private final InvocationStack futureInvocations = new InvocationStack(256); + + /** + * A flag to indicate that the engine is active. + */ + private boolean active; + + private final Dispatcher dispatcher; + private final InstantSource clock; + + /** + * Create a new {@link FlowEngine} instance using the specified {@link CoroutineContext} and {@link InstantSource}. + */ + public static FlowEngine create(Dispatcher dispatcher) { + return new FlowEngine(dispatcher); + } + + FlowEngine(Dispatcher dispatcher) { + this.dispatcher = dispatcher; + this.clock = dispatcher.getTimeSource(); + } + + /** + * Obtain the (virtual) {@link Clock} driving the simulation. + */ + public InstantSource getClock() { + return clock; + } + + /** + * Return a new {@link FlowGraph} that can be used to build a flow network. + */ + public FlowGraph newGraph() { + return new FlowGraph(this); + } + + /** + * Enqueue the specified {@link FlowNode} to be updated immediately during the active engine cycle. + *

+ * This method should be used when the state of a flow context is invalidated/interrupted and needs to be + * re-computed. + */ + void scheduleImmediate(long now, FlowNode ctx) { + scheduleImmediateInContext(ctx); + + // In-case the engine is already running in the call-stack, return immediately. The changes will be picked + // up by the active engine. + if (active) { + return; + } + + trySchedule(futureInvocations, now, now); + } + + /** + * Enqueue the specified {@link FlowNode} to be updated immediately during the active engine cycle. + *

+ * This method should be used when the state of a flow context is invalidated/interrupted and needs to be + * re-computed. + *

+ * This method should only be invoked while inside an engine cycle. + */ + void scheduleImmediateInContext(FlowNode ctx) { + queue.add(ctx); + } + + /** + * Enqueue the specified {@link FlowNode} to be updated at its updated deadline. + */ + void scheduleDelayed(FlowNode ctx) { + scheduleDelayedInContext(ctx); + + // In-case the engine is already running in the call-stack, return immediately. The changes will be picked + // up by the active engine. + if (active) { + return; + } + + long deadline = timerQueue.peekDeadline(); + if (deadline != Long.MAX_VALUE) { + trySchedule(futureInvocations, clock.millis(), deadline); + } + } + + /** + * Enqueue the specified {@link FlowNode} to be updated at its updated deadline. + *

+ * This method should only be invoked while inside an engine cycle. + */ + void scheduleDelayedInContext(FlowNode ctx) { + FlowTimerQueue timerQueue = this.timerQueue; + timerQueue.enqueue(ctx); + } + + /** + * Run all the enqueued actions for the specified timestamp (now). + */ + private void doRunEngine(long now) { + final FlowNodeQueue queue = this.queue; + final FlowTimerQueue timerQueue = this.timerQueue; + + try { + // Mark the engine as active to prevent concurrent calls to this method + active = true; + + // Execute all scheduled updates at current timestamp + while (true) { + final FlowNode ctx = timerQueue.poll(now); + if (ctx == null) { + break; + } + + ctx.update(now); + } + + // Execute all immediate updates + while (true) { + final FlowNode ctx = queue.poll(); + if (ctx == null) { + break; + } + + ctx.update(now); + } + } finally { + active = false; + } + + // Schedule an engine invocation for the next update to occur. + long headDeadline = timerQueue.peekDeadline(); + if (headDeadline != Long.MAX_VALUE && headDeadline >= now) { + trySchedule(futureInvocations, now, headDeadline); + } + } + + @Override + public void run() { + doRunEngine(futureInvocations.poll()); + } + + /** + * Try to schedule an engine invocation at the specified [target]. + * + * @param scheduled The queue of scheduled invocations. + * @param now The current virtual timestamp. + * @param target The virtual timestamp at which the engine invocation should happen. + */ + private void trySchedule(InvocationStack scheduled, long now, long target) { + // Only schedule a new scheduler invocation in case the target is earlier than all other pending + // scheduler invocations + if (scheduled.tryAdd(target)) { + dispatcher.schedule(target - now, this); + } + } +} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowGraph.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowGraph.java new file mode 100644 index 00000000..d82b542b --- /dev/null +++ b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowGraph.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2024 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.engine; + +import java.util.ArrayList; +import java.util.HashMap; + +public class FlowGraph { + private final FlowEngine engine; + private final ArrayList nodes = new ArrayList<>(); + private final ArrayList edges = new ArrayList<>(); + private final HashMap> nodeToEdge = new HashMap<>(); + + public FlowGraph(FlowEngine engine) { + this.engine = engine; + } + + /** + * Return the {@link FlowEngine} driving the simulation of the graph. + */ + public FlowEngine getEngine() { + return engine; + } + + /** + * Create a new {@link FlowNode} representing a node in the flow network. + */ + public void addNode(FlowNode node) { + if (nodes.contains(node)) { + System.out.println("Node already exists"); + } + nodes.add(node); + nodeToEdge.put(node, new ArrayList<>()); + long now = this.engine.getClock().millis(); + node.invalidate(now); + } + + /** + * Internal method to remove the specified {@link FlowNode} from the graph. + */ + public void removeNode(FlowNode node) { + + // Remove all edges connected to node + final ArrayList connectedEdges = nodeToEdge.get(node); + while (connectedEdges.size() > 0) { + removeEdge(connectedEdges.get(0)); + } + + nodeToEdge.remove(node); + + // remove the node + nodes.remove(node); + } + + /** + * Add an edge between the specified consumer and supplier in this graph. + */ + public void addEdge(FlowConsumer flowConsumer, FlowSupplier flowSupplier) { + // Check if the consumer and supplier are both FlowNodes + if (!(flowConsumer instanceof FlowNode)) { + throw new IllegalArgumentException("Flow consumer is not a FlowNode"); + } + if (!(flowSupplier instanceof FlowNode)) { + throw new IllegalArgumentException("Flow consumer is not a FlowNode"); + } + + // Check of the consumer and supplier are present in this graph + if (!(this.nodes.contains((FlowNode) flowConsumer))) { + throw new IllegalArgumentException("The consumer is not a node in this graph"); + } + if (!(this.nodes.contains((FlowNode) flowSupplier))) { + throw new IllegalArgumentException("The consumer is not a node in this graph"); + } + + final FlowEdge flowEdge = new FlowEdge(flowConsumer, flowSupplier); + + edges.add(flowEdge); + + nodeToEdge.get((FlowNode) flowConsumer).add(flowEdge); + nodeToEdge.get((FlowNode) flowSupplier).add(flowEdge); + } + + public void removeEdge(FlowEdge flowEdge) { + final FlowConsumer consumer = flowEdge.getConsumer(); + final FlowSupplier supplier = flowEdge.getSupplier(); + nodeToEdge.get((FlowNode) consumer).remove(flowEdge); + nodeToEdge.get((FlowNode) supplier).remove(flowEdge); + + edges.remove(flowEdge); + flowEdge.close(); + } +} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowNode.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowNode.java new file mode 100644 index 00000000..d1faf465 --- /dev/null +++ b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowNode.java @@ -0,0 +1,191 @@ +/* + * 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.engine; + +import java.time.InstantSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A {@link FlowNode} represents a node in a {@link FlowGraph}. + */ +public abstract class FlowNode { + private static final Logger LOGGER = LoggerFactory.getLogger(FlowNode.class); + + protected enum NodeState { + PENDING, // Stage is active, but is not running any updates + UPDATING, // Stage is active, and running an update + INVALIDATED, // Stage is deemed invalid, and should run an update + CLOSING, // Stage is being closed, final updates can still be run + CLOSED // Stage is closed and should not run any updates + } + + protected NodeState nodeState = NodeState.PENDING; + + /** + * The deadline of the stage after which an update should run. + */ + long deadline = Long.MAX_VALUE; + + /** + * The index of the timer in the {@link FlowTimerQueue}. + */ + int timerIndex = -1; + + protected InstantSource clock; + protected FlowGraph parentGraph; + protected FlowEngine engine; + + /** + * Construct a new {@link FlowNode} instance. + * + * @param parentGraph The {@link FlowGraph} this stage belongs to. + */ + public FlowNode(FlowGraph parentGraph) { + this.parentGraph = parentGraph; + this.engine = parentGraph.getEngine(); + this.clock = engine.getClock(); + + this.parentGraph.addNode(this); + } + + /** + * Return the {@link FlowGraph} to which this stage belongs. + */ + public FlowGraph getGraph() { + return parentGraph; + } + + /** + * Return the current deadline of the {@link FlowNode}'s timer (in milliseconds after epoch). + */ + public long getDeadline() { + return deadline; + } + + public void setDeadline(long deadline) { + this.deadline = deadline; + } + + public void setTimerIndex(int index) { + this.timerIndex = index; + } + /** + * Invalidate the {@link FlowNode} forcing the stage to update. + * + *

+ * This method is similar to {@link #invalidate()}, but allows the user to manually pass the current timestamp to + * prevent having to re-query the clock. This method should not be called during an update. + */ + public void invalidate(long now) { + // If there is already an update running, + // notify the update, that a next update should be run after + if (this.nodeState == NodeState.UPDATING) { + this.nodeState = NodeState.INVALIDATED; + } else { + engine.scheduleImmediate(now, this); + } + } + + /** + * Invalidate the {@link FlowNode} forcing the stage to update. + */ + public void invalidate() { + invalidate(clock.millis()); + } + + /** + * Update the state of the stage. + */ + public void update(long now) { + this.nodeState = NodeState.UPDATING; + + long newDeadline = this.deadline; + + try { + newDeadline = this.onUpdate(now); + } catch (Exception e) { + doFail(e); + } + + // Check whether the stage is marked as closing. + if (this.nodeState == NodeState.INVALIDATED) { + newDeadline = now; + } + if (this.nodeState == NodeState.CLOSING) { + closeNode(); + return; + } + + this.deadline = newDeadline; + + // Update the timer queue with the new deadline + engine.scheduleDelayedInContext(this); + + this.nodeState = NodeState.PENDING; + } + + /** + * This method is invoked when the one of the stage's InPorts or OutPorts is invalidated. + * + * @param now The virtual timestamp in milliseconds after epoch at which the update is occurring. + * @return The next deadline for the stage. + */ + public abstract long onUpdate(long now); + + /** + * This method is invoked when an uncaught exception is caught by the engine. When this happens, the + */ + void doFail(Throwable cause) { + LOGGER.warn("Uncaught exception (closing stage)", cause); + + closeNode(); + } + + /** + * This method is invoked when the {@link FlowNode} exits successfully or due to failure. + */ + public void closeNode() { + if (this.nodeState == NodeState.CLOSED) { + // LOGGER.warn("Flowstage:doClose() => Tried closing a stage that was already closed"); + return; + } + + // If this stage is running an update, notify it that is should close after. + if (this.nodeState == NodeState.UPDATING) { + // LOGGER.warn("Flowstage:doClose() => Tried closing a stage, but update was active"); + this.nodeState = NodeState.CLOSING; + return; + } + + // Mark the stage as closed + this.nodeState = NodeState.CLOSED; + + // Remove stage from parent graph + this.parentGraph.removeNode(this); + + // Remove stage from the timer queue + this.deadline = Long.MAX_VALUE; + this.engine.scheduleDelayedInContext(this); + } +} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowNodeQueue.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowNodeQueue.java new file mode 100644 index 00000000..37b3c65b --- /dev/null +++ b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowNodeQueue.java @@ -0,0 +1,109 @@ +/* + * 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.engine; + +import java.util.ArrayDeque; +import java.util.Arrays; + +/** + * A specialized {@link ArrayDeque} implementation that contains the {@link FlowNode}s + * that have been updated during the engine cycle and should converge. + *

+ * By using a specialized class, we reduce the overhead caused by type-erasure. + */ +final class FlowNodeQueue { + /** + * The array of elements in the queue. + */ + private FlowNode[] elements; + + private int head = 0; + private int tail = 0; + + public FlowNodeQueue(int initialCapacity) { + elements = new FlowNode[initialCapacity]; + } + + /** + * Add the specified context to the queue. + */ + void add(FlowNode ctx) { + final FlowNode[] es = elements; + int tail = this.tail; + + es[tail] = ctx; + + tail = inc(tail, es.length); + this.tail = tail; + + if (head == tail) { + doubleCapacity(); + } + } + + /** + * Remove a {@link FlowNode} from the queue or null if the queue is empty. + */ + FlowNode poll() { + final FlowNode[] es = elements; + int head = this.head; + FlowNode ctx = es[head]; + + if (ctx != null) { + es[head] = null; + this.head = inc(head, es.length); + } + + return ctx; + } + + /** + * Doubles the capacity of this deque + */ + private void doubleCapacity() { + int oldCapacity = elements.length; + int newCapacity = oldCapacity + (oldCapacity >> 1); + if (newCapacity < 0) { + throw new IllegalStateException("Sorry, deque too big"); + } + + final FlowNode[] es = elements = Arrays.copyOf(elements, newCapacity); + + // Exceptionally, here tail == head needs to be disambiguated + if (tail < head || (tail == head && es[head] != null)) { + // wrap around; slide first leg forward to end of array + int newSpace = newCapacity - oldCapacity; + System.arraycopy(es, head, es, head + newSpace, oldCapacity - head); + for (int i = head, to = (head += newSpace); i < to; i++) es[i] = null; + } + } + + /** + * Circularly increments i, mod modulus. + * Precondition and postcondition: 0 <= i < modulus. + */ + private static int inc(int i, int modulus) { + if (++i >= modulus) i = 0; + return i; + } +} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowSupplier.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowSupplier.java new file mode 100644 index 00000000..87729fca --- /dev/null +++ b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowSupplier.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 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.engine; + +public interface FlowSupplier { + + void handleDemand(FlowEdge consumerEdge, float newDemand); + + void pushSupply(FlowEdge consumerEdge, float newSupply); + + void addConsumerEdge(FlowEdge consumerEdge); + + void removeConsumerEdge(FlowEdge consumerEdge); + + float getCapacity(); +} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowTimerQueue.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowTimerQueue.java new file mode 100644 index 00000000..1e348b10 --- /dev/null +++ b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/FlowTimerQueue.java @@ -0,0 +1,205 @@ +/* + * 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.engine; + +import java.util.Arrays; + +/** + * A specialized priority queue for timers of {@link FlowNode}s. + *

+ * By using a specialized priority queue, we reduce the overhead caused by the default priority queue implementation + * being generic. + */ +public final class FlowTimerQueue { + /** + * Array representation of binary heap of {@link FlowNode} instances. + */ + private FlowNode[] queue; + + /** + * The number of elements in the priority queue. + */ + private int size = 0; + + /** + * Construct a {@link FlowTimerQueue} with the specified initial capacity. + * + * @param initialCapacity The initial capacity of the queue. + */ + public FlowTimerQueue(int initialCapacity) { + this.queue = new FlowNode[initialCapacity]; + } + + /** + * Enqueue a timer for the specified context or update the existing timer. + */ + public void enqueue(FlowNode node) { + FlowNode[] es = queue; + int k = node.timerIndex; + + if (node.deadline != Long.MAX_VALUE) { + if (k >= 0) { + update(es, node, k); + } else { + add(es, node); + } + } else if (k >= 0) { + delete(es, k); + } + } + + /** + * Retrieve the head of the queue if its deadline does not exceed now. + * + * @param now The timestamp that the deadline of the head of the queue should not exceed. + * @return The head of the queue if its deadline does not exceed now, otherwise null. + */ + public FlowNode poll(long now) { + if (this.size == 0) { + return null; + } + + final FlowNode[] es = queue; + final FlowNode head = es[0]; + + if (now < head.deadline) { + return null; + } + + int n = size - 1; + this.size = n; + final FlowNode next = es[n]; + es[n] = null; // Clear the last element of the queue + + if (n > 0) { + siftDown(0, next, es, n); + } + + head.timerIndex = -1; + return head; + } + + /** + * Find the earliest deadline in the queue. + */ + public long peekDeadline() { + if (this.size > 0) { + return this.queue[0].deadline; + } + + return Long.MAX_VALUE; + } + + /** + * Add a new entry to the queue. + */ + private void add(FlowNode[] es, FlowNode node) { + if (this.size >= es.length) { + // Re-fetch the resized array + es = grow(); + } + + siftUp(this.size, node, es); + + this.size++; + } + + /** + * Update the deadline of an existing entry in the queue. + */ + private void update(FlowNode[] es, FlowNode node, int k) { + if (k > 0) { + int parent = (k - 1) >>> 1; + if (es[parent].deadline > node.deadline) { + siftUp(k, node, es); + return; + } + } + + siftDown(k, node, es, this.size); + } + + /** + * Deadline an entry from the queue. + */ + private void delete(FlowNode[] es, int k) { + int s = --this.size; + if (s == k) { + es[k] = null; // Element is last in the queue + } else { + FlowNode moved = es[s]; + es[s] = null; + + siftDown(k, moved, es, s); + + if (es[k] == moved) { + siftUp(k, moved, es); + } + } + } + + /** + * Increases the capacity of the array. + */ + private FlowNode[] grow() { + FlowNode[] queue = this.queue; + int oldCapacity = queue.length; + int newCapacity = oldCapacity + (oldCapacity >> 1); + + queue = Arrays.copyOf(queue, newCapacity); + this.queue = queue; + return queue; + } + + private static void siftUp(int k, FlowNode key, FlowNode[] es) { + while (k > 0) { + int parent = (k - 1) >>> 1; + FlowNode e = es[parent]; + if (key.deadline >= e.deadline) break; + es[k] = e; + e.timerIndex = k; + k = parent; + } + es[k] = key; + key.timerIndex = k; + } + + private static void siftDown(int k, FlowNode key, FlowNode[] es, int n) { + int half = n >>> 1; // loop while a non-leaf + while (k < half) { + int child = (k << 1) + 1; // assume left child is least + FlowNode c = es[child]; + int right = child + 1; + if (right < n && c.deadline > es[right].deadline) c = es[child = right]; + + if (key.deadline <= c.deadline) break; + + es[k] = c; + c.timerIndex = k; + k = child; + } + + es[k] = key; + key.timerIndex = k; + } +} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/InvocationStack.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/InvocationStack.java new file mode 100644 index 00000000..15da2f23 --- /dev/null +++ b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/InvocationStack.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.engine; + +import java.util.Arrays; + +/** + * A specialized monotonic stack implementation for tracking the scheduled engine invocations. + *

+ * By using a specialized class, we reduce the overhead caused by type-erasure. + */ +public final class InvocationStack { + /** + * The array of elements in the stack. + */ + private long[] elements; + + private int head = -1; + + public InvocationStack(int initialCapacity) { + this.elements = new long[initialCapacity]; + Arrays.fill(this.elements, Long.MIN_VALUE); + } + + /** + * Try to add the specified invocation to the monotonic stack. + * + * @param invocation The timestamp of the invocation. + * @return true if the invocation was added, false otherwise. + */ + public boolean tryAdd(long invocation) { + final long[] es = this.elements; + int head = this.head; + + if (head < 0 || es[head] > invocation) { + es[head + 1] = invocation; + this.head = head + 1; + + if (head + 2 == es.length) { + doubleCapacity(); + } + + return true; + } + + return false; + } + + /** + * Remove the head invocation from the stack or return {@link Long#MAX_VALUE} if the stack is empty. + */ + public long poll() { + int head = this.head--; + + if (head >= 0) { + return this.elements[head]; + } + + return Long.MAX_VALUE; + } + + /** + * Doubles the capacity of this deque + */ + private void doubleCapacity() { + int oldCapacity = this.elements.length; + int newCapacity = oldCapacity + (oldCapacity >> 1); + if (newCapacity < 0) { + throw new IllegalStateException("Sorry, deque too big"); + } + + this.elements = Arrays.copyOf(this.elements, newCapacity); + } +} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowEngine.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowEngine.java deleted file mode 100644 index c0f52505..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowEngine.java +++ /dev/null @@ -1,256 +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.flow2; - -import java.time.Clock; -import java.time.InstantSource; -import java.util.ArrayList; -import java.util.List; -import kotlin.coroutines.CoroutineContext; -import org.opendc.common.Dispatcher; - -/** - * A {@link FlowEngine} simulates a generic flow network. - *

- * The engine centralizes the scheduling logic of state updates of flow connections, allowing update propagation - * to happen more efficiently. and overall, reducing the work necessary to transition into a steady state. - */ -public final class FlowEngine implements Runnable { - /** - * The queue of {@link FlowStage} updates that are scheduled for immediate execution. - */ - private final FlowStageQueue queue = new FlowStageQueue(256); - - /** - * A priority queue containing the {@link FlowStage} updates to be scheduled in the future. - */ - private final FlowTimerQueue timerQueue = new FlowTimerQueue(256); - - /** - * The stack of engine invocations to occur in the future. - */ - private final InvocationStack futureInvocations = new InvocationStack(256); - - /** - * A flag to indicate that the engine is active. - */ - private boolean active; - - private final Dispatcher dispatcher; - private final InstantSource clock; - - /** - * Create a new {@link FlowEngine} instance using the specified {@link CoroutineContext} and {@link InstantSource}. - */ - public static FlowEngine create(Dispatcher dispatcher) { - return new FlowEngine(dispatcher); - } - - FlowEngine(Dispatcher dispatcher) { - this.dispatcher = dispatcher; - this.clock = dispatcher.getTimeSource(); - } - - /** - * Obtain the (virtual) {@link Clock} driving the simulation. - */ - public InstantSource getClock() { - return clock; - } - - /** - * Return a new {@link FlowGraph} that can be used to build a flow network. - */ - public FlowGraph newGraph() { - return new RootGraph(this); - } - - /** - * Enqueue the specified {@link FlowStage} to be updated immediately during the active engine cycle. - *

- * This method should be used when the state of a flow context is invalidated/interrupted and needs to be - * re-computed. - */ - void scheduleImmediate(long now, FlowStage ctx) { - scheduleImmediateInContext(ctx); - - // In-case the engine is already running in the call-stack, return immediately. The changes will be picked - // up by the active engine. - if (active) { - return; - } - - trySchedule(futureInvocations, now, now); - } - - /** - * Enqueue the specified {@link FlowStage} to be updated immediately during the active engine cycle. - *

- * This method should be used when the state of a flow context is invalidated/interrupted and needs to be - * re-computed. - *

- * This method should only be invoked while inside an engine cycle. - */ - void scheduleImmediateInContext(FlowStage ctx) { - queue.add(ctx); - } - - /** - * Enqueue the specified {@link FlowStage} to be updated at its updated deadline. - */ - void scheduleDelayed(FlowStage ctx) { - scheduleDelayedInContext(ctx); - - // In-case the engine is already running in the call-stack, return immediately. The changes will be picked - // up by the active engine. - if (active) { - return; - } - - long deadline = timerQueue.peekDeadline(); - if (deadline != Long.MAX_VALUE) { - trySchedule(futureInvocations, clock.millis(), deadline); - } - } - - /** - * Enqueue the specified {@link FlowStage} to be updated at its updated deadline. - *

- * This method should only be invoked while inside an engine cycle. - */ - void scheduleDelayedInContext(FlowStage ctx) { - FlowTimerQueue timerQueue = this.timerQueue; - timerQueue.enqueue(ctx); - } - - /** - * Run all the enqueued actions for the specified timestamp (now). - */ - private void doRunEngine(long now) { - final FlowStageQueue queue = this.queue; - final FlowTimerQueue timerQueue = this.timerQueue; - - try { - // Mark the engine as active to prevent concurrent calls to this method - active = true; - - // Execute all scheduled updates at current timestamp - while (true) { - final FlowStage ctx = timerQueue.poll(now); - if (ctx == null) { - break; - } - - ctx.onUpdate(now); - } - - // Execute all immediate updates - while (true) { - final FlowStage ctx = queue.poll(); - if (ctx == null) { - break; - } - - ctx.onUpdate(now); - } - } finally { - active = false; - } - - // Schedule an engine invocation for the next update to occur. - long headDeadline = timerQueue.peekDeadline(); - if (headDeadline != Long.MAX_VALUE && headDeadline >= now) { - trySchedule(futureInvocations, now, headDeadline); - } - } - - @Override - public void run() { - doRunEngine(futureInvocations.poll()); - } - - /** - * Try to schedule an engine invocation at the specified [target]. - * - * @param scheduled The queue of scheduled invocations. - * @param now The current virtual timestamp. - * @param target The virtual timestamp at which the engine invocation should happen. - */ - private void trySchedule(InvocationStack scheduled, long now, long target) { - // Only schedule a new scheduler invocation in case the target is earlier than all other pending - // scheduler invocations - if (scheduled.tryAdd(target)) { - dispatcher.schedule(target - now, this); - } - } - - /** - * Internal implementation of a root {@link FlowGraph}. - */ - private static final class RootGraph implements FlowGraphInternal { - private final FlowEngine engine; - private final List stages = new ArrayList<>(); - - public RootGraph(FlowEngine engine) { - this.engine = engine; - } - - @Override - public FlowEngine getEngine() { - return engine; - } - - @Override - public FlowStage newStage(FlowStageLogic logic) { - final FlowEngine engine = this.engine; - final FlowStage stage = new FlowStage(this, logic); - stages.add(stage); - long now = engine.getClock().millis(); - stage.invalidate(now); - return stage; - } - - @Override - public void connect(Outlet outlet, Inlet inlet) { - FlowGraphInternal.connect(this, outlet, inlet); - } - - @Override - public void disconnect(Outlet outlet) { - FlowGraphInternal.disconnect(this, outlet); - } - - @Override - public void disconnect(Inlet inlet) { - FlowGraphInternal.disconnect(this, inlet); - } - - /** - * Internal method to remove the specified {@link FlowStage} from the graph. - */ - @Override - public void detach(FlowStage stage) { - stages.remove(stage); - } - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowGraph.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowGraph.java deleted file mode 100644 index f45be6cd..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowGraph.java +++ /dev/null @@ -1,63 +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.flow2; - -/** - * A representation of a flow network. A flow network is a directed graph where each edge has a capacity and receives an - * amount of flow that cannot exceed the edge's capacity. - */ -public interface FlowGraph { - /** - * Return the {@link FlowEngine} driving the simulation of the graph. - */ - FlowEngine getEngine(); - - /** - * Create a new {@link FlowStage} representing a node in the flow network. - * - * @param logic The logic for handling the events of the stage. - */ - FlowStage newStage(FlowStageLogic logic); - - /** - * Add an edge between the specified outlet port and inlet port in this graph. - * - * @param outlet The outlet of the source from which the flow originates. - * @param inlet The inlet of the sink that should receive the flow. - */ - void connect(Outlet outlet, Inlet inlet); - - /** - * Disconnect the specified {@link Outlet} (if connected). - * - * @param outlet The outlet to disconnect. - */ - void disconnect(Outlet outlet); - - /** - * Disconnect the specified {@link Inlet} (if connected). - * - * @param inlet The inlet to disconnect. - */ - void disconnect(Inlet inlet); -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowGraphInternal.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowGraphInternal.java deleted file mode 100644 index 0f608b60..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowGraphInternal.java +++ /dev/null @@ -1,93 +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.flow2; - -/** - * Interface implemented by {@link FlowGraph} implementations. - */ -interface FlowGraphInternal extends FlowGraph { - /** - * Internal method to remove the specified {@link FlowStage} from the graph. - */ - void detach(FlowStage stage); - - /** - * Helper method to connect an outlet to an inlet. - */ - static void connect(FlowGraph graph, Outlet outlet, Inlet inlet) { - if (!(outlet instanceof OutPort) || !(inlet instanceof InPort)) { - throw new IllegalArgumentException("Invalid outlet or inlet passed to graph"); - } - - InPort inPort = (InPort) inlet; - OutPort outPort = (OutPort) outlet; - - if (!graph.equals(outPort.getGraph()) || !graph.equals(inPort.getGraph())) { - throw new IllegalArgumentException("Outlet or inlet does not belong to graph"); - } else if (outPort.input != null || inPort.output != null) { - throw new IllegalStateException("Inlet or outlet already connected"); - } - - outPort.input = inPort; - inPort.output = outPort; - - inPort.connect(); - outPort.connect(); - } - - /** - * Helper method to disconnect an outlet. - */ - static void disconnect(FlowGraph graph, Outlet outlet) { - if (!(outlet instanceof OutPort)) { - throw new IllegalArgumentException("Invalid outlet passed to graph"); - } - - OutPort outPort = (OutPort) outlet; - - if (!graph.equals(outPort.getGraph())) { - throw new IllegalArgumentException("Outlet or inlet does not belong to graph"); - } - - outPort.cancel(null); - outPort.complete(); - } - - /** - * Helper method to disconnect an inlet. - */ - static void disconnect(FlowGraph graph, Inlet inlet) { - if (!(inlet instanceof InPort)) { - throw new IllegalArgumentException("Invalid outlet passed to graph"); - } - - InPort inPort = (InPort) inlet; - - if (!graph.equals(inPort.getGraph())) { - throw new IllegalArgumentException("Outlet or inlet does not belong to graph"); - } - - inPort.finish(null); - inPort.cancel(null); - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowStage.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowStage.java deleted file mode 100644 index 25f87e04..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowStage.java +++ /dev/null @@ -1,312 +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.flow2; - -import java.time.InstantSource; -import java.util.HashMap; -import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * A {@link FlowStage} represents a node in a {@link FlowGraph}. - */ -public final class FlowStage { - private static final Logger LOGGER = LoggerFactory.getLogger(FlowStage.class); - - /** - * States of the flow stage. - */ - private static final int STAGE_PENDING = 0; // Stage is pending to be started - - private static final int STAGE_ACTIVE = 1; // Stage is actively running - private static final int STAGE_CLOSED = 2; // Stage is closed - private static final int STAGE_STATE = 0b11; // Mask for accessing the state of the flow stage - - /** - * Flags of the flow connection - */ - private static final int STAGE_INVALIDATE = 1 << 2; // The stage is invalidated - - private static final int STAGE_CLOSE = 1 << 3; // The stage should be closed - private static final int STAGE_UPDATE_ACTIVE = 1 << 4; // An update for the connection is active - private static final int STAGE_UPDATE_PENDING = 1 << 5; // An (immediate) update of the connection is pending - - /** - * The flags representing the state and pending actions for the stage. - */ - private int flags = STAGE_PENDING; - - /** - * The deadline of the stage after which an update should run. - */ - long deadline = Long.MAX_VALUE; - - /** - * The index of the timer in the {@link FlowTimerQueue}. - */ - int timerIndex = -1; - - final InstantSource clock; - private final FlowStageLogic logic; - final FlowGraphInternal parentGraph; - private final FlowEngine engine; - - private final Map inlets = new HashMap<>(); - private final Map outlets = new HashMap<>(); - private int nextInlet = 0; - private int nextOutlet = 0; - - /** - * Construct a new {@link FlowStage} instance. - * - * @param parentGraph The {@link FlowGraph} this stage belongs to. - * @param logic The logic of the stage. - */ - FlowStage(FlowGraphInternal parentGraph, FlowStageLogic logic) { - this.parentGraph = parentGraph; - this.logic = logic; - this.engine = parentGraph.getEngine(); - this.clock = engine.getClock(); - } - - /** - * Return the {@link FlowGraph} to which this stage belongs. - */ - public FlowGraph getGraph() { - return parentGraph; - } - - /** - * Return the {@link Inlet} (an in-going edge) with the specified name for this {@link FlowStage}. - * If an inlet with that name does not exist, a new one is allocated for the stage. - * - * @param name The name of the inlet. - * @return The {@link InPort} representing an {@link Inlet} with the specified name. - */ - public InPort getInlet(String name) { - return inlets.computeIfAbsent(name, (key) -> new InPort(this, key, nextInlet++)); - } - - /** - * Return the {@link Outlet} (an out-going edge) with the specified name for this {@link FlowStage}. - * If an outlet with that name does not exist, a new one is allocated for the stage. - * - * @param name The name of the outlet. - * @return The {@link OutPort} representing an {@link Outlet} with the specified name. - */ - public OutPort getOutlet(String name) { - return outlets.computeIfAbsent(name, (key) -> new OutPort(this, key, nextOutlet++)); - } - - /** - * Return the current deadline of the {@link FlowStage}'s timer (in milliseconds after epoch). - */ - public long getDeadline() { - return deadline; - } - - /** - * Set the deadline of the {@link FlowStage}'s timer. - * - * @param deadline The new deadline (in milliseconds after epoch) when the stage should be interrupted. - */ - public void setDeadline(long deadline) { - this.deadline = deadline; - - if ((flags & STAGE_UPDATE_ACTIVE) == 0) { - // Update the timer queue with the new deadline - engine.scheduleDelayed(this); - } - } - - /** - * Invalidate the {@link FlowStage} forcing the stage to update. - */ - public void invalidate() { - int flags = this.flags; - - if ((flags & STAGE_UPDATE_ACTIVE) == 0) { - scheduleImmediate(clock.millis(), flags | STAGE_INVALIDATE); - } - } - - /** - * Synchronously update the {@link FlowStage} at the current timestamp. - */ - public void sync() { - this.flags |= STAGE_INVALIDATE; - onUpdate(clock.millis()); - engine.scheduleDelayed(this); - } - - /** - * Close the {@link FlowStage} and disconnect all inlets and outlets. - */ - public void close() { - int flags = this.flags; - - if ((flags & STAGE_STATE) == STAGE_CLOSED) { - return; - } - - // Toggle the close bit. In case no update is active, schedule a new update. - if ((flags & STAGE_UPDATE_ACTIVE) != 0) { - this.flags = flags | STAGE_CLOSE; - } else { - scheduleImmediate(clock.millis(), flags | STAGE_CLOSE); - } - } - - /** - * Update the state of the flow stage. - * - * @param now The current virtual timestamp. - */ - void onUpdate(long now) { - int flags = this.flags; - int state = flags & STAGE_STATE; - - if (state == STAGE_ACTIVE) { - doUpdate(now, flags); - } else if (state == STAGE_PENDING) { - doStart(now, flags); - } - } - - /** - * Invalidate the {@link FlowStage} forcing the stage to update. - * - *

- * This method is similar to {@link #invalidate()}, but allows the user to manually pass the current timestamp to - * prevent having to re-query the clock. This method should not be called during an update. - */ - void invalidate(long now) { - scheduleImmediate(now, flags | STAGE_INVALIDATE); - } - - /** - * Schedule an immediate update for this stage. - */ - private void scheduleImmediate(long now, int flags) { - // In case an immediate update is already scheduled, no need to do anything - if ((flags & STAGE_UPDATE_PENDING) != 0) { - this.flags = flags; - return; - } - - // Mark the stage that there is an update pending - this.flags = flags | STAGE_UPDATE_PENDING; - - engine.scheduleImmediate(now, this); - } - - /** - * Start the stage. - */ - private void doStart(long now, int flags) { - // Update state before calling into the outside world, so it observes a consistent state - flags = flags | STAGE_ACTIVE | STAGE_UPDATE_ACTIVE; - - doUpdate(now, flags); - } - - /** - * Update the state of the stage. - */ - private void doUpdate(long now, int flags) { - long deadline = this.deadline; - long newDeadline = deadline; - - // Update the stage if: - // (1) the timer of the stage has expired. - // (2) one of the input ports is pushed, - // (3) one of the output ports is pulled, - if ((flags & STAGE_INVALIDATE) != 0 || deadline == now) { - // Update state before calling into the outside world, so it observes a consistent state - this.flags = (flags & ~STAGE_INVALIDATE) | STAGE_UPDATE_ACTIVE; - - try { - newDeadline = logic.onUpdate(this, now); - - // IMPORTANT: Re-fetch the flags after the callback might have changed those - flags = this.flags; - } catch (Exception e) { - doFail(e); - } - } - - // Check whether the stage is marked as closing. - if ((flags & STAGE_CLOSE) != 0) { - doClose(flags, null); - - // IMPORTANT: Re-fetch the flags after the callback might have changed those - flags = this.flags; - } - - // Indicate that no update is active anymore and flush the flags - this.flags = flags & ~(STAGE_UPDATE_ACTIVE | STAGE_UPDATE_PENDING); - this.deadline = newDeadline; - - // Update the timer queue with the new deadline - engine.scheduleDelayedInContext(this); - } - - /** - * This method is invoked when an uncaught exception is caught by the engine. When this happens, the - * {@link FlowStageLogic} "fails" and disconnects all its inputs and outputs. - */ - void doFail(Throwable cause) { - LOGGER.warn("Uncaught exception (closing stage)", cause); - - doClose(flags, cause); - } - - /** - * This method is invoked when the {@link FlowStageLogic} exits successfully or due to failure. - */ - private void doClose(int flags, Throwable cause) { - // Mark the stage as closed - this.flags = flags & ~(STAGE_STATE | STAGE_INVALIDATE | STAGE_CLOSE) | STAGE_CLOSED; - - // Remove stage from parent graph - parentGraph.detach(this); - - // Remove stage from the timer queue - setDeadline(Long.MAX_VALUE); - - // Cancel all input ports - for (InPort port : inlets.values()) { - if (port != null) { - port.cancel(cause); - } - } - - // Cancel all output ports - for (OutPort port : outlets.values()) { - if (port != null) { - port.fail(cause); - } - } - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowStageLogic.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowStageLogic.java deleted file mode 100644 index 70986a35..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowStageLogic.java +++ /dev/null @@ -1,38 +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.flow2; - -/** - * The {@link FlowStageLogic} interface is responsible for describing the behaviour of a {@link FlowStage} via - * out-going flows based on its potential inputs. - */ -public interface FlowStageLogic { - /** - * This method is invoked when the one of the stage's inlets or outlets is invalidated. - * - * @param ctx The context in which the stage runs. - * @param now The virtual timestamp in milliseconds after epoch at which the update is occurring. - * @return The next deadline for the stage. - */ - long onUpdate(FlowStage ctx, long now); -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowStageQueue.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowStageQueue.java deleted file mode 100644 index 56ec7702..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowStageQueue.java +++ /dev/null @@ -1,109 +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.flow2; - -import java.util.ArrayDeque; -import java.util.Arrays; - -/** - * A specialized {@link ArrayDeque} implementation that contains the {@link FlowStageLogic}s - * that have been updated during the engine cycle and should converge. - *

- * By using a specialized class, we reduce the overhead caused by type-erasure. - */ -final class FlowStageQueue { - /** - * The array of elements in the queue. - */ - private FlowStage[] elements; - - private int head = 0; - private int tail = 0; - - public FlowStageQueue(int initialCapacity) { - elements = new FlowStage[initialCapacity]; - } - - /** - * Add the specified context to the queue. - */ - void add(FlowStage ctx) { - final FlowStage[] es = elements; - int tail = this.tail; - - es[tail] = ctx; - - tail = inc(tail, es.length); - this.tail = tail; - - if (head == tail) { - doubleCapacity(); - } - } - - /** - * Remove a {@link FlowStage} from the queue or null if the queue is empty. - */ - FlowStage poll() { - final FlowStage[] es = elements; - int head = this.head; - FlowStage ctx = es[head]; - - if (ctx != null) { - es[head] = null; - this.head = inc(head, es.length); - } - - return ctx; - } - - /** - * Doubles the capacity of this deque - */ - private void doubleCapacity() { - int oldCapacity = elements.length; - int newCapacity = oldCapacity + (oldCapacity >> 1); - if (newCapacity < 0) { - throw new IllegalStateException("Sorry, deque too big"); - } - - final FlowStage[] es = elements = Arrays.copyOf(elements, newCapacity); - - // Exceptionally, here tail == head needs to be disambiguated - if (tail < head || (tail == head && es[head] != null)) { - // wrap around; slide first leg forward to end of array - int newSpace = newCapacity - oldCapacity; - System.arraycopy(es, head, es, head + newSpace, oldCapacity - head); - for (int i = head, to = (head += newSpace); i < to; i++) es[i] = null; - } - } - - /** - * Circularly increments i, mod modulus. - * Precondition and postcondition: 0 <= i < modulus. - */ - private static int inc(int i, int modulus) { - if (++i >= modulus) i = 0; - return i; - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowTimerQueue.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowTimerQueue.java deleted file mode 100644 index 4b746202..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowTimerQueue.java +++ /dev/null @@ -1,208 +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.flow2; - -import java.util.Arrays; - -/** - * A specialized priority queue for timers of {@link FlowStageLogic}s. - *

- * By using a specialized priority queue, we reduce the overhead caused by the default priority queue implementation - * being generic. - */ -final class FlowTimerQueue { - /** - * Array representation of binary heap of {@link FlowStage} instances. - */ - private FlowStage[] queue; - - /** - * The number of elements in the priority queue. - */ - private int size = 0; - - /** - * Construct a {@link FlowTimerQueue} with the specified initial capacity. - * - * @param initialCapacity The initial capacity of the queue. - */ - public FlowTimerQueue(int initialCapacity) { - this.queue = new FlowStage[initialCapacity]; - } - - /** - * Enqueue a timer for the specified context or update the existing timer. - */ - void enqueue(FlowStage ctx) { - FlowStage[] es = queue; - int k = ctx.timerIndex; - - if (ctx.deadline != Long.MAX_VALUE) { - if (k >= 0) { - update(es, ctx, k); - } else { - add(es, ctx); - } - } else if (k >= 0) { - delete(es, k); - } - } - - /** - * Retrieve the head of the queue if its deadline does not exceed now. - * - * @param now The timestamp that the deadline of the head of the queue should not exceed. - * @return The head of the queue if its deadline does not exceed now, otherwise null. - */ - FlowStage poll(long now) { - int size = this.size; - if (size == 0) { - return null; - } - - final FlowStage[] es = queue; - final FlowStage head = es[0]; - - if (now < head.deadline) { - return null; - } - - int n = size - 1; - this.size = n; - final FlowStage next = es[n]; - es[n] = null; // Clear the last element of the queue - - if (n > 0) { - siftDown(0, next, es, n); - } - - head.timerIndex = -1; - return head; - } - - /** - * Find the earliest deadline in the queue. - */ - long peekDeadline() { - if (size > 0) { - return queue[0].deadline; - } - - return Long.MAX_VALUE; - } - - /** - * Add a new entry to the queue. - */ - private void add(FlowStage[] es, FlowStage ctx) { - int i = size; - - if (i >= es.length) { - // Re-fetch the resized array - es = grow(); - } - - siftUp(i, ctx, es); - - size = i + 1; - } - - /** - * Update the deadline of an existing entry in the queue. - */ - private void update(FlowStage[] es, FlowStage ctx, int k) { - if (k > 0) { - int parent = (k - 1) >>> 1; - if (es[parent].deadline > ctx.deadline) { - siftUp(k, ctx, es); - return; - } - } - - siftDown(k, ctx, es, size); - } - - /** - * Deadline an entry from the queue. - */ - private void delete(FlowStage[] es, int k) { - int s = --size; - if (s == k) { - es[k] = null; // Element is last in the queue - } else { - FlowStage moved = es[s]; - es[s] = null; - - siftDown(k, moved, es, s); - - if (es[k] == moved) { - siftUp(k, moved, es); - } - } - } - - /** - * Increases the capacity of the array. - */ - private FlowStage[] grow() { - FlowStage[] queue = this.queue; - int oldCapacity = queue.length; - int newCapacity = oldCapacity + (oldCapacity >> 1); - - queue = Arrays.copyOf(queue, newCapacity); - this.queue = queue; - return queue; - } - - private static void siftUp(int k, FlowStage key, FlowStage[] es) { - while (k > 0) { - int parent = (k - 1) >>> 1; - FlowStage e = es[parent]; - if (key.deadline >= e.deadline) break; - es[k] = e; - e.timerIndex = k; - k = parent; - } - es[k] = key; - key.timerIndex = k; - } - - private static void siftDown(int k, FlowStage key, FlowStage[] es, int n) { - int half = n >>> 1; // loop while a non-leaf - while (k < half) { - int child = (k << 1) + 1; // assume left child is least - FlowStage c = es[child]; - int right = child + 1; - if (right < n && c.deadline > es[right].deadline) c = es[child = right]; - - if (key.deadline <= c.deadline) break; - - es[k] = c; - c.timerIndex = k; - k = child; - } - - es[k] = key; - key.timerIndex = k; - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/InHandler.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/InHandler.java deleted file mode 100644 index 839b01db..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/InHandler.java +++ /dev/null @@ -1,54 +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.flow2; - -/** - * Collection of callbacks for the input port (a {@link InPort}) of a {@link FlowStageLogic}. - */ -public interface InHandler { - /** - * Return the actual flow rate over the input port. - * - * @param port The input port to which the flow was pushed. - * @return The actual flow rate over the port. - */ - default float getRate(InPort port) { - return Math.min(port.getDemand(), port.getCapacity()); - } - - /** - * This method is invoked when another {@link FlowStageLogic} changes the rate of flow to the specified inlet. - * - * @param port The input port to which the flow was pushed. - * @param demand The rate of flow the output attempted to push to the port. - */ - void onPush(InPort port, float demand); - - /** - * This method is invoked when the input port is finished. - * - * @param port The input port that has finished. - * @param cause The cause of the input port being finished or null if the port completed successfully. - */ - void onUpstreamFinish(InPort port, Throwable cause); -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/InHandlers.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/InHandlers.java deleted file mode 100644 index 9d5b4bef..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/InHandlers.java +++ /dev/null @@ -1,53 +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.flow2; - -/** - * A collection of common {@link InHandler} implementations. - */ -public class InHandlers { - /** - * Prevent construction of this class. - */ - private InHandlers() {} - - /** - * Return an {@link InHandler} that does nothing. - */ - public static InHandler noop() { - return NoopInHandler.INSTANCE; - } - - /** - * No-op implementation of {@link InHandler}. - */ - private static final class NoopInHandler implements InHandler { - public static final InHandler INSTANCE = new NoopInHandler(); - - @Override - public void onPush(InPort port, float demand) {} - - @Override - public void onUpstreamFinish(InPort port, Throwable cause) {} - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/InPort.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/InPort.java deleted file mode 100644 index 16fed4eb..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/InPort.java +++ /dev/null @@ -1,214 +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.flow2; - -import java.time.InstantSource; -import java.util.Objects; - -/** - * A port that consumes a flow. - *

- * Input ports are represented as in-going edges in the flow graph. - */ -public final class InPort implements Inlet { - private final int id; - - private float capacity; - private float demand; - - private boolean mask; - - OutPort output; - private InHandler handler = InHandlers.noop(); - private final InstantSource clock; - private final String name; - private final FlowStage stage; - - InPort(FlowStage stage, String name, int id) { - this.name = name; - this.id = id; - this.stage = stage; - this.clock = stage.clock; - } - - @Override - public FlowGraph getGraph() { - return stage.parentGraph; - } - - @Override - public String getName() { - return name; - } - - /** - * Return the identifier of the {@link InPort} with respect to its stage. - */ - public int getId() { - return id; - } - - /** - * Return the current capacity of the input port. - */ - public float getCapacity() { - return capacity; - } - - /** - * Return the current demand of flow of the input port. - */ - public float getDemand() { - return demand; - } - - /** - * Return the current rate of flow of the input port. - */ - public float getRate() { - return handler.getRate(this); - } - - /** - * Pull the flow with the specified capacity from the input port. - * - * @param capacity The maximum throughput that the stage can receive from the input port. - */ - public void pull(float capacity) { - this.capacity = capacity; - - OutPort output = this.output; - if (output != null) { - output.pull(capacity); - } - } - - /** - * Return the current {@link InHandler} of the input port. - */ - public InHandler getHandler() { - return handler; - } - - /** - * Set the {@link InHandler} of the input port. - */ - public void setHandler(InHandler handler) { - this.handler = handler; - } - - /** - * Return the mask of this port. - *

- * Stages ignore events originating from masked ports. - */ - public boolean getMask() { - return mask; - } - - /** - * (Un)mask the port. - */ - public void setMask(boolean mask) { - this.mask = mask; - } - - /** - * Disconnect the input port from its (potentially) connected outlet. - *

- * The inlet can still be used and re-connected to another outlet. - * - * @param cause The cause for disconnecting the port or null when no more flow is needed. - */ - public void cancel(Throwable cause) { - demand = 0.f; - - OutPort output = this.output; - if (output != null) { - this.output = null; - output.input = null; - output.cancel(cause); - } - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - InPort port = (InPort) o; - return stage.equals(port.stage) && name.equals(port.name); - } - - @Override - public int hashCode() { - return Objects.hash(stage.parentGraph, name); - } - - /** - * This method is invoked when the inlet is connected to an outlet. - */ - void connect() { - OutPort output = this.output; - output.pull(capacity); - } - - /** - * Push a flow from an outlet to this inlet. - * - * @param demand The rate of flow to push. - */ - void push(float demand) { - // No-op when the rate is unchanged - if (this.demand == demand) { - return; - } - - try { - handler.onPush(this, demand); - this.demand = demand; - - if (!mask) { - stage.invalidate(clock.millis()); - } - } catch (Exception e) { - stage.doFail(e); - } - } - - /** - * This method is invoked by the connected {@link OutPort} when it finishes. - */ - void finish(Throwable cause) { - try { - long now = clock.millis(); - handler.onUpstreamFinish(this, cause); - this.demand = 0.f; - - if (!mask) { - stage.invalidate(now); - } - } catch (Exception e) { - stage.doFail(e); - } - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/Inlet.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/Inlet.java deleted file mode 100644 index 4a9ea6a5..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/Inlet.java +++ /dev/null @@ -1,38 +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.flow2; - -/** - * An in-going edge in a {@link FlowGraph}. - */ -public interface Inlet { - /** - * Return the {@link FlowGraph} to which the inlet is exposed. - */ - FlowGraph getGraph(); - - /** - * Return the name of the inlet. - */ - String getName(); -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/InvocationStack.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/InvocationStack.java deleted file mode 100644 index a5b5114b..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/InvocationStack.java +++ /dev/null @@ -1,95 +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.flow2; - -import java.util.Arrays; - -/** - * A specialized monotonic stack implementation for tracking the scheduled engine invocations. - *

- * By using a specialized class, we reduce the overhead caused by type-erasure. - */ -final class InvocationStack { - /** - * The array of elements in the stack. - */ - private long[] elements; - - private int head = -1; - - public InvocationStack(int initialCapacity) { - elements = new long[initialCapacity]; - Arrays.fill(elements, Long.MIN_VALUE); - } - - /** - * Try to add the specified invocation to the monotonic stack. - * - * @param invocation The timestamp of the invocation. - * @return true if the invocation was added, false otherwise. - */ - boolean tryAdd(long invocation) { - final long[] es = elements; - int head = this.head; - - if (head < 0 || es[head] > invocation) { - es[head + 1] = invocation; - this.head = head + 1; - - if (head + 2 == es.length) { - doubleCapacity(); - } - - return true; - } - - return false; - } - - /** - * Remove the head invocation from the stack or return {@link Long#MAX_VALUE} if the stack is empty. - */ - long poll() { - final long[] es = elements; - int head = this.head--; - - if (head >= 0) { - return es[head]; - } - - return Long.MAX_VALUE; - } - - /** - * Doubles the capacity of this deque - */ - private void doubleCapacity() { - int oldCapacity = elements.length; - int newCapacity = oldCapacity + (oldCapacity >> 1); - if (newCapacity < 0) { - throw new IllegalStateException("Sorry, deque too big"); - } - - elements = Arrays.copyOf(elements, newCapacity); - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/OutHandler.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/OutHandler.java deleted file mode 100644 index 723c6d6b..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/OutHandler.java +++ /dev/null @@ -1,47 +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.flow2; - -/** - * Collection of callbacks for the output port (a {@link OutPort}) of a {@link FlowStageLogic}. - */ -public interface OutHandler { - /** - * This method is invoked when another {@link FlowStageLogic} changes the capacity of the outlet. - * - * @param port The output port of which the capacity was changed. - * @param capacity The new capacity of the outlet. - */ - void onPull(OutPort port, float capacity); - - /** - * This method is invoked when the output port no longer accepts any flow. - *

- * After this callback no other callbacks will be called for this port. - * - * @param port The outlet that no longer accepts any flow. - * @param cause The cause of the output port no longer accepting any flow or null if the port closed - * successfully. - */ - void onDownstreamFinish(OutPort port, Throwable cause); -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/OutHandlers.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/OutHandlers.java deleted file mode 100644 index 8fbfda0d..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/OutHandlers.java +++ /dev/null @@ -1,53 +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.flow2; - -/** - * A collection of common {@link OutHandler} implementations. - */ -public class OutHandlers { - /** - * Prevent construction of this class. - */ - private OutHandlers() {} - - /** - * Return an {@link OutHandler} that does nothing. - */ - public static OutHandler noop() { - return NoopOutHandler.INSTANCE; - } - - /** - * No-op implementation of {@link OutHandler}. - */ - private static final class NoopOutHandler implements OutHandler { - public static final OutHandler INSTANCE = new NoopOutHandler(); - - @Override - public void onPull(OutPort port, float capacity) {} - - @Override - public void onDownstreamFinish(OutPort port, Throwable cause) {} - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/OutPort.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/OutPort.java deleted file mode 100644 index 1f7ed4ee..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/OutPort.java +++ /dev/null @@ -1,224 +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.flow2; - -import java.time.InstantSource; -import java.util.Objects; - -/** - * A port that outputs a flow. - *

- * Output ports are represented as out-going edges in the flow graph. - */ -public final class OutPort implements Outlet { - private final int id; - - private float capacity; - private float demand; - - private boolean mask; - - InPort input; - private OutHandler handler = OutHandlers.noop(); - private final String name; - private final FlowStage stage; - private final InstantSource clock; - - OutPort(FlowStage stage, String name, int id) { - this.name = name; - this.id = id; - this.stage = stage; - this.clock = stage.clock; - } - - @Override - public FlowGraph getGraph() { - return stage.parentGraph; - } - - @Override - public String getName() { - return name; - } - - /** - * Return the identifier of the {@link OutPort} with respect to its stage. - */ - public int getId() { - return id; - } - - /** - * Return the capacity of the output port. - */ - public float getCapacity() { - return capacity; - } - - /** - * Return the current demand of flow of the output port. - */ - public float getDemand() { - return demand; - } - - /** - * Return the current rate of flow of the input port. - */ - public float getRate() { - InPort input = this.input; - if (input != null) { - return input.getRate(); - } - - return 0.f; - } - - /** - * Return the current {@link OutHandler} of the output port. - */ - public OutHandler getHandler() { - return handler; - } - - /** - * Set the {@link OutHandler} of the output port. - */ - public void setHandler(OutHandler handler) { - this.handler = handler; - } - - /** - * Return the mask of this port. - *

- * Stages ignore events originating from masked ports. - */ - public boolean getMask() { - return mask; - } - - /** - * (Un)mask the port. - */ - public void setMask(boolean mask) { - this.mask = mask; - } - - /** - * Push the given flow rate over output port. - * - * @param rate The rate of the flow to push. - */ - public void push(float rate) { - demand = rate; - InPort input = this.input; - - if (input != null) { - input.push(rate); - } - } - - /** - * Signal to the downstream port that the output has completed successfully and disconnect the port from its input. - *

- * The output port can still be used and re-connected to another input. - */ - public void complete() { - fail(null); - } - - /** - * Signal a failure to the downstream port and disconnect the port from its input. - *

- * The output can still be used and re-connected to another input. - */ - public void fail(Throwable cause) { - capacity = 0.f; - - InPort input = this.input; - if (input != null) { - this.input = null; - input.output = null; - input.finish(cause); - } - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - OutPort port = (OutPort) o; - return stage.equals(port.stage) && name.equals(port.name); - } - - @Override - public int hashCode() { - return Objects.hash(stage.parentGraph, name); - } - - /** - * This method is invoked when the outlet is connected to an inlet. - */ - void connect() { - input.push(demand); - } - - /** - * Pull from this outlet with a specified capacity. - * - * @param capacity The capacity of the inlet. - */ - void pull(float capacity) { - // No-op when outlet is not active or the rate is unchanged - if (this.capacity == capacity) { - return; - } - - try { - handler.onPull(this, capacity); - this.capacity = capacity; - - if (!mask) { - stage.invalidate(clock.millis()); - } - } catch (Exception e) { - stage.doFail(e); - } - } - - /** - * This method is invoked by the connected {@link InPort} when downstream cancels the connection. - */ - void cancel(Throwable cause) { - try { - handler.onDownstreamFinish(this, cause); - this.capacity = 0.f; - - if (!mask) { - stage.invalidate(clock.millis()); - } - } catch (Exception e) { - stage.doFail(e); - } - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/Outlet.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/Outlet.java deleted file mode 100644 index 32e19a3b..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/Outlet.java +++ /dev/null @@ -1,38 +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.flow2; - -/** - * An out-going edge in a {@link FlowGraph}. - */ -public interface Outlet { - /** - * Return the {@link FlowGraph} to which the outlet is exposed. - */ - FlowGraph getGraph(); - - /** - * Return the name of the outlet. - */ - String getName(); -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/mux/FlowMultiplexer.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/mux/FlowMultiplexer.java deleted file mode 100644 index dec98955..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/mux/FlowMultiplexer.java +++ /dev/null @@ -1,95 +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.flow2.mux; - -import org.opendc.simulator.flow2.FlowStageLogic; -import org.opendc.simulator.flow2.Inlet; -import org.opendc.simulator.flow2.Outlet; - -/** - * A {@link FlowStageLogic} that multiplexes multiple inputs over (possibly) multiple outputs. - */ -public interface FlowMultiplexer { - /** - * Return maximum number of inputs supported by the multiplexer. - */ - int getMaxInputs(); - - /** - * Return maximum number of outputs supported by the multiplexer. - */ - int getMaxOutputs(); - - /** - * Return the number of active inputs on this multiplexer. - */ - int getInputCount(); - - /** - * Allocate a new input on this multiplexer with the specified capacity.. - * - * @return The identifier of the input for this stage. - */ - Inlet newInput(); - - /** - * Release the input at the specified slot. - * - * @param inlet The inlet to release. - */ - void releaseInput(Inlet inlet); - - /** - * Return the number of active outputs on this multiplexer. - */ - int getOutputCount(); - - /** - * Allocate a new output on this multiplexer. - * - * @return The outlet for this stage. - */ - Outlet newOutput(); - - /** - * Release the output at the specified slot. - * - * @param outlet The outlet to release. - */ - void releaseOutput(Outlet outlet); - - /** - * Return the total input capacity of the {@link FlowMultiplexer}. - */ - float getCapacity(); - - /** - * Return the total input demand for the {@link FlowMultiplexer}. - */ - float getDemand(); - - /** - * Return the total input rate for the {@link FlowMultiplexer}. - */ - float getRate(); -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/mux/FlowMultiplexerFactory.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/mux/FlowMultiplexerFactory.java deleted file mode 100644 index 0b5b9141..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/mux/FlowMultiplexerFactory.java +++ /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.flow2.mux; - -import org.opendc.simulator.flow2.FlowGraph; - -/** - * Factory interface for a {@link FlowMultiplexer} implementation. - */ -public interface FlowMultiplexerFactory { - /** - * Construct a new {@link FlowMultiplexer} belonging to the specified {@link FlowGraph}. - * - * @param graph The graph to which the multiplexer belongs. - */ - FlowMultiplexer newMultiplexer(FlowGraph graph); - - /** - * Return a {@link FlowMultiplexerFactory} for {@link ForwardingFlowMultiplexer} instances. - */ - static FlowMultiplexerFactory forwardingMultiplexer() { - return ForwardingFlowMultiplexer.FACTORY; - } - - /** - * Return a {@link FlowMultiplexerFactory} for {@link MaxMinFlowMultiplexer} instances. - */ - static FlowMultiplexerFactory maxMinMultiplexer() { - return MaxMinFlowMultiplexer.FACTORY; - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/mux/ForwardingFlowMultiplexer.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/mux/ForwardingFlowMultiplexer.java deleted file mode 100644 index e0564cd2..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/mux/ForwardingFlowMultiplexer.java +++ /dev/null @@ -1,287 +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.flow2.mux; - -import java.util.Arrays; -import java.util.BitSet; -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.Outlet; - -/** - * A {@link FlowMultiplexer} implementation that allocates inputs to the outputs of the multiplexer exclusively. - * This means that a single input is directly connected to an output and that the multiplexer can only support as many - * inputs as outputs. - */ -public final class ForwardingFlowMultiplexer implements FlowMultiplexer, FlowStageLogic { - /** - * Factory implementation for this implementation. - */ - static FlowMultiplexerFactory FACTORY = ForwardingFlowMultiplexer::new; - - public final IdleInHandler IDLE_IN_HANDLER = new IdleInHandler(); - public final IdleOutHandler IDLE_OUT_HANDLER = new IdleOutHandler(); - - private final FlowStage stage; - - private InPort[] inlets; - private OutPort[] outlets; - private final BitSet activeInputs; - private final BitSet activeOutputs; - private final BitSet availableOutputs; - - private float capacity = 0.f; - private float demand = 0.f; - - public ForwardingFlowMultiplexer(FlowGraph graph) { - this.stage = graph.newStage(this); - - this.inlets = new InPort[4]; - this.activeInputs = new BitSet(); - this.outlets = new OutPort[4]; - this.activeOutputs = new BitSet(); - this.availableOutputs = new BitSet(); - } - - @Override - public float getCapacity() { - return capacity; - } - - @Override - public float getDemand() { - return demand; - } - - @Override - public float getRate() { - final BitSet activeOutputs = this.activeOutputs; - final OutPort[] outlets = this.outlets; - float rate = 0.f; - for (int i = activeOutputs.nextSetBit(0); i != -1; i = activeOutputs.nextSetBit(i + 1)) { - rate += outlets[i].getRate(); - } - return rate; - } - - @Override - public int getMaxInputs() { - return getOutputCount(); - } - - @Override - public int getMaxOutputs() { - return Integer.MAX_VALUE; - } - - @Override - public int getInputCount() { - return activeInputs.length(); - } - - @Override - public Inlet newInput() { - final BitSet activeInputs = this.activeInputs; - int slot = activeInputs.nextClearBit(0); - - InPort inPort = stage.getInlet("in" + slot); - inPort.setMask(true); - - InPort[] inlets = this.inlets; - if (slot >= inlets.length) { - int newLength = inlets.length + (inlets.length >> 1); - inlets = Arrays.copyOf(inlets, newLength); - this.inlets = inlets; - } - - final BitSet availableOutputs = this.availableOutputs; - int outSlot = availableOutputs.nextSetBit(0); - - if (outSlot < 0) { - throw new IllegalStateException("No capacity available for a new input"); - } - - inlets[slot] = inPort; - activeInputs.set(slot); - - OutPort outPort = outlets[outSlot]; - availableOutputs.clear(outSlot); - - inPort.setHandler(new ForwardingInHandler(outPort)); - outPort.setHandler(new ForwardingOutHandler(inPort)); - - inPort.pull(outPort.getCapacity()); - - return inPort; - } - - @Override - public void releaseInput(Inlet inlet) { - InPort port = (InPort) inlet; - int slot = port.getId(); - - final BitSet activeInputs = this.activeInputs; - - if (!activeInputs.get(slot)) { - return; - } - - port.cancel(null); - activeInputs.clear(slot); - - ForwardingInHandler inHandler = (ForwardingInHandler) port.getHandler(); - availableOutputs.set(inHandler.output.getId()); - - port.setHandler(IDLE_IN_HANDLER); - } - - @Override - public int getOutputCount() { - return activeOutputs.length(); - } - - @Override - public Outlet newOutput() { - final BitSet activeOutputs = this.activeOutputs; - int slot = activeOutputs.nextClearBit(0); - - OutPort port = stage.getOutlet("out" + slot); - OutPort[] outlets = this.outlets; - if (slot >= outlets.length) { - int newLength = outlets.length + (outlets.length >> 1); - outlets = Arrays.copyOf(outlets, newLength); - this.outlets = outlets; - } - outlets[slot] = port; - - activeOutputs.set(slot); - availableOutputs.set(slot); - - port.setHandler(IDLE_OUT_HANDLER); - - return port; - } - - @Override - public void releaseOutput(Outlet outlet) { - OutPort port = (OutPort) outlet; - int slot = port.getId(); - activeInputs.clear(slot); - availableOutputs.clear(slot); - port.complete(); - - port.setHandler(IDLE_OUT_HANDLER); - } - - @Override - public long onUpdate(FlowStage ctx, long now) { - return Long.MAX_VALUE; - } - - class ForwardingInHandler implements InHandler { - final OutPort output; - - ForwardingInHandler(OutPort output) { - this.output = output; - } - - @Override - public float getRate(InPort port) { - return output.getRate(); - } - - @Override - public void onPush(InPort port, float rate) { - ForwardingFlowMultiplexer.this.demand += -port.getDemand() + rate; - - output.push(rate); - } - - @Override - public void onUpstreamFinish(InPort port, Throwable cause) { - ForwardingFlowMultiplexer.this.demand -= port.getDemand(); - - final OutPort output = this.output; - output.push(0.f); - - releaseInput(port); - } - } - - private class ForwardingOutHandler implements OutHandler { - private final InPort input; - - ForwardingOutHandler(InPort input) { - this.input = input; - } - - @Override - public void onPull(OutPort port, float capacity) { - ForwardingFlowMultiplexer.this.capacity += -port.getCapacity() + capacity; - - input.pull(capacity); - } - - @Override - public void onDownstreamFinish(OutPort port, Throwable cause) { - ForwardingFlowMultiplexer.this.capacity -= port.getCapacity(); - - input.cancel(cause); - - releaseOutput(port); - } - } - - private static class IdleInHandler implements InHandler { - @Override - public float getRate(InPort port) { - return 0.f; - } - - @Override - public void onPush(InPort port, float rate) { - port.cancel(new IllegalStateException("Inlet is not allocated")); - } - - @Override - public void onUpstreamFinish(InPort port, Throwable cause) {} - } - - private class IdleOutHandler implements OutHandler { - @Override - public void onPull(OutPort port, float capacity) { - ForwardingFlowMultiplexer.this.capacity += -port.getCapacity() + capacity; - } - - @Override - public void onDownstreamFinish(OutPort port, Throwable cause) { - ForwardingFlowMultiplexer.this.capacity -= port.getCapacity(); - } - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/mux/MaxMinFlowMultiplexer.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/mux/MaxMinFlowMultiplexer.java deleted file mode 100644 index ac5c4f5c..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/mux/MaxMinFlowMultiplexer.java +++ /dev/null @@ -1,297 +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.flow2.mux; - -import java.util.Arrays; -import java.util.BitSet; -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.Outlet; - -/** - * A {@link FlowMultiplexer} implementation that distributes the available capacity of the outputs over the inputs - * using max-min fair sharing. - *

- * The max-min fair sharing algorithm of this multiplexer ensures that each input receives a fair share of the combined - * output capacity, but allows individual inputs to use more capacity if there is still capacity left. - */ -public final class MaxMinFlowMultiplexer implements FlowMultiplexer, FlowStageLogic { - /** - * Factory implementation for this implementation. - */ - static FlowMultiplexerFactory FACTORY = MaxMinFlowMultiplexer::new; - - private final FlowStage stage; - private final BitSet activeInputs; - private final BitSet activeOutputs; - - private float capacity = 0.f; - private float demand = 0.f; - private float rate = 0.f; - - private InPort[] inlets; - private long[] inputs; - private float[] rates; - private OutPort[] outlets; - - private final MultiplexerInHandler inHandler = new MultiplexerInHandler(); - private final MultiplexerOutHandler outHandler = new MultiplexerOutHandler(); - - /** - * Construct a {@link MaxMinFlowMultiplexer} instance. - * - * @param graph The {@link FlowGraph} to add the multiplexer to. - */ - public MaxMinFlowMultiplexer(FlowGraph graph) { - this.stage = graph.newStage(this); - this.activeInputs = new BitSet(); - this.activeOutputs = new BitSet(); - - this.inlets = new InPort[4]; - this.inputs = new long[4]; - this.rates = new float[4]; - this.outlets = new OutPort[4]; - } - - @Override - public float getCapacity() { - return capacity; - } - - @Override - public float getDemand() { - return demand; - } - - @Override - public float getRate() { - return rate; - } - - @Override - public int getMaxInputs() { - return Integer.MAX_VALUE; - } - - @Override - public int getMaxOutputs() { - return Integer.MAX_VALUE; - } - - @Override - public long onUpdate(FlowStage ctx, long now) { - float capacity = this.capacity; - float demand = this.demand; - float rate = demand; - - if (demand > capacity) { - rate = redistributeCapacity(inlets, inputs, rates, capacity); - } - - if (this.rate != rate) { - // Only update the outputs if the output rate has changed - this.rate = rate; - - changeRate(activeOutputs, outlets, capacity, rate); - } - - return Long.MAX_VALUE; - } - - @Override - public int getInputCount() { - return activeInputs.length(); - } - - @Override - public Inlet newInput() { - final BitSet activeInputs = this.activeInputs; - int slot = activeInputs.nextClearBit(0); - - InPort port = stage.getInlet("in" + slot); - port.setHandler(inHandler); - port.pull(this.capacity); - - InPort[] inlets = this.inlets; - if (slot >= inlets.length) { - int newLength = inlets.length + (inlets.length >> 1); - inlets = Arrays.copyOf(inlets, newLength); - inputs = Arrays.copyOf(inputs, newLength); - rates = Arrays.copyOf(rates, newLength); - this.inlets = inlets; - } - inlets[slot] = port; - - activeInputs.set(slot); - return port; - } - - @Override - public void releaseInput(Inlet inlet) { - InPort port = (InPort) inlet; - - activeInputs.clear(port.getId()); - port.cancel(null); - } - - @Override - public int getOutputCount() { - return activeOutputs.length(); - } - - @Override - public Outlet newOutput() { - final BitSet activeOutputs = this.activeOutputs; - int slot = activeOutputs.nextClearBit(0); - - OutPort port = stage.getOutlet("out" + slot); - port.setHandler(outHandler); - - OutPort[] outlets = this.outlets; - if (slot >= outlets.length) { - int newLength = outlets.length + (outlets.length >> 1); - outlets = Arrays.copyOf(outlets, newLength); - this.outlets = outlets; - } - outlets[slot] = port; - - activeOutputs.set(slot); - return port; - } - - @Override - public void releaseOutput(Outlet outlet) { - OutPort port = (OutPort) outlet; - activeInputs.clear(port.getId()); - port.complete(); - } - - /** - * Helper function to redistribute the specified capacity across the inlets. - */ - private static float redistributeCapacity(InPort[] inlets, long[] inputs, float[] rates, float capacity) { - // If the demand is higher than the capacity, we need use max-min fair sharing to distribute the - // constrained capacity across the inputs. - for (int i = 0; i < inputs.length; i++) { - InPort inlet = inlets[i]; - if (inlet == null) { - break; - } - - inputs[i] = ((long) Float.floatToRawIntBits(inlet.getDemand()) << 32) | (i & 0xFFFFFFFFL); - } - Arrays.sort(inputs); - - float availableCapacity = capacity; - int inputSize = inputs.length; - - // Divide the available output capacity fairly over the inputs using max-min fair sharing - for (int i = 0; i < inputs.length; i++) { - long v = inputs[i]; - int slot = (int) v; - float d = Float.intBitsToFloat((int) (v >> 32)); - - if (d == 0.0) { - continue; - } - - float availableShare = availableCapacity / (inputSize - i); - float r = Math.min(d, availableShare); - - rates[slot] = r; - availableCapacity -= r; - } - - return capacity - availableCapacity; - } - - /** - * Helper method to change the rate of the outlets. - */ - private static void changeRate(BitSet activeOutputs, OutPort[] outlets, float capacity, float rate) { - // Divide the requests over the available capacity of the input resources fairly - for (int i = activeOutputs.nextSetBit(0); i != -1; i = activeOutputs.nextSetBit(i + 1)) { - OutPort outlet = outlets[i]; - float fraction = outlet.getCapacity() / capacity; - outlet.push(rate * fraction); - } - } - - /** - * A {@link InHandler} implementation for the multiplexer inputs. - */ - private class MultiplexerInHandler implements InHandler { - @Override - public float getRate(InPort port) { - return rates[port.getId()]; - } - - @Override - public void onPush(InPort port, float demand) { - MaxMinFlowMultiplexer.this.demand += -port.getDemand() + demand; - rates[port.getId()] = demand; - } - - @Override - public void onUpstreamFinish(InPort port, Throwable cause) { - MaxMinFlowMultiplexer.this.demand -= port.getDemand(); - releaseInput(port); - rates[port.getId()] = 0.f; - } - } - - /** - * A {@link OutHandler} implementation for the multiplexer outputs. - */ - private class MultiplexerOutHandler implements OutHandler { - @Override - public void onPull(OutPort port, float capacity) { - float newCapacity = MaxMinFlowMultiplexer.this.capacity - port.getCapacity() + capacity; - MaxMinFlowMultiplexer.this.capacity = newCapacity; - changeInletCapacity(newCapacity); - } - - @Override - public void onDownstreamFinish(OutPort port, Throwable cause) { - float newCapacity = MaxMinFlowMultiplexer.this.capacity - port.getCapacity(); - MaxMinFlowMultiplexer.this.capacity = newCapacity; - releaseOutput(port); - changeInletCapacity(newCapacity); - } - - private void changeInletCapacity(float capacity) { - BitSet activeInputs = MaxMinFlowMultiplexer.this.activeInputs; - InPort[] inlets = MaxMinFlowMultiplexer.this.inlets; - - for (int i = activeInputs.nextSetBit(0); i != -1; i = activeInputs.nextSetBit(i + 1)) { - inlets[i].pull(capacity); - } - } - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/sink/FlowSink.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/sink/FlowSink.java deleted file mode 100644 index 69c94708..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/sink/FlowSink.java +++ /dev/null @@ -1,36 +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.flow2.sink; - -import org.opendc.simulator.flow2.FlowStage; -import org.opendc.simulator.flow2.Inlet; - -/** - * A {@link FlowStage} with a single input. - */ -public interface FlowSink { - /** - * Return the input of this {@link FlowSink}. - */ - Inlet getInput(); -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/sink/SimpleFlowSink.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/sink/SimpleFlowSink.java deleted file mode 100644 index fdfe5ee8..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/sink/SimpleFlowSink.java +++ /dev/null @@ -1,123 +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.flow2.sink; - -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; - -/** - * A sink with a fixed capacity. - */ -public final class SimpleFlowSink implements FlowSink, FlowStageLogic { - private final FlowStage stage; - private final InPort input; - private final Handler handler; - - /** - * Construct a new {@link SimpleFlowSink} with the specified initial capacity. - * - * @param graph The graph to add the sink to. - * @param initialCapacity The initial capacity of the sink. - */ - public SimpleFlowSink(FlowGraph graph, float initialCapacity) { - this.stage = graph.newStage(this); - this.handler = new Handler(); - this.input = stage.getInlet("in"); - this.input.pull(initialCapacity); - this.input.setMask(true); - this.input.setHandler(handler); - } - - /** - * Return the {@link Inlet} of this sink. - */ - @Override - public Inlet getInput() { - return input; - } - - /** - * Return the capacity of the sink. - */ - public float getCapacity() { - return input.getCapacity(); - } - - /** - * Update the capacity of the sink. - * - * @param capacity The new capacity to update the sink to. - */ - public void setCapacity(float capacity) { - input.pull(capacity); - stage.invalidate(); - } - - /** - * Return the flow rate of the sink. - */ - public float getRate() { - return input.getRate(); - } - - /** - * Remove this node from the graph. - */ - public void close() { - stage.close(); - } - - @Override - public long onUpdate(FlowStage ctx, long now) { - InPort input = this.input; - handler.rate = Math.min(input.getDemand(), input.getCapacity()); - return Long.MAX_VALUE; - } - - /** - * The {@link InHandler} implementation for the sink. - */ - private static final class Handler implements InHandler { - float rate; - - @Override - public float getRate(InPort port) { - return rate; - } - - @Override - public void onPush(InPort port, float demand) { - float capacity = port.getCapacity(); - rate = Math.min(demand, capacity); - } - - @Override - public void onUpstreamFinish(InPort port, Throwable cause) { - rate = 0.f; - } - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/source/EmptyFlowSource.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/source/EmptyFlowSource.java deleted file mode 100644 index 2dcc66e4..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/source/EmptyFlowSource.java +++ /dev/null @@ -1,65 +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.flow2.source; - -import org.opendc.simulator.flow2.FlowGraph; -import org.opendc.simulator.flow2.FlowStage; -import org.opendc.simulator.flow2.FlowStageLogic; -import org.opendc.simulator.flow2.OutPort; -import org.opendc.simulator.flow2.Outlet; - -/** - * An empty {@link FlowSource}. - */ -public final class EmptyFlowSource implements FlowSource, FlowStageLogic { - private final FlowStage stage; - private final OutPort output; - - /** - * Construct a new {@link EmptyFlowSource}. - */ - public EmptyFlowSource(FlowGraph graph) { - this.stage = graph.newStage(this); - this.output = stage.getOutlet("out"); - } - - /** - * Return the {@link Outlet} of the source. - */ - @Override - public Outlet getOutput() { - return output; - } - - /** - * Remove this node from the graph. - */ - public void close() { - stage.close(); - } - - @Override - public long onUpdate(FlowStage ctx, long now) { - return Long.MAX_VALUE; - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/source/FlowSource.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/source/FlowSource.java deleted file mode 100644 index f9432c33..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/source/FlowSource.java +++ /dev/null @@ -1,36 +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.flow2.source; - -import org.opendc.simulator.flow2.FlowStage; -import org.opendc.simulator.flow2.Outlet; - -/** - * A {@link FlowStage} with a single output. - */ -public interface FlowSource { - /** - * Return the output of this {@link FlowSource}. - */ - Outlet getOutput(); -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/source/RuntimeFlowSource.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/source/RuntimeFlowSource.java deleted file mode 100644 index c09987cd..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/source/RuntimeFlowSource.java +++ /dev/null @@ -1,128 +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.flow2.source; - -import java.util.function.Consumer; -import org.opendc.simulator.flow2.FlowGraph; -import org.opendc.simulator.flow2.FlowStage; -import org.opendc.simulator.flow2.FlowStageLogic; -import org.opendc.simulator.flow2.OutHandler; -import org.opendc.simulator.flow2.OutPort; -import org.opendc.simulator.flow2.Outlet; - -/** - * A {@link FlowSource} that ensures a flow is emitted for a specified amount of time at some utilization. - */ -public class RuntimeFlowSource implements FlowSource, FlowStageLogic { - private final float utilization; - - private final FlowStage stage; - private final OutPort output; - private final Consumer completionHandler; - - private long duration; - private long lastPull; - - /** - * Construct a {@link RuntimeFlowSource} instance. - * - * @param graph The {@link FlowGraph} to which this source belongs. - * @param duration The duration of the source. - * @param utilization The utilization of the capacity of the outlet. - * @param completionHandler A callback invoked when the source completes. - */ - public RuntimeFlowSource( - FlowGraph graph, long duration, float utilization, Consumer completionHandler) { - if (duration <= 0) { - throw new IllegalArgumentException("Duration must be positive and non-zero"); - } - - if (utilization <= 0.0) { - throw new IllegalArgumentException("Utilization must be positive and non-zero"); - } - - this.stage = graph.newStage(this); - this.output = stage.getOutlet("out"); - this.output.setHandler(new OutHandler() { - @Override - public void onPull(OutPort port, float capacity) {} - - @Override - public void onDownstreamFinish(OutPort port, Throwable cause) { - // Source cannot complete without re-connecting to another sink, so mark the source as completed - completionHandler.accept(RuntimeFlowSource.this); - } - }); - this.duration = duration; - this.utilization = utilization; - this.completionHandler = completionHandler; - this.lastPull = graph.getEngine().getClock().millis(); - } - - /** - * Construct a new {@link RuntimeFlowSource}. - * - * @param graph The {@link FlowGraph} to which this source belongs. - * @param duration The duration of the source. - * @param utilization The utilization of the capacity of the outlet. - */ - public RuntimeFlowSource(FlowGraph graph, long duration, float utilization) { - this(graph, duration, utilization, RuntimeFlowSource::close); - } - - /** - * Return the {@link Outlet} of the source. - */ - @Override - public Outlet getOutput() { - return output; - } - - /** - * Remove this node from the graph. - */ - public void close() { - stage.close(); - } - - @Override - public long onUpdate(FlowStage ctx, long now) { - long lastPull = this.lastPull; - this.lastPull = now; - - long delta = Math.max(0, now - lastPull); - - OutPort output = this.output; - float limit = output.getCapacity() * utilization; - long duration = this.duration - delta; - - if (duration <= 0) { - completionHandler.accept(this); - return Long.MAX_VALUE; - } - - this.duration = duration; - output.push(limit); - return now + duration; - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/source/SimpleFlowSource.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/source/SimpleFlowSource.java deleted file mode 100644 index a0e9cb9d..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/source/SimpleFlowSource.java +++ /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.flow2.source; - -import java.util.function.Consumer; -import org.opendc.simulator.flow2.FlowGraph; -import org.opendc.simulator.flow2.FlowStage; -import org.opendc.simulator.flow2.FlowStageLogic; -import org.opendc.simulator.flow2.OutHandler; -import org.opendc.simulator.flow2.OutPort; -import org.opendc.simulator.flow2.Outlet; - -/** - * A flow source that contains a fixed amount and is pushed with a given utilization. - */ -public final class SimpleFlowSource implements FlowSource, FlowStageLogic { - private final float utilization; - private float remainingAmount; - private long lastPull; - - private final FlowStage stage; - private final OutPort output; - private final Consumer completionHandler; - - /** - * Construct a new {@link SimpleFlowSource}. - * - * @param graph The {@link FlowGraph} to which this source belongs. - * @param amount The amount to transfer via the outlet. - * @param utilization The utilization of the capacity of the outlet. - * @param completionHandler A callback invoked when the source completes. - */ - public SimpleFlowSource( - FlowGraph graph, float amount, float utilization, Consumer completionHandler) { - if (amount < 0.0) { - throw new IllegalArgumentException("Amount must be non-negative"); - } - - if (utilization <= 0.0) { - throw new IllegalArgumentException("Utilization must be positive and non-zero"); - } - - this.stage = graph.newStage(this); - this.output = stage.getOutlet("out"); - this.output.setHandler(new OutHandler() { - @Override - public void onPull(OutPort port, float capacity) {} - - @Override - public void onDownstreamFinish(OutPort port, Throwable cause) { - // Source cannot complete without re-connecting to another sink, so mark the source as completed - completionHandler.accept(SimpleFlowSource.this); - } - }); - this.completionHandler = completionHandler; - this.utilization = utilization; - this.remainingAmount = amount; - this.lastPull = graph.getEngine().getClock().millis(); - } - - /** - * Construct a new {@link SimpleFlowSource}. - * - * @param graph The {@link FlowGraph} to which this source belongs. - * @param amount The amount to transfer via the outlet. - * @param utilization The utilization of the capacity of the outlet. - */ - public SimpleFlowSource(FlowGraph graph, float amount, float utilization) { - this(graph, amount, utilization, SimpleFlowSource::close); - } - - /** - * Return the {@link Outlet} of the source. - */ - @Override - public Outlet getOutput() { - return output; - } - - /** - * Remove this node from the graph. - */ - public void close() { - stage.close(); - } - - @Override - public long onUpdate(FlowStage ctx, long now) { - long lastPull = this.lastPull; - this.lastPull = now; - - long delta = Math.max(0, now - lastPull); - - OutPort output = this.output; - float consumed = output.getRate() * delta / 1000.f; - float limit = output.getCapacity() * utilization; - - float remainingAmount = this.remainingAmount - consumed; - this.remainingAmount = remainingAmount; - - long duration = (long) Math.ceil(remainingAmount / limit * 1000); - - if (duration <= 0) { - completionHandler.accept(this); - return Long.MAX_VALUE; - } - - output.push(limit); - return now + duration; - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/source/TraceFlowSource.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/source/TraceFlowSource.java deleted file mode 100644 index e8abc2d7..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/source/TraceFlowSource.java +++ /dev/null @@ -1,151 +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.flow2.source; - -import java.util.function.Consumer; -import org.opendc.simulator.flow2.FlowGraph; -import org.opendc.simulator.flow2.FlowStage; -import org.opendc.simulator.flow2.FlowStageLogic; -import org.opendc.simulator.flow2.OutHandler; -import org.opendc.simulator.flow2.OutPort; -import org.opendc.simulator.flow2.Outlet; - -/** - * A flow source that replays a sequence of fragments, each indicating the flow rate for some period of time. - */ -public final class TraceFlowSource implements FlowSource, FlowStageLogic { - private final OutPort output; - private final long[] deadlines; - private final float[] usages; - private final int size; - private int index; - - private final FlowStage stage; - private final Consumer completionHandler; - - /** - * Construct a {@link TraceFlowSource}. - * - * @param graph The {@link FlowGraph} to which the source belongs. - * @param trace The {@link Trace} to replay. - * @param completionHandler The completion handler to invoke when the source finishes. - */ - public TraceFlowSource(FlowGraph graph, Trace trace, Consumer completionHandler) { - this.stage = graph.newStage(this); - this.output = stage.getOutlet("out"); - this.output.setHandler(new OutHandler() { - @Override - public void onPull(OutPort port, float capacity) {} - - @Override - public void onDownstreamFinish(OutPort port, Throwable cause) { - // Source cannot complete without re-connecting to another sink, so mark the source as completed - completionHandler.accept(TraceFlowSource.this); - } - }); - this.deadlines = trace.deadlines; - this.usages = trace.usages; - this.size = trace.size; - this.completionHandler = completionHandler; - } - - /** - * Construct a {@link TraceFlowSource}. - * - * @param graph The {@link FlowGraph} to which the source belongs. - * @param trace The {@link Trace} to replay. - */ - public TraceFlowSource(FlowGraph graph, Trace trace) { - this(graph, trace, TraceFlowSource::close); - } - - @Override - public Outlet getOutput() { - return output; - } - - /** - * Remove this node from the graph. - */ - public void close() { - stage.close(); - } - - @Override - public long onUpdate(FlowStage ctx, long now) { - int size = this.size; - int index = this.index; - long[] deadlines = this.deadlines; - long deadline; - - do { - deadline = deadlines[index]; - } while (deadline <= now && ++index < size); - - if (index >= size) { - output.push(0.0f); - completionHandler.accept(this); - return Long.MAX_VALUE; - } - - this.index = index; - float usage = usages[index]; - output.push(usage); - - return deadline; - } - - /** - * A trace describes the workload over time. - */ - public static final class Trace { - private final long[] deadlines; - private final float[] usages; - private final int size; - - /** - * Construct a {@link Trace}. - * - * @param deadlines The deadlines of the trace fragments. - * @param usages The usages of the trace fragments. - * @param size The size of the trace. - */ - public Trace(long[] deadlines, float[] usages, int size) { - this.deadlines = deadlines; - this.usages = usages; - this.size = size; - } - - public long[] getDeadlines() { - return deadlines; - } - - public float[] getUsages() { - return usages; - } - - public int getSize() { - return size; - } - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/util/FlowTransform.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/util/FlowTransform.java deleted file mode 100644 index 51ea7df3..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/util/FlowTransform.java +++ /dev/null @@ -1,41 +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.flow2.util; - -import org.opendc.simulator.flow2.FlowGraph; - -/** - * A {@link FlowTransform} describes a transformation between two components in a {@link FlowGraph} that might operate - * at different units of flow. - */ -public interface FlowTransform { - /** - * Apply the transform to the specified flow rate. - */ - float apply(float value); - - /** - * Apply the inverse of the transformation to the specified flow rate. - */ - float applyInverse(float value); -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/util/FlowTransformer.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/util/FlowTransformer.java deleted file mode 100644 index 852240d8..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/util/FlowTransformer.java +++ /dev/null @@ -1,124 +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.flow2.util; - -import org.opendc.simulator.flow2.*; -import org.opendc.simulator.flow2.sink.FlowSink; -import org.opendc.simulator.flow2.source.FlowSource; - -/** - * Helper class to transform flow from outlet to inlet. - */ -public final class FlowTransformer implements FlowStageLogic, FlowSource, FlowSink { - private final FlowStage stage; - private final InPort input; - private final OutPort output; - - /** - * Construct a new {@link FlowTransformer}. - */ - public FlowTransformer(FlowGraph graph, FlowTransform transform) { - this.stage = graph.newStage(this); - this.input = stage.getInlet("in"); - this.output = stage.getOutlet("out"); - - this.input.setHandler(new ForwardInHandler(output, transform)); - this.input.setMask(true); - this.output.setHandler(new ForwardOutHandler(input, transform)); - this.output.setMask(true); - } - - /** - * Return the {@link Outlet} of the transformer. - */ - @Override - public Outlet getOutput() { - return output; - } - - /** - * Return the {@link Inlet} of the transformer. - */ - @Override - public Inlet getInput() { - return input; - } - - /** - * Close the transformer. - */ - void close() { - stage.close(); - } - - @Override - public long onUpdate(FlowStage ctx, long now) { - return Long.MAX_VALUE; - } - - private static class ForwardInHandler implements InHandler { - private final OutPort output; - private final FlowTransform transform; - - ForwardInHandler(OutPort output, FlowTransform transform) { - this.output = output; - this.transform = transform; - } - - @Override - public float getRate(InPort port) { - return transform.applyInverse(output.getRate()); - } - - @Override - public void onPush(InPort port, float demand) { - float rate = transform.apply(demand); - output.push(rate); - } - - @Override - public void onUpstreamFinish(InPort port, Throwable cause) { - output.fail(cause); - } - } - - private static class ForwardOutHandler implements OutHandler { - private final InPort input; - private final FlowTransform transform; - - ForwardOutHandler(InPort input, FlowTransform transform) { - this.input = input; - this.transform = transform; - } - - @Override - public void onPull(OutPort port, float capacity) { - input.pull(transform.applyInverse(capacity)); - } - - @Override - public void onDownstreamFinish(OutPort port, Throwable cause) { - input.cancel(cause); - } - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/util/FlowTransforms.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/util/FlowTransforms.java deleted file mode 100644 index 428dbfca..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/util/FlowTransforms.java +++ /dev/null @@ -1,57 +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.flow2.util; - -/** - * A collection of common {@link FlowTransform} implementations. - */ -public class FlowTransforms { - /** - * Prevent construction of this class. - */ - private FlowTransforms() {} - - /** - * Return a {@link FlowTransform} that forwards the flow rate unmodified. - */ - public static FlowTransform noop() { - return NoopFlowTransform.INSTANCE; - } - - /** - * No-op implementation of a {@link FlowTransform}. - */ - private static final class NoopFlowTransform implements FlowTransform { - static final NoopFlowTransform INSTANCE = new NoopFlowTransform(); - - @Override - public float apply(float value) { - return value; - } - - @Override - public float applyInverse(float value) { - return value; - } - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/InvocationStackTest.kt b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/InvocationStackTest.kt new file mode 100644 index 00000000..7744d7b2 --- /dev/null +++ b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/InvocationStackTest.kt @@ -0,0 +1,70 @@ +/* + * 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. + */ + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import org.opendc.simulator.engine.InvocationStack + +/** + * Test suite for the [InvocationStack] class. + */ +class InvocationStackTest { + private val stack = InvocationStack(2) + + @Test + fun testPollEmpty() { + assertEquals(Long.MAX_VALUE, stack.poll()) + } + + @Test + fun testAddSingle() { + assertTrue(stack.tryAdd(10)) + assertEquals(10, stack.poll()) + } + + @Test + fun testAddLater() { + assertTrue(stack.tryAdd(10)) + assertFalse(stack.tryAdd(15)) + assertEquals(10, stack.poll()) + } + + @Test + fun testAddEarlier() { + assertTrue(stack.tryAdd(10)) + assertTrue(stack.tryAdd(5)) + assertEquals(5, stack.poll()) + assertEquals(10, stack.poll()) + } + + @Test + fun testCapacityExceeded() { + assertTrue(stack.tryAdd(10)) + assertTrue(stack.tryAdd(5)) + assertTrue(stack.tryAdd(2)) + assertEquals(2, stack.poll()) + assertEquals(5, stack.poll()) + assertEquals(10, stack.poll()) + } +} diff --git a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/FlowEngineTest.kt b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/FlowEngineTest.kt deleted file mode 100644 index 413a5878..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/FlowEngineTest.kt +++ /dev/null @@ -1,210 +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.flow2 - -import io.mockk.mockk -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertNotEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.opendc.simulator.flow2.mux.MaxMinFlowMultiplexer -import org.opendc.simulator.flow2.sink.SimpleFlowSink -import org.opendc.simulator.flow2.source.SimpleFlowSource -import org.opendc.simulator.kotlin.runSimulation - -/** - * Smoke tests for the Flow API. - */ -class FlowEngineTest { - @Test - fun testSmoke() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val multiplexer = MaxMinFlowMultiplexer(graph) - val sink = SimpleFlowSink(graph, 2.0f) - - graph.connect(multiplexer.newOutput(), sink.input) - - val sourceA = SimpleFlowSource(graph, 2000.0f, 0.8f) - val sourceB = SimpleFlowSource(graph, 2000.0f, 0.8f) - - graph.connect(sourceA.output, multiplexer.newInput()) - graph.connect(sourceB.output, multiplexer.newInput()) - } - - @Test - fun testConnectInvalidInlet() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val inlet = mockk() - val source = SimpleFlowSource(graph, 2000.0f, 0.8f) - assertThrows { graph.connect(source.output, inlet) } - } - - @Test - fun testConnectInvalidOutlet() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val outlet = mockk() - val sink = SimpleFlowSink(graph, 2.0f) - assertThrows { graph.connect(outlet, sink.input) } - } - - @Test - fun testConnectInletBelongsToDifferentGraph() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graphA = engine.newGraph() - val graphB = engine.newGraph() - - val sink = SimpleFlowSink(graphB, 2.0f) - val source = SimpleFlowSource(graphA, 2000.0f, 0.8f) - - assertThrows { graphA.connect(source.output, sink.input) } - } - - @Test - fun testConnectOutletBelongsToDifferentGraph() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graphA = engine.newGraph() - val graphB = engine.newGraph() - - val sink = SimpleFlowSink(graphA, 2.0f) - val source = SimpleFlowSource(graphB, 2000.0f, 0.8f) - - assertThrows { graphA.connect(source.output, sink.input) } - } - - @Test - fun testConnectInletAlreadyConnected() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val sink = SimpleFlowSink(graph, 2.0f) - val sourceA = SimpleFlowSource(graph, 2000.0f, 0.8f) - val sourceB = SimpleFlowSource(graph, 2000.0f, 0.8f) - - graph.connect(sourceA.output, sink.input) - assertThrows { graph.connect(sourceB.output, sink.input) } - } - - @Test - fun testConnectOutletAlreadyConnected() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val sinkA = SimpleFlowSink(graph, 2.0f) - val sinkB = SimpleFlowSink(graph, 2.0f) - val source = SimpleFlowSource(graph, 2000.0f, 0.8f) - - graph.connect(source.output, sinkA.input) - assertThrows { graph.connect(source.output, sinkB.input) } - } - - @Test - fun testDisconnectInletInvalid() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val inlet = mockk() - assertThrows { graph.disconnect(inlet) } - } - - @Test - fun testDisconnectOutletInvalid() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val outlet = mockk() - assertThrows { graph.disconnect(outlet) } - } - - @Test - fun testDisconnectInletInvalidGraph() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graphA = engine.newGraph() - val graphB = engine.newGraph() - - val sink = SimpleFlowSink(graphA, 2.0f) - - assertThrows { graphB.disconnect(sink.input) } - } - - @Test - fun testDisconnectOutletInvalidGraph() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graphA = engine.newGraph() - val graphB = engine.newGraph() - - val source = SimpleFlowSource(graphA, 2000.0f, 0.8f) - - assertThrows { graphB.disconnect(source.output) } - } - - @Test - fun testInletEquality() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val sinkA = SimpleFlowSink(graph, 2.0f) - val sinkB = SimpleFlowSink(graph, 2.0f) - - val multiplexer = MaxMinFlowMultiplexer(graph) - - assertEquals(sinkA.input, sinkA.input) - assertNotEquals(sinkA.input, sinkB.input) - - assertNotEquals(multiplexer.newInput(), multiplexer.newInput()) - } - - @Test - fun testOutletEquality() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val sourceA = SimpleFlowSource(graph, 2000.0f, 0.8f) - val sourceB = SimpleFlowSource(graph, 2000.0f, 0.8f) - - val multiplexer = MaxMinFlowMultiplexer(graph) - - assertEquals(sourceA.output, sourceA.output) - assertNotEquals(sourceA.output, sourceB.output) - - assertNotEquals(multiplexer.newOutput(), multiplexer.newOutput()) - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/FlowTimerQueueTest.kt b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/FlowTimerQueueTest.kt deleted file mode 100644 index 059bd5f5..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/FlowTimerQueueTest.kt +++ /dev/null @@ -1,385 +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.flow2 - -import io.mockk.mockk -import org.junit.jupiter.api.Assertions.assertAll -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertNull -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test - -/** - * Test suite for the [FlowTimerQueue] class. - */ -class FlowTimerQueueTest { - private lateinit var queue: FlowTimerQueue - - @BeforeEach - fun setUp() { - queue = FlowTimerQueue(3) - } - - /** - * Test whether a call to [FlowTimerQueue.poll] returns `null` for an empty queue. - */ - @Test - fun testPollEmpty() { - assertAll( - { assertEquals(Long.MAX_VALUE, queue.peekDeadline()) }, - { assertNull(queue.poll(100L)) }, - ) - } - - /** - * Test whether a call to [FlowTimerQueue.poll] returns the proper value for a queue with a single entry. - */ - @Test - fun testSingleEntry() { - val entry = mockk() - entry.deadline = 100 - entry.timerIndex = -1 - - queue.enqueue(entry) - - assertAll( - { assertEquals(100, queue.peekDeadline()) }, - { assertNull(queue.poll(10L)) }, - { assertEquals(entry, queue.poll(200L)) }, - { assertNull(queue.poll(200L)) }, - ) - } - - /** - * Test whether [FlowTimerQueue.poll] returns values in the queue in the proper order. - */ - @Test - fun testMultipleEntries() { - val entryA = mockk() - entryA.deadline = 100 - entryA.timerIndex = -1 - - queue.enqueue(entryA) - - val entryB = mockk() - entryB.deadline = 10 - entryB.timerIndex = -1 - - queue.enqueue(entryB) - - val entryC = mockk() - entryC.deadline = 58 - entryC.timerIndex = -1 - - queue.enqueue(entryC) - - assertAll( - { assertEquals(10, queue.peekDeadline()) }, - { assertEquals(entryB, queue.poll(100L)) }, - { assertEquals(entryC, queue.poll(100L)) }, - { assertEquals(entryA, queue.poll(100L)) }, - { assertNull(queue.poll(100L)) }, - ) - } - - /** - * Test that the queue is properly resized when the number of entries exceed the capacity. - */ - @Test - fun testResize() { - val entryA = mockk() - entryA.deadline = 100 - entryA.timerIndex = -1 - - queue.enqueue(entryA) - - val entryB = mockk() - entryB.deadline = 20 - entryB.timerIndex = -1 - - queue.enqueue(entryB) - - val entryC = mockk() - entryC.deadline = 58 - entryC.timerIndex = -1 - - queue.enqueue(entryC) - - val entryD = mockk() - entryD.deadline = 31 - entryD.timerIndex = -1 - - queue.enqueue(entryD) - - assertAll( - { assertEquals(20, queue.peekDeadline()) }, - { assertEquals(entryB, queue.poll(100L)) }, - { assertEquals(entryD, queue.poll(100L)) }, - { assertEquals(entryC, queue.poll(100L)) }, - { assertEquals(entryA, queue.poll(100L)) }, - { assertNull(queue.poll(100L)) }, - ) - } - - /** - * Test to verify that we can change the deadline of the last element in the queue. - */ - @Test - fun testChangeDeadlineTail() { - val entryA = mockk() - entryA.deadline = 100 - entryA.timerIndex = -1 - - queue.enqueue(entryA) - - val entryB = mockk() - entryB.deadline = 20 - entryB.timerIndex = -1 - - queue.enqueue(entryB) - - val entryC = mockk() - entryC.deadline = 58 - entryC.timerIndex = -1 - - queue.enqueue(entryC) - - entryA.deadline = 10 - queue.enqueue(entryA) - - assertAll( - { assertEquals(10, queue.peekDeadline()) }, - { assertEquals(entryA, queue.poll(100L)) }, - { assertEquals(entryB, queue.poll(100L)) }, - { assertEquals(entryC, queue.poll(100L)) }, - { assertNull(queue.poll(100L)) }, - ) - } - - /** - * Test that we can change the deadline of the head entry in the queue. - */ - @Test - fun testChangeDeadlineMiddle() { - val entryA = mockk() - entryA.deadline = 100 - entryA.timerIndex = -1 - - queue.enqueue(entryA) - - val entryB = mockk() - entryB.deadline = 20 - entryB.timerIndex = -1 - - queue.enqueue(entryB) - - val entryC = mockk() - entryC.deadline = 58 - entryC.timerIndex = -1 - - queue.enqueue(entryC) - - entryC.deadline = 10 - queue.enqueue(entryC) - - assertAll( - { assertEquals(10, queue.peekDeadline()) }, - { assertEquals(entryC, queue.poll(100L)) }, - { assertEquals(entryB, queue.poll(100L)) }, - { assertEquals(entryA, queue.poll(100L)) }, - { assertNull(queue.poll(100L)) }, - ) - } - - /** - * Test that we can change the deadline of the head entry in the queue. - */ - @Test - fun testChangeDeadlineHead() { - val entryA = mockk() - entryA.deadline = 100 - entryA.timerIndex = -1 - - queue.enqueue(entryA) - - val entryB = mockk() - entryB.deadline = 20 - entryB.timerIndex = -1 - - queue.enqueue(entryB) - - val entryC = mockk() - entryC.deadline = 58 - entryC.timerIndex = -1 - - queue.enqueue(entryC) - - entryB.deadline = 30 - queue.enqueue(entryB) - - assertAll( - { assertEquals(30, queue.peekDeadline()) }, - { assertEquals(entryB, queue.poll(100L)) }, - { assertEquals(entryC, queue.poll(100L)) }, - { assertEquals(entryA, queue.poll(100L)) }, - { assertNull(queue.poll(100L)) }, - ) - } - - /** - * Test that an unchanged deadline results in a no-op. - */ - @Test - fun testChangeDeadlineNop() { - val entryA = mockk() - entryA.deadline = 100 - entryA.timerIndex = -1 - - queue.enqueue(entryA) - - val entryB = mockk() - entryB.deadline = 20 - entryB.timerIndex = -1 - - queue.enqueue(entryB) - - val entryC = mockk() - entryC.deadline = 58 - entryC.timerIndex = -1 - - queue.enqueue(entryC) - - // Should be a no-op - queue.enqueue(entryA) - - assertAll( - { assertEquals(20, queue.peekDeadline()) }, - { assertEquals(entryB, queue.poll(100L)) }, - { assertEquals(entryC, queue.poll(100L)) }, - { assertEquals(entryA, queue.poll(100L)) }, - { assertNull(queue.poll(100L)) }, - ) - } - - /** - * Test that we can remove an entry from the end of the queue. - */ - @Test - fun testRemoveEntryTail() { - val entryA = mockk() - entryA.deadline = 100 - entryA.timerIndex = -1 - - queue.enqueue(entryA) - - val entryB = mockk() - entryB.deadline = 20 - entryB.timerIndex = -1 - - queue.enqueue(entryB) - - val entryC = mockk() - entryC.deadline = 58 - entryC.timerIndex = -1 - - queue.enqueue(entryC) - - entryC.deadline = Long.MAX_VALUE - queue.enqueue(entryC) - - assertAll( - { assertEquals(20, queue.peekDeadline()) }, - { assertEquals(entryB, queue.poll(100L)) }, - { assertEquals(entryA, queue.poll(100L)) }, - { assertNull(queue.poll(100L)) }, - ) - } - - /** - * Test that we can remove an entry from the head of the queue. - */ - @Test - fun testRemoveEntryHead() { - val entryA = mockk() - entryA.deadline = 100 - entryA.timerIndex = -1 - - queue.enqueue(entryA) - - val entryB = mockk() - entryB.deadline = 20 - entryB.timerIndex = -1 - - queue.enqueue(entryB) - - val entryC = mockk() - entryC.deadline = 58 - entryC.timerIndex = -1 - - queue.enqueue(entryC) - - entryB.deadline = Long.MAX_VALUE - queue.enqueue(entryB) - - assertAll( - { assertEquals(58, queue.peekDeadline()) }, - { assertEquals(entryC, queue.poll(100L)) }, - { assertEquals(entryA, queue.poll(100L)) }, - { assertNull(queue.poll(100L)) }, - ) - } - - /** - * Test that we can remove an entry from the middle of a queue. - */ - @Test - fun testRemoveEntryMiddle() { - val entryA = mockk() - entryA.deadline = 100 - entryA.timerIndex = -1 - - queue.enqueue(entryA) - - val entryB = mockk() - entryB.deadline = 20 - entryB.timerIndex = -1 - - queue.enqueue(entryB) - - val entryC = mockk() - entryC.deadline = 58 - entryC.timerIndex = -1 - - queue.enqueue(entryC) - - entryC.deadline = Long.MAX_VALUE - queue.enqueue(entryC) - - assertAll( - { assertEquals(20, queue.peekDeadline()) }, - { assertEquals(entryB, queue.poll(100L)) }, - { assertEquals(entryA, queue.poll(100L)) }, - { assertNull(queue.poll(100L)) }, - ) - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/InvocationStackTest.kt b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/InvocationStackTest.kt deleted file mode 100644 index 2250fe87..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/InvocationStackTest.kt +++ /dev/null @@ -1,71 +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.flow2 - -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertFalse -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.Test - -/** - * Test suite for the [InvocationStack] class. - */ -class InvocationStackTest { - private val stack = InvocationStack(2) - - @Test - fun testPollEmpty() { - assertEquals(Long.MAX_VALUE, stack.poll()) - } - - @Test - fun testAddSingle() { - assertTrue(stack.tryAdd(10)) - assertEquals(10, stack.poll()) - } - - @Test - fun testAddLater() { - assertTrue(stack.tryAdd(10)) - assertFalse(stack.tryAdd(15)) - assertEquals(10, stack.poll()) - } - - @Test - fun testAddEarlier() { - assertTrue(stack.tryAdd(10)) - assertTrue(stack.tryAdd(5)) - assertEquals(5, stack.poll()) - assertEquals(10, stack.poll()) - } - - @Test - fun testCapacityExceeded() { - assertTrue(stack.tryAdd(10)) - assertTrue(stack.tryAdd(5)) - assertTrue(stack.tryAdd(2)) - assertEquals(2, stack.poll()) - assertEquals(5, stack.poll()) - assertEquals(10, stack.poll()) - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/ForwardingFlowMultiplexerTest.kt b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/ForwardingFlowMultiplexerTest.kt deleted file mode 100644 index 2aef5174..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/ForwardingFlowMultiplexerTest.kt +++ /dev/null @@ -1,72 +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.flow2.mux - -import kotlinx.coroutines.yield -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertAll -import org.opendc.simulator.flow2.FlowEngine -import org.opendc.simulator.flow2.sink.SimpleFlowSink -import org.opendc.simulator.flow2.source.TraceFlowSource -import org.opendc.simulator.kotlin.runSimulation - -/** - * Test suite for the [ForwardingFlowMultiplexer] class. - */ -class ForwardingFlowMultiplexerTest { - /** - * Test a trace workload. - */ - @Test - fun testTrace() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val switch = ForwardingFlowMultiplexer(graph) - val sink = SimpleFlowSink(graph, 3200.0f) - graph.connect(switch.newOutput(), sink.input) - - yield() - - assertEquals(sink.capacity, switch.capacity) { "Capacity is not detected" } - - val workload = - TraceFlowSource( - graph, - TraceFlowSource.Trace( - longArrayOf(1000, 2000, 3000, 4000), - floatArrayOf(28.0f, 3500.0f, 0.0f, 183.0f), - 4, - ), - ) - graph.connect(workload.output, switch.newInput()) - - advanceUntilIdle() - - assertAll( - { assertEquals(4000, timeSource.millis()) { "Took enough time" } }, - ) - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/MaxMinFlowMultiplexerTest.kt b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/MaxMinFlowMultiplexerTest.kt deleted file mode 100644 index 0bcf4a3f..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/MaxMinFlowMultiplexerTest.kt +++ /dev/null @@ -1,55 +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.flow2.mux - -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.opendc.simulator.flow2.FlowEngine -import org.opendc.simulator.flow2.sink.SimpleFlowSink -import org.opendc.simulator.flow2.source.SimpleFlowSource -import org.opendc.simulator.kotlin.runSimulation - -/** - * Test suite for the [MaxMinFlowMultiplexer] class. - */ -class MaxMinFlowMultiplexerTest { - @Test - fun testSmoke() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val switch = MaxMinFlowMultiplexer(graph) - - val sinks = List(2) { SimpleFlowSink(graph, 2000.0f) } - for (source in sinks) { - graph.connect(switch.newOutput(), source.input) - } - - val source = SimpleFlowSource(graph, 2000.0f, 1.0f) - graph.connect(source.output, switch.newInput()) - - advanceUntilIdle() - - assertEquals(500, timeSource.millis()) - } -} diff --git a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/sink/FlowSinkTest.kt b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/sink/FlowSinkTest.kt deleted file mode 100644 index 7085a4b9..00000000 --- a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/sink/FlowSinkTest.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.flow2.sink - -import kotlinx.coroutines.delay -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.opendc.simulator.flow2.FlowEngine -import org.opendc.simulator.flow2.source.SimpleFlowSource -import org.opendc.simulator.flow2.source.TraceFlowSource -import org.opendc.simulator.kotlin.runSimulation -import java.util.concurrent.ThreadLocalRandom - -/** - * Test suite for the [SimpleFlowSink] class. - */ -class FlowSinkTest { - @Test - fun testSmoke() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val sink = SimpleFlowSink(graph, 1.0f) - val source = SimpleFlowSource(graph, 2.0f, 1.0f) - - graph.connect(source.output, sink.input) - advanceUntilIdle() - - assertEquals(2000, timeSource.millis()) - } - - @Test - fun testAdjustCapacity() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val sink = SimpleFlowSink(graph, 1.0f) - val source = SimpleFlowSource(graph, 2.0f, 1.0f) - - graph.connect(source.output, sink.input) - - delay(1000) - sink.capacity = 0.5f - - advanceUntilIdle() - - assertEquals(3000, timeSource.millis()) - } - - @Test - fun testUtilization() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val sink = SimpleFlowSink(graph, 1.0f) - val source = SimpleFlowSource(graph, 2.0f, 0.5f) - - graph.connect(source.output, sink.input) - advanceUntilIdle() - - assertEquals(4000, timeSource.millis()) - } - - @Test - fun testFragments() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - - val sink = SimpleFlowSink(graph, 1.0f) - val trace = - TraceFlowSource.Trace( - longArrayOf(1000, 2000, 3000, 4000), - floatArrayOf(1.0f, 0.5f, 2.0f, 1.0f), - 4, - ) - val source = - TraceFlowSource( - graph, - trace, - ) - - graph.connect(source.output, sink.input) - advanceUntilIdle() - - assertEquals(4000, timeSource.millis()) - } - - @Test - fun benchmarkSink() { - val random = ThreadLocalRandom.current() - val traceSize = 10000000 - val trace = - TraceFlowSource.Trace( - LongArray(traceSize) { it * 1000L }, - FloatArray(traceSize) { random.nextDouble(0.0, 4500.0).toFloat() }, - traceSize, - ) - - return runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val sink = SimpleFlowSink(graph, 4200.0f) - val source = TraceFlowSource(graph, trace) - graph.connect(source.output, sink.input) - } - } -} diff --git a/opendc-simulator/opendc-simulator-network/build.gradle.kts b/opendc-simulator/opendc-simulator-network/build.gradle.kts deleted file mode 100644 index a7f309c0..00000000 --- a/opendc-simulator/opendc-simulator-network/build.gradle.kts +++ /dev/null @@ -1,34 +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. - */ - -description = "Library for simulating datacenter network components" - -plugins { - `kotlin-library-conventions` -} - -dependencies { - api(projects.opendcSimulator.opendcSimulatorFlow) - implementation(projects.opendcSimulator.opendcSimulatorCore) - - testImplementation(libs.slf4j.simple) -} diff --git a/opendc-simulator/opendc-simulator-network/src/main/java/org/opendc/simulator/network/SimNetworkLink.java b/opendc-simulator/opendc-simulator-network/src/main/java/org/opendc/simulator/network/SimNetworkLink.java deleted file mode 100644 index 1ea9cb0e..00000000 --- a/opendc-simulator/opendc-simulator-network/src/main/java/org/opendc/simulator/network/SimNetworkLink.java +++ /dev/null @@ -1,77 +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.network; - -/** - * A physical bidirectional communication link between two [SimNetworkPort]s. - */ -public final class SimNetworkLink { - private final SimNetworkPort left; - private final SimNetworkPort right; - - SimNetworkLink(SimNetworkPort left, SimNetworkPort right) { - this.left = left; - this.right = right; - } - - /** - * Determine whether the specified port participates in this network link. - * - * @return true if the port participates in this link, false otherwise. - */ - public boolean contains(SimNetworkPort port) { - return port == left || port == right; - } - - /** - * Obtain the opposite port to which the specified port is connected through this link. - */ - public SimNetworkPort opposite(SimNetworkPort port) { - if (port == left) { - return right; - } else if (port == right) { - return left; - } - - throw new IllegalArgumentException("Invalid port given"); - } - - /** - * Return the first port of the link. - */ - public SimNetworkPort getLeft() { - return left; - } - - /** - * Return the second port of the link. - */ - public SimNetworkPort getRight() { - return right; - } - - @Override - public String toString() { - return "SimNetworkLink[left=" + left + ",right=" + right + "]"; - } -} diff --git a/opendc-simulator/opendc-simulator-network/src/main/java/org/opendc/simulator/network/SimNetworkPort.java b/opendc-simulator/opendc-simulator-network/src/main/java/org/opendc/simulator/network/SimNetworkPort.java deleted file mode 100644 index b5e09b9b..00000000 --- a/opendc-simulator/opendc-simulator-network/src/main/java/org/opendc/simulator/network/SimNetworkPort.java +++ /dev/null @@ -1,110 +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.network; - -import org.opendc.simulator.flow2.Inlet; -import org.opendc.simulator.flow2.Outlet; - -/** - * A network port allows network devices to be connected to network through links. - */ -public abstract class SimNetworkPort { - SimNetworkLink link; - - /** - * Determine whether the network port is connected to another port. - * - * @return true if the network port is connected, false otherwise. - */ - public boolean isConnected() { - return link != null; - } - - /** - * Return network link which connects this port to another port. - */ - public SimNetworkLink getLink() { - return link; - } - - /** - * Connect this port to the specified port. - */ - public void connect(SimNetworkPort port) { - if (port == this) { - throw new IllegalArgumentException("Circular reference"); - } - if (isConnected()) { - throw new IllegalStateException("Port already connected"); - } - if (port.isConnected()) { - throw new IllegalStateException("Target port already connected"); - } - - final SimNetworkLink link = new SimNetworkLink(this, port); - this.link = link; - port.link = link; - - // Start bidirectional flow channel between the two ports - final Outlet outlet = getOutlet(); - final Inlet inlet = getInlet(); - - outlet.getGraph().connect(outlet, port.getInlet()); - inlet.getGraph().connect(port.getOutlet(), inlet); - } - - /** - * Disconnect the current network link if it exists. - */ - public void disconnect() { - final SimNetworkLink link = this.link; - if (link == null) { - return; - } - - final SimNetworkPort opposite = link.opposite(this); - this.link = null; - opposite.link = null; - - final Outlet outlet = getOutlet(); - final Inlet inlet = getInlet(); - - outlet.getGraph().disconnect(outlet); - inlet.getGraph().disconnect(inlet); - } - - /** - * Return the {@link Outlet} representing the outgoing traffic of this port. - */ - protected abstract Outlet getOutlet(); - - /** - * An [Inlet] representing the ingoing traffic of this port. - */ - protected abstract Inlet getInlet(); - - @Override - public String toString() { - return "SimNetworkPort[isConnected=" + isConnected() + "]"; - } -} diff --git a/opendc-simulator/opendc-simulator-network/src/main/java/org/opendc/simulator/network/SimNetworkSink.java b/opendc-simulator/opendc-simulator-network/src/main/java/org/opendc/simulator/network/SimNetworkSink.java deleted file mode 100644 index f8918328..00000000 --- a/opendc-simulator/opendc-simulator-network/src/main/java/org/opendc/simulator/network/SimNetworkSink.java +++ /dev/null @@ -1,70 +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.network; - -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.source.EmptyFlowSource; - -/** - * A network sink which discards all received traffic and does not generate any traffic itself. - */ -public final class SimNetworkSink extends SimNetworkPort { - private final EmptyFlowSource source; - private final SimpleFlowSink sink; - - /** - * Construct a {@link SimNetworkSink} instance. - * - * @param graph The {@link FlowGraph} to which the sink belongs. - * @param capacity The capacity of the sink in terms of processed data. - */ - public SimNetworkSink(FlowGraph graph, float capacity) { - this.source = new EmptyFlowSource(graph); - this.sink = new SimpleFlowSink(graph, capacity); - } - - /** - * Return the capacity of the sink. - */ - public float getCapacity() { - return sink.getCapacity(); - } - - @Override - protected Outlet getOutlet() { - return source.getOutput(); - } - - @Override - protected Inlet getInlet() { - return sink.getInput(); - } - - @Override - public String toString() { - return "SimNetworkSink[capacity=" + getCapacity() + "]"; - } -} diff --git a/opendc-simulator/opendc-simulator-network/src/main/java/org/opendc/simulator/network/SimNetworkSwitch.java b/opendc-simulator/opendc-simulator-network/src/main/java/org/opendc/simulator/network/SimNetworkSwitch.java deleted file mode 100644 index b05dc53d..00000000 --- a/opendc-simulator/opendc-simulator-network/src/main/java/org/opendc/simulator/network/SimNetworkSwitch.java +++ /dev/null @@ -1,35 +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.network; - -import java.util.List; - -/** - * A network device connects devices on a network by switching the traffic over its ports. - */ -public interface SimNetworkSwitch { - /** - * Return the ports of the switch. - */ - List getPorts(); -} diff --git a/opendc-simulator/opendc-simulator-network/src/main/java/org/opendc/simulator/network/SimNetworkSwitchVirtual.java b/opendc-simulator/opendc-simulator-network/src/main/java/org/opendc/simulator/network/SimNetworkSwitchVirtual.java deleted file mode 100644 index a94bf799..00000000 --- a/opendc-simulator/opendc-simulator-network/src/main/java/org/opendc/simulator/network/SimNetworkSwitchVirtual.java +++ /dev/null @@ -1,107 +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.network; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import org.opendc.simulator.flow2.FlowGraph; -import org.opendc.simulator.flow2.Inlet; -import org.opendc.simulator.flow2.Outlet; -import org.opendc.simulator.flow2.mux.FlowMultiplexer; -import org.opendc.simulator.flow2.mux.MaxMinFlowMultiplexer; - -/** - * A {@link SimNetworkSwitch} that can support new networking ports on demand. - */ -public final class SimNetworkSwitchVirtual implements SimNetworkSwitch { - private final List ports = new ArrayList<>(); - - /** - * The {@link MaxMinFlowMultiplexer} to actually perform the switching. - */ - private final MaxMinFlowMultiplexer mux; - - /** - * Construct a {@link SimNetworkSwitchVirtual} instance. - * - * @param graph The {@link FlowGraph} to drive the simulation. - */ - public SimNetworkSwitchVirtual(FlowGraph graph) { - this.mux = new MaxMinFlowMultiplexer(graph); - } - - /** - * Open a new port on the switch. - */ - public Port newPort() { - final Port port = new Port(mux); - ports.add(port); - return port; - } - - @Override - public List getPorts() { - return Collections.unmodifiableList(ports); - } - - /** - * A port on the network switch. - */ - public class Port extends SimNetworkPort implements AutoCloseable { - private final FlowMultiplexer mux; - private final Inlet inlet; - private final Outlet outlet; - private boolean isClosed; - - private Port(FlowMultiplexer mux) { - this.mux = mux; - this.inlet = mux.newInput(); - this.outlet = mux.newOutput(); - } - - @Override - protected Outlet getOutlet() { - if (isClosed) { - throw new IllegalStateException("Port is closed"); - } - return outlet; - } - - @Override - protected Inlet getInlet() { - if (isClosed) { - throw new IllegalStateException("Port is closed"); - } - return inlet; - } - - @Override - public void close() { - isClosed = true; - mux.releaseInput(inlet); - mux.releaseOutput(outlet); - ports.remove(this); - } - } -} diff --git a/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkLinkTest.kt b/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkLinkTest.kt deleted file mode 100644 index 9863507d..00000000 --- a/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkLinkTest.kt +++ /dev/null @@ -1,91 +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.network - -import io.mockk.mockk -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertFalse -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows - -/** - * Test suite for [SimNetworkLink] class. - */ -class SimNetworkLinkTest { - @Test - fun testContainsLeft() { - val left = mockk() - val right = mockk() - - val link = SimNetworkLink(left, right) - assertTrue(left in link) - } - - @Test - fun testContainsRight() { - val left = mockk() - val right = mockk() - - val link = SimNetworkLink(left, right) - assertTrue(right in link) - } - - @Test - fun testContainsNone() { - val left = mockk() - val right = mockk() - val none = mockk() - - val link = SimNetworkLink(left, right) - assertFalse(none in link) - } - - @Test - fun testOppositeLeft() { - val left = mockk() - val right = mockk() - - val link = SimNetworkLink(left, right) - assertEquals(right, link.opposite(left)) - } - - @Test - fun testOppositeRight() { - val left = mockk() - val right = mockk() - - val link = SimNetworkLink(left, right) - assertEquals(left, link.opposite(right)) - } - - @Test - fun testOppositeNone() { - val left = mockk() - val right = mockk() - val none = mockk() - - val link = SimNetworkLink(left, right) - assertThrows { link.opposite(none) } - } -} diff --git a/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSinkTest.kt b/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSinkTest.kt deleted file mode 100644 index 4655bfea..00000000 --- a/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSinkTest.kt +++ /dev/null @@ -1,153 +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.network - -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import kotlinx.coroutines.yield -import org.junit.jupiter.api.Assertions.assertAll -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertFalse -import org.junit.jupiter.api.Assertions.assertNull -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertDoesNotThrow -import org.junit.jupiter.api.assertThrows -import org.opendc.simulator.flow2.FlowEngine -import org.opendc.simulator.kotlin.runSimulation - -/** - * Test suite for the [SimNetworkSink] class. - */ -class SimNetworkSinkTest { - @Test - fun testInitialState() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val sink = SimNetworkSink(graph, 100.0f) - - assertAll( - { assertFalse(sink.isConnected) }, - { assertNull(sink.link) }, - { assertEquals(100.0f, sink.capacity) }, - ) - } - - @Test - fun testDisconnectIdempotent() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val sink = SimNetworkSink(graph, 100.0f) - - assertDoesNotThrow { sink.disconnect() } - assertFalse(sink.isConnected) - } - - @Test - fun testConnectCircular() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val sink = SimNetworkSink(graph, 100.0f) - - assertThrows { - sink.connect(sink) - } - } - - @Test - fun testConnectAlreadyConnectedTarget() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val sink = SimNetworkSink(graph, 100.0f) - val source = mockk(relaxUnitFun = true) - every { source.isConnected } returns true - - assertThrows { - sink.connect(source) - } - } - - @Test - fun testConnectAlreadyConnected() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val sink = SimNetworkSink(graph, 100.0f) - val source1 = TestSource(graph) - - val source2 = mockk(relaxUnitFun = true) - - every { source2.isConnected } returns false - - sink.connect(source1) - assertThrows { - sink.connect(source2) - } - } - - @Test - fun testConnect() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val sink = SimNetworkSink(graph, 100.0f) - val source = TestSource(graph) - - sink.connect(source) - - yield() - - assertAll( - { assertTrue(sink.isConnected) }, - { assertTrue(source.isConnected) }, - { assertEquals(100.0f, source.outlet.capacity) }, - ) - - verify { source.logic.onUpdate(any(), any()) } - } - - @Test - fun testDisconnect() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val sink = SimNetworkSink(graph, 100.0f) - val source = TestSource(graph) - - sink.connect(source) - sink.disconnect() - - yield() - - assertAll( - { assertFalse(sink.isConnected) }, - { assertFalse(source.isConnected) }, - { assertEquals(0.0f, source.outlet.capacity) }, - ) - } -} diff --git a/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSwitchVirtualTest.kt b/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSwitchVirtualTest.kt deleted file mode 100644 index b5a00ffc..00000000 --- a/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSwitchVirtualTest.kt +++ /dev/null @@ -1,77 +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.network - -import io.mockk.verify -import kotlinx.coroutines.yield -import org.junit.jupiter.api.Assertions.assertAll -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.opendc.simulator.flow2.FlowEngine -import org.opendc.simulator.kotlin.runSimulation - -/** - * Test suite for the [SimNetworkSwitchVirtual] class. - */ -class SimNetworkSwitchVirtualTest { - @Test - fun testConnect() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val sink = SimNetworkSink(graph, 100.0f) - val source = TestSource(graph) - val switch = SimNetworkSwitchVirtual(graph) - - switch.newPort().connect(sink) - switch.newPort().connect(source) - - yield() - - assertAll( - { assertTrue(sink.isConnected) }, - { assertTrue(source.isConnected) }, - { assertEquals(100.0f, source.outlet.capacity) }, - ) - - verify { source.logic.onUpdate(any(), any()) } - } - - @Test - fun testConnectClosedPort() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val sink = SimNetworkSink(graph, 100.0f) - val switch = SimNetworkSwitchVirtual(graph) - - val port = switch.newPort() - port.close() - - assertThrows { - port.connect(sink) - } - } -} diff --git a/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/TestSource.kt b/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/TestSource.kt deleted file mode 100644 index 298a5d48..00000000 --- a/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/TestSource.kt +++ /dev/null @@ -1,56 +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.network - -import io.mockk.spyk -import org.opendc.simulator.flow2.FlowGraph -import org.opendc.simulator.flow2.FlowStage -import org.opendc.simulator.flow2.FlowStageLogic -import org.opendc.simulator.flow2.InPort -import org.opendc.simulator.flow2.Inlet -import org.opendc.simulator.flow2.OutPort -import org.opendc.simulator.flow2.Outlet - -/** - * A [SimNetworkPort] that acts as a test source. - */ -class TestSource(graph: FlowGraph) : SimNetworkPort(), FlowStageLogic { - val logic = spyk(this) - private val stage = graph.newStage(logic) - - val outlet: OutPort = stage.getOutlet("out") - val inlet: InPort = stage.getInlet("in") - - init { - outlet.push(80.0f) - } - - override fun onUpdate( - ctx: FlowStage, - now: Long, - ): Long = Long.MAX_VALUE - - override fun getOutlet(): Outlet = outlet - - override fun getInlet(): Inlet = inlet -} diff --git a/opendc-simulator/opendc-simulator-power/build.gradle.kts b/opendc-simulator/opendc-simulator-power/build.gradle.kts deleted file mode 100644 index ea36ce75..00000000 --- a/opendc-simulator/opendc-simulator-power/build.gradle.kts +++ /dev/null @@ -1,34 +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. - */ - -description = "Library for simulating datacenter power components" - -plugins { - `kotlin-library-conventions` -} - -dependencies { - api(projects.opendcSimulator.opendcSimulatorFlow) - implementation(projects.opendcSimulator.opendcSimulatorCore) - - testImplementation(libs.slf4j.simple) -} diff --git a/opendc-simulator/opendc-simulator-power/src/main/java/org/opendc/simulator/power/SimPdu.java b/opendc-simulator/opendc-simulator-power/src/main/java/org/opendc/simulator/power/SimPdu.java deleted file mode 100644 index 8790a2d7..00000000 --- a/opendc-simulator/opendc-simulator-power/src/main/java/org/opendc/simulator/power/SimPdu.java +++ /dev/null @@ -1,141 +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.power; - -import org.jetbrains.annotations.NotNull; -import org.opendc.simulator.flow2.FlowGraph; -import org.opendc.simulator.flow2.Inlet; -import org.opendc.simulator.flow2.Outlet; -import org.opendc.simulator.flow2.mux.FlowMultiplexer; -import org.opendc.simulator.flow2.mux.MaxMinFlowMultiplexer; -import org.opendc.simulator.flow2.util.FlowTransform; -import org.opendc.simulator.flow2.util.FlowTransformer; - -/** - * A model of a Power Distribution Unit (PDU). - */ -public final class SimPdu extends SimPowerInlet { - /** - * The {@link FlowMultiplexer} that distributes the electricity over the PDU outlets. - */ - private final MaxMinFlowMultiplexer mux; - - /** - * A {@link FlowTransformer} that applies the power loss to the PDU's power inlet. - */ - private final FlowTransformer transformer; - - /** - * Construct a {@link SimPdu} instance. - * - * @param graph The underlying {@link FlowGraph} to which the PDU belongs. - * @param idlePower The idle power consumption of the PDU independent of the load on the PDU. - * @param lossCoefficient The coefficient for the power loss of the PDU proportional to the square load. - */ - public SimPdu(FlowGraph graph, float idlePower, float lossCoefficient) { - this.mux = new MaxMinFlowMultiplexer(graph); - this.transformer = new FlowTransformer(graph, new FlowTransform() { - @Override - public float apply(float value) { - // See https://download.schneider-electric.com/files?p_Doc_Ref=SPD_NRAN-66CK3D_EN - return value * (lossCoefficient * value + 1) + idlePower; - } - - @Override - public float applyInverse(float value) { - float c = lossCoefficient; - if (c != 0.f) { - return (float) (1 + Math.sqrt(4 * value * c - 4 * idlePower * c + 1)) / (2 * c); - } else { - return value - idlePower; - } - } - }); - - graph.connect(mux.newOutput(), transformer.getInput()); - } - - /** - * Construct a {@link SimPdu} instance without any loss. - * - * @param graph The underlying {@link FlowGraph} to which the PDU belongs. - */ - public SimPdu(FlowGraph graph) { - this(graph, 0.f, 0.f); - } - - /** - * Create a new PDU outlet. - */ - public PowerOutlet newOutlet() { - return new PowerOutlet(mux); - } - - @NotNull - @Override - public Outlet getFlowOutlet() { - return transformer.getOutput(); - } - - @Override - public String toString() { - return "SimPdu"; - } - - /** - * A PDU outlet. - */ - public static final class PowerOutlet extends SimPowerOutlet implements AutoCloseable { - private final FlowMultiplexer mux; - private final Inlet inlet; - private boolean isClosed; - - private PowerOutlet(FlowMultiplexer mux) { - this.mux = mux; - this.inlet = mux.newInput(); - } - - /** - * Remove the outlet from the PDU. - */ - @Override - public void close() { - isClosed = true; - mux.releaseInput(inlet); - } - - @Override - public String toString() { - return "SimPdu.Outlet"; - } - - @NotNull - @Override - protected Inlet getFlowInlet() { - if (isClosed) { - throw new IllegalStateException("Outlet is closed"); - } - return inlet; - } - } -} diff --git a/opendc-simulator/opendc-simulator-power/src/main/java/org/opendc/simulator/power/SimPowerInlet.java b/opendc-simulator/opendc-simulator-power/src/main/java/org/opendc/simulator/power/SimPowerInlet.java deleted file mode 100644 index a6e167c2..00000000 --- a/opendc-simulator/opendc-simulator-power/src/main/java/org/opendc/simulator/power/SimPowerInlet.java +++ /dev/null @@ -1,53 +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.power; - -import org.opendc.simulator.flow2.Outlet; - -/** - * An abstract inlet that consumes electricity from a power outlet. - */ -public abstract class SimPowerInlet { - SimPowerOutlet outlet; - - /** - * Determine whether the inlet is connected to a {@link SimPowerOutlet}. - * - * @return true if the inlet is connected to an outlet, false otherwise. - */ - public boolean isConnected() { - return outlet != null; - } - - /** - * Return the {@link SimPowerOutlet} to which the inlet is connected. - */ - public SimPowerOutlet getOutlet() { - return outlet; - } - - /** - * Return the flow {@link Outlet} that models the consumption of a power inlet as flow output. - */ - protected abstract Outlet getFlowOutlet(); -} diff --git a/opendc-simulator/opendc-simulator-power/src/main/java/org/opendc/simulator/power/SimPowerOutlet.java b/opendc-simulator/opendc-simulator-power/src/main/java/org/opendc/simulator/power/SimPowerOutlet.java deleted file mode 100644 index e33d35d0..00000000 --- a/opendc-simulator/opendc-simulator-power/src/main/java/org/opendc/simulator/power/SimPowerOutlet.java +++ /dev/null @@ -1,91 +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.power; - -import org.opendc.simulator.flow2.Inlet; -import org.opendc.simulator.flow2.Outlet; - -/** - * An abstract outlet that provides a source of electricity for datacenter components. - */ -public abstract class SimPowerOutlet { - private SimPowerInlet inlet; - - /** - * Determine whether the outlet is connected to a {@link SimPowerInlet}. - * - * @return true if the outlet is connected to an inlet, false otherwise. - */ - public boolean isConnected() { - return inlet != null; - } - - /** - * Return the {@link SimPowerInlet} to which the outlet is connected. - */ - public SimPowerInlet getInlet() { - return inlet; - } - - /** - * Connect the specified power [inlet] to this outlet. - * - * @param inlet The inlet to connect to the outlet. - */ - public void connect(SimPowerInlet inlet) { - if (isConnected()) { - throw new IllegalStateException("Outlet already connected"); - } - if (inlet.isConnected()) { - throw new IllegalStateException("Inlet already connected"); - } - - this.inlet = inlet; - this.inlet.outlet = this; - - final Inlet flowInlet = getFlowInlet(); - final Outlet flowOutlet = inlet.getFlowOutlet(); - - flowInlet.getGraph().connect(flowOutlet, flowInlet); - } - - /** - * Disconnect the connected power outlet from this inlet - */ - public void disconnect() { - SimPowerInlet inlet = this.inlet; - if (inlet != null) { - this.inlet = null; - assert inlet.outlet == this : "Inlet state incorrect"; - inlet.outlet = null; - - final Inlet flowInlet = getFlowInlet(); - flowInlet.getGraph().disconnect(flowInlet); - } - } - - /** - * Return the flow {@link Inlet} that models the consumption of a power outlet as flow input. - */ - protected abstract Inlet getFlowInlet(); -} diff --git a/opendc-simulator/opendc-simulator-power/src/main/java/org/opendc/simulator/power/SimPowerSource.java b/opendc-simulator/opendc-simulator-power/src/main/java/org/opendc/simulator/power/SimPowerSource.java deleted file mode 100644 index a2d62c48..00000000 --- a/opendc-simulator/opendc-simulator-power/src/main/java/org/opendc/simulator/power/SimPowerSource.java +++ /dev/null @@ -1,71 +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.power; - -import org.opendc.simulator.flow2.FlowGraph; -import org.opendc.simulator.flow2.Inlet; -import org.opendc.simulator.flow2.sink.SimpleFlowSink; - -/** - * A {@link SimPowerOutlet} that represents a source of electricity with a maximum capacity. - */ -public final class SimPowerSource extends SimPowerOutlet { - /** - * The resource source that drives this power source. - */ - private final SimpleFlowSink sink; - - /** - * Construct a {@link SimPowerSource} instance. - * - * @param graph The underlying {@link FlowGraph} to which the power source belongs. - * @param capacity The maximum amount of power provided by the source. - */ - public SimPowerSource(FlowGraph graph, float capacity) { - this.sink = new SimpleFlowSink(graph, capacity); - } - - /** - * Return the capacity of the power source. - */ - public float getCapacity() { - return sink.getCapacity(); - } - - /** - * Return the power draw at this instant. - */ - public float getPowerDraw() { - return sink.getRate(); - } - - @Override - protected Inlet getFlowInlet() { - return sink.getInput(); - } - - @Override - public String toString() { - return "SimPowerSource"; - } -} diff --git a/opendc-simulator/opendc-simulator-power/src/main/java/org/opendc/simulator/power/SimUps.java b/opendc-simulator/opendc-simulator-power/src/main/java/org/opendc/simulator/power/SimUps.java deleted file mode 100644 index df7508d9..00000000 --- a/opendc-simulator/opendc-simulator-power/src/main/java/org/opendc/simulator/power/SimUps.java +++ /dev/null @@ -1,137 +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.power; - -import org.jetbrains.annotations.NotNull; -import org.opendc.simulator.flow2.FlowGraph; -import org.opendc.simulator.flow2.Inlet; -import org.opendc.simulator.flow2.Outlet; -import org.opendc.simulator.flow2.mux.FlowMultiplexer; -import org.opendc.simulator.flow2.mux.MaxMinFlowMultiplexer; -import org.opendc.simulator.flow2.util.FlowTransform; -import org.opendc.simulator.flow2.util.FlowTransformer; - -/** - * A model of an Uninterruptible Power Supply (UPS). - *

- * This model aggregates multiple power sources into a single source in order to ensure that power is always available. - */ -public final class SimUps extends SimPowerOutlet { - /** - * The {@link FlowMultiplexer} that distributes the electricity over the PDU outlets. - */ - private final MaxMinFlowMultiplexer mux; - - /** - * A {@link FlowTransformer} that applies the power loss to the PDU's power inlet. - */ - private final FlowTransformer transformer; - - /** - * Construct a {@link SimUps} instance. - * - * @param graph The underlying {@link FlowGraph} to which the UPS belongs. - * @param idlePower The idle power consumption of the UPS independent of the load. - * @param lossCoefficient The coefficient for the power loss of the UPS proportional to the load. - */ - public SimUps(FlowGraph graph, float idlePower, float lossCoefficient) { - this.mux = new MaxMinFlowMultiplexer(graph); - this.transformer = new FlowTransformer(graph, new FlowTransform() { - @Override - public float apply(float value) { - // See https://download.schneider-electric.com/files?p_Doc_Ref=SPD_NRAN-66CK3D_EN - return value * (lossCoefficient + 1) + idlePower; - } - - @Override - public float applyInverse(float value) { - return (value - idlePower) / (lossCoefficient + 1); - } - }); - - graph.connect(transformer.getOutput(), mux.newInput()); - } - - /** - * Construct a {@link SimUps} instance without any loss. - * - * @param graph The underlying {@link FlowGraph} to which the UPS belongs. - */ - public SimUps(FlowGraph graph) { - this(graph, 0.f, 0.f); - } - - /** - * Create a new UPS inlet. - */ - public PowerInlet newInlet() { - return new PowerInlet(mux); - } - - @Override - protected Inlet getFlowInlet() { - return transformer.getInput(); - } - - @Override - public String toString() { - return "SimUps"; - } - - /** - * A UPS inlet. - */ - public static final class PowerInlet extends SimPowerInlet implements AutoCloseable { - private final FlowMultiplexer mux; - private final Outlet outlet; - private boolean isClosed; - - private PowerInlet(FlowMultiplexer mux) { - this.mux = mux; - this.outlet = mux.newOutput(); - } - - /** - * Remove the inlet from the PDU. - */ - @Override - public void close() { - isClosed = true; - mux.releaseOutput(outlet); - } - - @Override - public String toString() { - return "SimPdu.Inlet"; - } - - @NotNull - @Override - protected Outlet getFlowOutlet() { - if (isClosed) { - throw new IllegalStateException("Inlet is closed"); - } - return outlet; - } - } -} diff --git a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPduTest.kt b/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPduTest.kt deleted file mode 100644 index 9df72c49..00000000 --- a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPduTest.kt +++ /dev/null @@ -1,133 +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.power - -import kotlinx.coroutines.yield -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.opendc.simulator.flow2.FlowEngine -import org.opendc.simulator.kotlin.runSimulation - -/** - * Test suite for the [SimPdu] class. - */ -internal class SimPduTest { - @Test - fun testZeroOutlets() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, 100.0f) - val pdu = SimPdu(graph) - source.connect(pdu) - - yield() - - assertEquals(0.0f, source.powerDraw) - } - - @Test - fun testSingleOutlet() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, 100.0f) - val pdu = SimPdu(graph) - source.connect(pdu) - pdu.newOutlet().connect(TestInlet(graph)) - - yield() - - assertEquals(100.0f, source.powerDraw) - } - - @Test - fun testDoubleOutlet() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, 200.0f) - val pdu = SimPdu(graph) - source.connect(pdu) - - pdu.newOutlet().connect(TestInlet(graph)) - pdu.newOutlet().connect(TestInlet(graph)) - - yield() - - assertEquals(200.0f, source.powerDraw) - } - - @Test - fun testDisconnect() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, 300.0f) - val pdu = SimPdu(graph) - source.connect(pdu) - - val outlet = pdu.newOutlet() - outlet.connect(TestInlet(graph)) - outlet.disconnect() - - yield() - - assertEquals(0.0f, source.powerDraw) - } - - @Test - fun testLoss() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, 500.0f) - // https://download.schneider-electric.com/files?p_Doc_Ref=SPD_NRAN-66CK3D_EN - val pdu = SimPdu(graph, 1.5f, 0.015f) - source.connect(pdu) - pdu.newOutlet().connect(TestInlet(graph)) - - yield() - - assertEquals(251.5f, source.powerDraw, 0.01f) - } - - @Test - fun testOutletClose() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, 100.0f) - val pdu = SimPdu(graph) - source.connect(pdu) - val outlet = pdu.newOutlet() - outlet.close() - - yield() - - assertThrows { - outlet.connect(TestInlet(graph)) - } - } -} diff --git a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPowerSourceTest.kt b/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPowerSourceTest.kt deleted file mode 100644 index bbc9ad92..00000000 --- a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPowerSourceTest.kt +++ /dev/null @@ -1,155 +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.power - -import io.mockk.every -import io.mockk.mockk -import kotlinx.coroutines.yield -import org.junit.jupiter.api.Assertions.assertAll -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertFalse -import org.junit.jupiter.api.Assertions.assertNull -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertDoesNotThrow -import org.junit.jupiter.api.assertThrows -import org.opendc.simulator.flow2.FlowEngine -import org.opendc.simulator.kotlin.runSimulation - -/** - * Test suite for the [SimPowerSource] - */ -internal class SimPowerSourceTest { - @Test - fun testInitialState() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, 100.0f) - - yield() - - assertAll( - { assertFalse(source.isConnected) }, - { assertNull(source.inlet) }, - { assertEquals(100.0f, source.capacity) }, - ) - } - - @Test - fun testDisconnectIdempotent() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, 100.0f) - - assertDoesNotThrow { source.disconnect() } - assertFalse(source.isConnected) - } - - @Test - fun testConnect() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, 100.0f) - val inlet = TestInlet(graph) - - source.connect(inlet) - - yield() - - assertAll( - { assertTrue(source.isConnected) }, - { assertEquals(inlet, source.inlet) }, - { assertTrue(inlet.isConnected) }, - { assertEquals(source, inlet.outlet) }, - { assertEquals(100.0f, source.powerDraw) }, - ) - } - - @Test - fun testDisconnect() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, 100.0f) - val inlet = TestInlet(graph) - - source.connect(inlet) - source.disconnect() - - yield() - - assertEquals(0.0f, inlet.flowOutlet.capacity) - } - - @Test - fun testDisconnectAssertion() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, 100.0f) - - val inlet = mockk(relaxUnitFun = true) - every { inlet.isConnected } returns false - every { inlet.flowOutlet } returns TestInlet(graph).flowOutlet - - source.connect(inlet) - inlet.outlet = null - - assertThrows { - source.disconnect() - } - } - - @Test - fun testOutletAlreadyConnected() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, 100.0f) - val inlet = TestInlet(graph) - - source.connect(inlet) - assertThrows { - source.connect(TestInlet(graph)) - } - - assertEquals(inlet, source.inlet) - } - - @Test - fun testInletAlreadyConnected() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, 100.0f) - val inlet = mockk(relaxUnitFun = true) - every { inlet.isConnected } returns true - - assertThrows { - source.connect(inlet) - } - } -} diff --git a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimUpsTest.kt b/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimUpsTest.kt deleted file mode 100644 index cbd23887..00000000 --- a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimUpsTest.kt +++ /dev/null @@ -1,108 +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.power - -import kotlinx.coroutines.yield -import org.junit.jupiter.api.Assertions.assertAll -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.opendc.simulator.flow2.FlowEngine -import org.opendc.simulator.kotlin.runSimulation - -/** - * Test suite for the [SimUps] class. - */ -internal class SimUpsTest { - @Test - fun testSingleInlet() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, 200.0f) - val ups = SimUps(graph) - source.connect(ups.newInlet()) - ups.connect(TestInlet(graph)) - - yield() - - assertEquals(100.0f, source.powerDraw) - } - - @Test - fun testDoubleInlet() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source1 = SimPowerSource(graph, 200.0f) - val source2 = SimPowerSource(graph, 200.0f) - val ups = SimUps(graph) - source1.connect(ups.newInlet()) - source2.connect(ups.newInlet()) - - ups.connect(TestInlet(graph)) - - yield() - - assertAll( - { assertEquals(50.0f, source1.powerDraw) }, - { assertEquals(50.0f, source2.powerDraw) }, - ) - } - - @Test - fun testLoss() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source = SimPowerSource(graph, 500.0f) - // https://download.schneider-electric.com/files?p_Doc_Ref=SPD_NRAN-66CK3D_EN - val ups = SimUps(graph, 4.0f, 0.05f) - source.connect(ups.newInlet()) - ups.connect(TestInlet(graph)) - - yield() - - assertEquals(109.0f, source.powerDraw, 0.01f) - } - - @Test - fun testDisconnect() = - runSimulation { - val engine = FlowEngine.create(dispatcher) - val graph = engine.newGraph() - val source1 = SimPowerSource(graph, 200.0f) - val source2 = SimPowerSource(graph, 200.0f) - val ups = SimUps(graph) - source1.connect(ups.newInlet()) - source2.connect(ups.newInlet()) - - val inlet = TestInlet(graph) - - ups.connect(inlet) - ups.disconnect() - - yield() - - assertEquals(0.0f, inlet.flowOutlet.capacity) - } -} diff --git a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/TestInlet.kt b/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/TestInlet.kt deleted file mode 100644 index 1c06acf4..00000000 --- a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/TestInlet.kt +++ /dev/null @@ -1,49 +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.power - -import org.opendc.simulator.flow2.FlowGraph -import org.opendc.simulator.flow2.FlowStage -import org.opendc.simulator.flow2.FlowStageLogic -import org.opendc.simulator.flow2.Outlet - -/** - * A test inlet. - */ -class TestInlet(graph: FlowGraph) : SimPowerInlet(), FlowStageLogic { - private val stage = graph.newStage(this) - val flowOutlet = stage.getOutlet("out") - - init { - flowOutlet.push(100.0f) - } - - override fun onUpdate( - ctx: FlowStage, - now: Long, - ): Long = Long.MAX_VALUE - - override fun getFlowOutlet(): Outlet { - return flowOutlet - } -} -- cgit v1.2.3