diff options
| author | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2022-09-01 14:38:34 +0200 |
|---|---|---|
| committer | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2022-10-21 22:13:04 +0200 |
| commit | 44215bd668c5fa3efe2f57fc577824478b00af57 (patch) | |
| tree | b933228e5e5748716351dc9ce031b4840f254428 /opendc-simulator/opendc-simulator-compute/src | |
| parent | c1f67a872e2d7ce63ac96f8ca80cbe8b25c62e3b (diff) | |
refactor(sim/compute): Re-implement using flow2
This change re-implements the OpenDC compute simulator framework using
the new flow2 framework for modelling multi-edge flow networks. The
re-implementation is written in Java and focusses on performance and
clean API surface.
Diffstat (limited to 'opendc-simulator/opendc-simulator-compute/src')
86 files changed, 4810 insertions, 3605 deletions
diff --git a/opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt b/opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt index 220b97cc..ec032070 100644 --- a/opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt +++ b/opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt @@ -29,12 +29,9 @@ import org.opendc.simulator.compute.model.MachineModel import org.opendc.simulator.compute.model.MemoryUnit import org.opendc.simulator.compute.model.ProcessingNode import org.opendc.simulator.compute.model.ProcessingUnit -import org.opendc.simulator.compute.power.ConstantPowerModel -import org.opendc.simulator.compute.power.SimplePowerDriver import org.opendc.simulator.compute.workload.SimTrace -import org.opendc.simulator.compute.workload.SimTraceWorkload -import org.opendc.simulator.flow.FlowEngine -import org.opendc.simulator.flow.mux.FlowMultiplexerFactory +import org.opendc.simulator.flow2.FlowEngine +import org.opendc.simulator.flow2.mux.FlowMultiplexerFactory import org.opendc.simulator.kotlin.runSimulation import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Fork @@ -60,14 +57,14 @@ class SimMachineBenchmarks { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) machineModel = MachineModel( - cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, - memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, + /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } ) val random = ThreadLocalRandom.current() val builder = SimTrace.builder() - repeat(10000) { - val timestamp = it.toLong() + repeat(1000000) { + val timestamp = it.toLong() * 1000 val deadline = timestamp + 1000 builder.add(deadline, random.nextDouble(0.0, 4500.0), 1) } @@ -77,29 +74,27 @@ class SimMachineBenchmarks { @Benchmark fun benchmarkBareMetal() { return runSimulation { - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine( - engine, - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) - ) - return@runSimulation machine.runWorkload(SimTraceWorkload(trace)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + val machine = SimBareMetalMachine.create(graph, machineModel) + return@runSimulation machine.runWorkload(trace.createWorkload(0)) } } @Benchmark fun benchmarkSpaceSharedHypervisor() { return runSimulation { - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(1), null) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(1)) launch { machine.runWorkload(hypervisor) } val vm = hypervisor.newMachine(machineModel) try { - return@runSimulation vm.runWorkload(SimTraceWorkload(trace)) + return@runSimulation vm.runWorkload(trace.createWorkload(0)) } finally { vm.cancel() machine.cancel() @@ -110,16 +105,17 @@ class SimMachineBenchmarks { @Benchmark fun benchmarkFairShareHypervisorSingle() { return runSimulation { - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1), null) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1)) launch { machine.runWorkload(hypervisor) } val vm = hypervisor.newMachine(machineModel) try { - return@runSimulation vm.runWorkload(SimTraceWorkload(trace)) + return@runSimulation vm.runWorkload(trace.createWorkload(0)) } finally { vm.cancel() machine.cancel() @@ -130,9 +126,10 @@ class SimMachineBenchmarks { @Benchmark fun benchmarkFairShareHypervisorDouble() { return runSimulation { - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1), null) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1)) launch { machine.runWorkload(hypervisor) } @@ -142,7 +139,7 @@ class SimMachineBenchmarks { launch { try { - vm.runWorkload(SimTraceWorkload(trace)) + vm.runWorkload(trace.createWorkload(0)) } finally { machine.cancel() } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimAbstractMachine.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimAbstractMachine.java new file mode 100644 index 00000000..cf5aed03 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimAbstractMachine.java @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.opendc.simulator.compute.device.SimNetworkAdapter; +import org.opendc.simulator.compute.model.MachineModel; +import org.opendc.simulator.compute.model.MemoryUnit; +import org.opendc.simulator.compute.workload.SimWorkload; +import org.opendc.simulator.flow2.FlowGraph; +import org.opendc.simulator.flow2.Inlet; +import org.opendc.simulator.flow2.Outlet; +import org.opendc.simulator.flow2.sink.SimpleFlowSink; +import org.opendc.simulator.flow2.util.FlowTransformer; +import org.opendc.simulator.flow2.util.FlowTransforms; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Abstract implementation of the {@link SimMachine} interface. + */ +public abstract class SimAbstractMachine implements SimMachine { + private static final Logger LOGGER = LoggerFactory.getLogger(SimAbstractMachine.class); + private final MachineModel model; + + private Context activeContext; + + /** + * Construct a {@link SimAbstractMachine} instance. + * + * @param model The model of the machine. + */ + public SimAbstractMachine(MachineModel model) { + this.model = model; + } + + @Override + public final MachineModel getModel() { + return model; + } + + @Override + public final SimMachineContext startWorkload(SimWorkload workload, Map<String, Object> meta) { + if (activeContext != null) { + throw new IllegalStateException("A machine cannot run multiple workloads concurrently"); + } + + final Context ctx = createContext(workload, new HashMap<>(meta)); + ctx.start(); + return ctx; + } + + @Override + public final void cancel() { + final Context context = activeContext; + if (context != null) { + context.shutdown(); + } + } + + /** + * Construct a new {@link Context} instance representing the active execution. + * + * @param workload The workload to start on the machine. + * @param meta The metadata to pass to the workload. + */ + protected abstract Context createContext(SimWorkload workload, Map<String, Object> meta); + + /** + * Return the active {@link Context} instance (if any). + */ + protected Context getActiveContext() { + return activeContext; + } + + /** + * The execution context in which the workload runs. + */ + public abstract static class Context implements SimMachineContext { + private final SimAbstractMachine machine; + private final SimWorkload workload; + private final Map<String, Object> meta; + private boolean isClosed; + + /** + * Construct a new {@link Context} instance. + * + * @param machine The {@link SimAbstractMachine} to which the context belongs. + * @param workload The {@link SimWorkload} to which the context belongs. + * @param meta The metadata passed to the context. + */ + public Context(SimAbstractMachine machine, SimWorkload workload, Map<String, Object> meta) { + this.machine = machine; + this.workload = workload; + this.meta = meta; + } + + @Override + public final Map<String, Object> getMeta() { + return meta; + } + + @Override + public final void shutdown() { + if (isClosed) { + return; + } + + isClosed = true; + final SimAbstractMachine machine = this.machine; + assert machine.activeContext == this : "Invariant violation: multiple contexts active for a single machine"; + machine.activeContext = null; + + // Cancel all the resources associated with the machine + doCancel(); + + try { + workload.onStop(this); + } catch (Exception cause) { + LOGGER.warn("Workload failed during onStop callback", cause); + } + } + + /** + * Start this context. + */ + final void start() { + try { + machine.activeContext = this; + workload.onStart(this); + } catch (Exception cause) { + LOGGER.warn("Workload failed during onStart callback", cause); + shutdown(); + } + } + + /** + * Run the stop procedures for the resources associated with the machine. + */ + protected void doCancel() { + final FlowGraph graph = getMemory().getInput().getGraph(); + + for (SimProcessingUnit cpu : getCpus()) { + final Inlet inlet = cpu.getInput(); + graph.disconnect(inlet); + } + + graph.disconnect(getMemory().getInput()); + + for (SimNetworkInterface ifx : getNetworkInterfaces()) { + ((NetworkAdapter) ifx).disconnect(); + } + + for (SimStorageInterface storage : getStorageInterfaces()) { + StorageDevice impl = (StorageDevice) storage; + graph.disconnect(impl.getRead()); + graph.disconnect(impl.getWrite()); + } + } + + @Override + public String toString() { + return "SimAbstractMachine.Context"; + } + } + + /** + * The [SimMemory] implementation for a machine. + */ + public static final class Memory implements SimMemory { + private final SimpleFlowSink sink; + private final List<MemoryUnit> models; + + public Memory(FlowGraph graph, List<MemoryUnit> models) { + long memorySize = 0; + for (MemoryUnit mem : models) { + memorySize += mem.getSize(); + } + + this.sink = new SimpleFlowSink(graph, (float) memorySize); + this.models = models; + } + + @Override + public double getCapacity() { + return sink.getCapacity(); + } + + @Override + public List<MemoryUnit> getModels() { + return models; + } + + @Override + public Inlet getInput() { + return sink.getInput(); + } + + @Override + public String toString() { + return "SimAbstractMachine.Memory"; + } + } + + /** + * A {@link SimNetworkAdapter} implementation for a machine. + */ + public static class NetworkAdapter extends SimNetworkAdapter implements SimNetworkInterface { + private final org.opendc.simulator.compute.model.NetworkAdapter model; + private final FlowTransformer tx; + private final FlowTransformer rx; + private final String name; + + /** + * Construct a {@link NetworkAdapter}. + */ + public NetworkAdapter(FlowGraph graph, org.opendc.simulator.compute.model.NetworkAdapter model, int index) { + this.model = model; + this.tx = new FlowTransformer(graph, FlowTransforms.noop()); + this.rx = new FlowTransformer(graph, FlowTransforms.noop()); + this.name = "eth" + index; + } + + @Override + public String getName() { + return name; + } + + @Override + public Inlet getTx() { + return tx.getInput(); + } + + @Override + public Outlet getRx() { + return rx.getOutput(); + } + + @Override + public double getBandwidth() { + return model.getBandwidth(); + } + + @Override + protected Outlet getOutlet() { + return tx.getOutput(); + } + + @Override + protected Inlet getInlet() { + return rx.getInput(); + } + + @Override + public String toString() { + return "SimAbstractMachine.NetworkAdapterImpl[name=" + name + ", bandwidth=" + model.getBandwidth() + + "Mbps]"; + } + } + + /** + * A {@link SimStorageInterface} implementation for a machine. + */ + public static class StorageDevice implements SimStorageInterface { + private final org.opendc.simulator.compute.model.StorageDevice model; + private final SimpleFlowSink read; + private final SimpleFlowSink write; + private final String name; + + /** + * Construct a {@link StorageDevice}. + */ + public StorageDevice(FlowGraph graph, org.opendc.simulator.compute.model.StorageDevice model, int index) { + this.model = model; + this.read = new SimpleFlowSink(graph, (float) model.getReadBandwidth()); + this.write = new SimpleFlowSink(graph, (float) model.getWriteBandwidth()); + this.name = "disk" + index; + } + + @Override + public String getName() { + return name; + } + + @Override + public Inlet getRead() { + return read.getInput(); + } + + @Override + public Inlet getWrite() { + return write.getInput(); + } + + @Override + public double getCapacity() { + return model.getCapacity(); + } + + @Override + public String toString() { + return "SimAbstractMachine.StorageDeviceImpl[name=" + name + ", capacity=" + model.getCapacity() + "MB]"; + } + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimBareMetalMachine.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimBareMetalMachine.java new file mode 100644 index 00000000..aa7502d6 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimBareMetalMachine.java @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.opendc.simulator.compute.device.SimPeripheral; +import org.opendc.simulator.compute.model.MachineModel; +import org.opendc.simulator.compute.model.ProcessingUnit; +import org.opendc.simulator.compute.workload.SimWorkload; +import org.opendc.simulator.flow2.FlowGraph; +import org.opendc.simulator.flow2.InPort; +import org.opendc.simulator.flow2.Inlet; + +/** + * A simulated bare-metal machine that is able to run a single workload. + * + * <p> + * A {@link SimBareMetalMachine} is a stateful object, and you should be careful when operating this object concurrently. For + * example, the class expects only a single concurrent call to {@link #startWorkload(SimWorkload, Map)}. + */ +public final class SimBareMetalMachine extends SimAbstractMachine { + /** + * The {@link FlowGraph} in which the simulation takes places. + */ + private final FlowGraph graph; + + /** + * The {@link SimPsu} of this bare metal machine. + */ + private final SimPsu psu; + + /** + * The resources of this machine. + */ + private final List<Cpu> cpus; + + private final Memory memory; + private final List<NetworkAdapter> net; + private final List<StorageDevice> disk; + + /** + * Construct a {@link SimBareMetalMachine} instance. + * + * @param graph The {@link FlowGraph} to which the machine belongs. + * @param model The machine model to simulate. + * @param psuFactory The {@link SimPsuFactory} to construct the power supply of the machine. + */ + private SimBareMetalMachine(FlowGraph graph, MachineModel model, SimPsuFactory psuFactory) { + super(model); + + this.graph = graph; + this.psu = psuFactory.newPsu(this, graph); + + int cpuIndex = 0; + final ArrayList<Cpu> cpus = new ArrayList<>(); + this.cpus = cpus; + for (ProcessingUnit cpu : model.getCpus()) { + cpus.add(new Cpu(psu, cpu, cpuIndex++)); + } + + this.memory = new Memory(graph, model.getMemory()); + + int netIndex = 0; + final ArrayList<NetworkAdapter> 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<StorageDevice> 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<? extends SimPeripheral> getPeripherals() { + return Collections.unmodifiableList(net); + } + + /** + * Return the CPU capacity of the machine in MHz. + */ + public double getCpuCapacity() { + final Context context = (Context) getActiveContext(); + + if (context == null) { + return 0.0; + } + + float capacity = 0.f; + + for (SimProcessingUnit cpu : context.cpus) { + capacity += cpu.getFrequency(); + } + + return capacity; + } + + /** + * The CPU demand of the machine in MHz. + */ + public double getCpuDemand() { + final Context context = (Context) getActiveContext(); + + if (context == null) { + return 0.0; + } + + float demand = 0.f; + + for (SimProcessingUnit cpu : context.cpus) { + demand += cpu.getDemand(); + } + + return demand; + } + + /** + * The CPU usage of the machine in MHz. + */ + public double getCpuUsage() { + final Context context = (Context) getActiveContext(); + + if (context == null) { + return 0.0; + } + + float rate = 0.f; + + for (SimProcessingUnit cpu : context.cpus) { + rate += cpu.getSpeed(); + } + + return rate; + } + + @Override + protected SimAbstractMachine.Context createContext(SimWorkload workload, Map<String, Object> meta) { + return new Context(this, workload, meta); + } + + /** + * The execution context for a {@link SimBareMetalMachine}. + */ + private static final class Context extends SimAbstractMachine.Context { + private final FlowGraph graph; + private final List<Cpu> cpus; + private final Memory memory; + private final List<NetworkAdapter> net; + private final List<StorageDevice> disk; + + private Context(SimBareMetalMachine machine, SimWorkload workload, Map<String, Object> meta) { + super(machine, workload, meta); + + this.graph = machine.graph; + this.cpus = machine.cpus; + this.memory = machine.memory; + this.net = machine.net; + this.disk = machine.disk; + } + + @Override + public FlowGraph getGraph() { + return graph; + } + + @Override + public List<? extends SimProcessingUnit> getCpus() { + return cpus; + } + + @Override + public SimMemory getMemory() { + return memory; + } + + @Override + public List<? extends SimNetworkInterface> getNetworkInterfaces() { + return net; + } + + @Override + public List<? extends SimStorageInterface> getStorageInterfaces() { + return disk; + } + } + + /** + * A {@link SimProcessingUnit} of a bare-metal machine. + */ + private static final class Cpu implements SimProcessingUnit { + private final SimPsu psu; + private final ProcessingUnit model; + private final InPort port; + + private Cpu(SimPsu psu, ProcessingUnit model, int id) { + this.psu = psu; + this.model = model; + this.port = psu.getCpuPower(id, model); + + this.port.pull((float) model.getFrequency()); + } + + @Override + public double getFrequency() { + return port.getCapacity(); + } + + @Override + public void setFrequency(double frequency) { + // Clamp the capacity of the CPU between [0.0, maxFreq] + frequency = Math.max(0, Math.min(model.getFrequency(), frequency)); + psu.setCpuFrequency(port, frequency); + } + + @Override + public double getDemand() { + return port.getDemand(); + } + + @Override + public double getSpeed() { + return port.getRate(); + } + + @Override + public ProcessingUnit getModel() { + return model; + } + + @Override + public Inlet getInput() { + return port; + } + + @Override + public String toString() { + return "SimBareMetalMachine.Cpu[model=" + model + "]"; + } + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachine.kt b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachine.java index 94581e89..59599875 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachine.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachine.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 AtLarge Research + * 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 @@ -20,40 +20,40 @@ * SOFTWARE. */ -package org.opendc.simulator.compute +package org.opendc.simulator.compute; -import org.opendc.simulator.compute.device.SimPeripheral -import org.opendc.simulator.compute.model.MachineModel -import org.opendc.simulator.compute.workload.SimWorkload +import java.util.List; +import java.util.Map; +import org.opendc.simulator.compute.device.SimPeripheral; +import org.opendc.simulator.compute.model.MachineModel; +import org.opendc.simulator.compute.workload.SimWorkload; /** - * A generic machine that is able to run a [SimWorkload]. + * A generic machine that is able to execute {@link SimWorkload} objects. */ public interface SimMachine { /** - * The model of the machine containing its specifications. + * Return the model of the machine containing its specifications. */ - public val model: MachineModel + MachineModel getModel(); /** - * The peripherals attached to the machine. + * Return the peripherals attached to the machine. */ - public val peripherals: List<SimPeripheral> + List<? extends SimPeripheral> getPeripherals(); /** - * Start the specified [SimWorkload] on this machine. + * Start the specified {@link SimWorkload} on this machine. * * @param workload The workload to start on the machine. * @param meta The metadata to pass to the workload. - * @return A [SimMachineContext] that represents the execution context for 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. */ - public fun startWorkload(workload: SimWorkload, meta: Map<String, Any> = emptyMap()): SimMachineContext + SimMachineContext startWorkload(SimWorkload workload, Map<String, Object> meta); /** - * Cancel the workload that is currently running on this machine. - * - * If no workload is active, this operation is a no-op. + * Cancel the active workload on this machine (if any). */ - public fun cancel() + void cancel(); } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachineContext.kt b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachineContext.java index 5e3a7766..f6a3bd38 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMachineContext.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachineContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 AtLarge Research + * 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 @@ -20,41 +20,55 @@ * SOFTWARE. */ -package org.opendc.simulator.compute +package org.opendc.simulator.compute; + +import java.util.List; +import java.util.Map; +import org.opendc.simulator.compute.workload.SimWorkload; +import org.opendc.simulator.flow2.FlowGraph; /** - * A simulated execution context in which a bootable image runs. This interface represents the - * firmware interface between the running image (e.g. operating system) and the physical or virtual firmware on - * which the image runs. + * A simulated execution context in which a bootable image runs. + * + * <p> + * 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 : AutoCloseable { +public interface SimMachineContext { + /** + * Return the {@link FlowGraph} in which the workload executes. + */ + FlowGraph getGraph(); + /** - * The metadata associated with the context. + * Return the metadata associated with the context. + * <p> + * Users can pass this metadata to the workload via {@link SimMachine#startWorkload(SimWorkload, Map)}. */ - public val meta: Map<String, Any> + Map<String, Object> getMeta(); /** - * The CPUs available on the machine. + * Return the CPUs available on the machine. */ - public val cpus: List<SimProcessingUnit> + List<? extends SimProcessingUnit> getCpus(); /** - * The memory interface of the machine. + * Return the memory interface of the machine. */ - public val memory: SimMemory + SimMemory getMemory(); /** - * The network interfaces available to the workload. + * Return the network interfaces available to the workload. */ - public val net: List<SimNetworkInterface> + List<? extends SimNetworkInterface> getNetworkInterfaces(); /** - * The storage devices available to the workload. + * Return the storage devices available to the workload. */ - public val storage: List<SimStorageInterface> + List<? extends SimStorageInterface> getStorageInterfaces(); /** - * Stop the workload. + * Shutdown the workload. */ - public override fun close() + void shutdown(); } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMemory.kt b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMemory.java index b1aef495..4fcc64ab 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimMemory.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMemory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 AtLarge Research + * 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 @@ -20,17 +20,23 @@ * SOFTWARE. */ -package org.opendc.simulator.compute +package org.opendc.simulator.compute; -import org.opendc.simulator.compute.model.MemoryUnit -import org.opendc.simulator.flow.FlowConsumer +import java.util.List; +import org.opendc.simulator.compute.model.MemoryUnit; +import org.opendc.simulator.flow2.sink.FlowSink; /** * An interface to control the memory usage of simulated workloads. */ -public interface SimMemory : FlowConsumer { +public interface SimMemory extends FlowSink { /** - * The models representing the static information of the memory units supporting this interface. + * Return the total capacity of the memory (in MBs). */ - public val models: List<MemoryUnit> + double getCapacity(); + + /** + * Return the models representing the static information of the memory units supporting this interface. + */ + List<MemoryUnit> getModels(); } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimNetworkInterface.kt b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimNetworkInterface.java index 660b2871..4b623e59 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimNetworkInterface.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimNetworkInterface.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 AtLarge Research + * 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 @@ -20,32 +20,32 @@ * SOFTWARE. */ -package org.opendc.simulator.compute +package org.opendc.simulator.compute; -import org.opendc.simulator.flow.FlowConsumer -import org.opendc.simulator.flow.FlowSource +import org.opendc.simulator.flow2.Inlet; +import org.opendc.simulator.flow2.Outlet; /** * A firmware interface to a network adapter. */ public interface SimNetworkInterface { /** - * The name of the network interface. + * Return the name of the network interface. */ - public val name: String + String getName(); /** - * The unidirectional bandwidth of the network interface in Mbps. + * Return the unidirectional bandwidth of the network interface in Mbps. */ - public val bandwidth: Double + double getBandwidth(); /** - * The resource provider for the transmit channel of the network interface. + * Return the inlet for the "transmit" channel of the network interface. */ - public val tx: FlowConsumer + Inlet getTx(); /** - * The resource consumer for the receive channel of the network interface. + * Return the outlet for the "receive" channel of the network interface. */ - public val rx: FlowSource + Outlet getRx(); } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimProcessingUnit.kt b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimProcessingUnit.java index c9f36ece..3dbd3656 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimProcessingUnit.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimProcessingUnit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 AtLarge Research + * 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 @@ -20,22 +20,43 @@ * SOFTWARE. */ -package org.opendc.simulator.compute +package org.opendc.simulator.compute; -import org.opendc.simulator.compute.model.ProcessingUnit -import org.opendc.simulator.flow.FlowConsumer +import org.opendc.simulator.compute.model.ProcessingUnit; +import org.opendc.simulator.flow2.sink.FlowSink; /** * A simulated processing unit. */ -public interface SimProcessingUnit : FlowConsumer { +public interface SimProcessingUnit extends FlowSink { /** - * The capacity of the processing unit, which can be adjusted by the workload if supported by the machine. + * Return the base clock frequency of the processing unit (in MHz). */ - public override var capacity: Double + double getFrequency(); + + /** + * Adjust the base clock frequency of the processing unit. + * + * <p> + * 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. */ - public val model: ProcessingUnit + ProcessingUnit getModel(); } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsu.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsu.java new file mode 100644 index 00000000..7f1f97a0 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsu.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute; + +import org.opendc.simulator.compute.model.ProcessingUnit; +import org.opendc.simulator.flow2.InPort; +import org.opendc.simulator.power.SimPowerInlet; + +/** + * A power supply unit in a {@link SimBareMetalMachine}. + * + * <p> + * 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. + * <p> + * This method provides access to the power consumption of the machine before PSU losses are applied. + */ + public abstract double getPowerDemand(); + + /** + * Return the instantaneous power usage of the machine (in W) measured at the inlet of the power supply. + */ + public abstract double getPowerUsage(); + + /** + * Return the cumulated energy usage of the machine (in J) measured at the inlet of the powers supply. + */ + public abstract double getEnergyUsage(); + + /** + * Return an {@link InPort} that converts processing demand (in MHz) into energy demand (J) for the specified CPU + * <code>model</code>. + * + * @param id The unique identifier of the CPU for this machine. + * @param model The details of the processing unit. + */ + abstract InPort getCpuPower(int id, ProcessingUnit model); + + /** + * This method is invoked when the CPU frequency is changed for the specified <code>port</code>. + * + * @param port The {@link InPort} for which the capacity is changed. + * @param capacity The capacity to change to. + */ + void setCpuFrequency(InPort port, double capacity) { + port.pull((float) capacity); + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactories.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactories.java new file mode 100644 index 00000000..52d04052 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactories.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute; + +import java.time.Clock; +import org.jetbrains.annotations.NotNull; +import org.opendc.simulator.compute.model.ProcessingUnit; +import org.opendc.simulator.compute.power.CpuPowerModel; +import org.opendc.simulator.flow2.FlowGraph; +import org.opendc.simulator.flow2.FlowStage; +import org.opendc.simulator.flow2.FlowStageLogic; +import org.opendc.simulator.flow2.InHandler; +import org.opendc.simulator.flow2.InPort; +import org.opendc.simulator.flow2.OutPort; +import org.opendc.simulator.flow2.Outlet; + +/** + * A collection {@link SimPsu} implementations. + */ +public class SimPsuFactories { + private SimPsuFactories() {} + + /** + * Return a {@link SimPsuFactory} of {@link SimPsu} implementations that do not measure any power consumption. + * + * <p> + * This implementation has the lowest performance impact and users are advised to use this factory if they do not + * consider power consumption in their experiments. + */ + public static SimPsuFactory noop() { + return NoopPsu.FACTORY; + } + + /** + * Return a {@link SimPsuFactory} of {@link SimPsu} implementations that use a {@link CpuPowerModel} to estimate the + * power consumption of a machine based on its CPU utilization. + * + * @param model The power model to estimate the power consumption based on the CPU usage. + */ + public static SimPsuFactory simple(CpuPowerModel model) { + return (machine, graph) -> new SimplePsu(graph, model); + } + + /** + * A {@link SimPsu} implementation that does not attempt to measure power consumption. + */ + private static final class NoopPsu extends SimPsu implements FlowStageLogic { + private static final SimPsuFactory FACTORY = (machine, graph) -> new NoopPsu(graph); + + private final FlowStage stage; + private final OutPort out; + + NoopPsu(FlowGraph graph) { + stage = graph.newStage(this); + out = stage.getOutlet("out"); + out.setMask(true); + } + + @Override + public double getPowerDemand() { + return 0; + } + + @Override + public double getPowerUsage() { + return 0; + } + + @Override + public double getEnergyUsage() { + return 0; + } + + @Override + InPort getCpuPower(int id, ProcessingUnit model) { + final InPort port = stage.getInlet("cpu" + id); + port.setMask(true); + return port; + } + + @Override + public long onUpdate(FlowStage ctx, long now) { + return Long.MAX_VALUE; + } + + @NotNull + @Override + public Outlet getFlowOutlet() { + return out; + } + } + + /** + * A {@link SimPsu} implementation that estimates the power consumption based on CPU usage. + */ + private static final class SimplePsu extends SimPsu implements FlowStageLogic { + private final FlowStage stage; + private final OutPort out; + private final CpuPowerModel model; + private final Clock clock; + + private double targetFreq; + private double totalUsage; + private long lastUpdate; + + private double powerUsage; + private double energyUsage; + + private final InHandler handler = new InHandler() { + @Override + public void onPush(InPort port, float demand) { + totalUsage += -port.getDemand() + demand; + } + + @Override + public void onUpstreamFinish(InPort port, Throwable cause) { + totalUsage -= port.getDemand(); + } + }; + + SimplePsu(FlowGraph graph, CpuPowerModel model) { + this.stage = graph.newStage(this); + this.model = model; + this.clock = graph.getEngine().getClock(); + this.out = stage.getOutlet("out"); + this.out.setMask(true); + + lastUpdate = graph.getEngine().getClock().millis(); + } + + @Override + public double getPowerDemand() { + return totalUsage; + } + + @Override + public double getPowerUsage() { + return powerUsage; + } + + @Override + public double getEnergyUsage() { + updateEnergyUsage(clock.millis()); + return energyUsage; + } + + @Override + InPort getCpuPower(int id, ProcessingUnit model) { + targetFreq += model.getFrequency(); + + final InPort port = stage.getInlet("cpu" + id); + port.setHandler(handler); + return port; + } + + @Override + void setCpuFrequency(InPort port, double capacity) { + targetFreq += -port.getCapacity() + capacity; + + super.setCpuFrequency(port, capacity); + } + + @Override + public long onUpdate(FlowStage ctx, long now) { + updateEnergyUsage(now); + + double usage = model.computePower(totalUsage / targetFreq); + out.push((float) usage); + powerUsage = usage; + + return Long.MAX_VALUE; + } + + @NotNull + @Override + public Outlet getFlowOutlet() { + return out; + } + + /** + * Calculate the energy usage up until <code>now</code>. + */ + private void updateEnergyUsage(long now) { + long lastUpdate = this.lastUpdate; + this.lastUpdate = now; + + long duration = now - lastUpdate; + if (duration > 0) { + // Compute the energy usage of the machine + energyUsage += powerUsage * duration * 0.001; + } + } + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactory.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactory.java new file mode 100644 index 00000000..872e7016 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactory.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute; + +import org.opendc.simulator.flow2.FlowGraph; + +/** + * A factory interface for {@link SimPsu} implementations. + */ +public interface SimPsuFactory { + /** + * Construct a new {@link SimPsu} for the specified <code>machine</code>. + * + * @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/kotlin/org/opendc/simulator/compute/SimStorageInterface.kt b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimStorageInterface.java index 3d648671..341122dc 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimStorageInterface.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimStorageInterface.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 AtLarge Research + * 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 @@ -20,31 +20,31 @@ * SOFTWARE. */ -package org.opendc.simulator.compute +package org.opendc.simulator.compute; -import org.opendc.simulator.flow.FlowConsumer +import org.opendc.simulator.flow2.Inlet; /** * A firmware interface to a storage device. */ public interface SimStorageInterface { /** - * The name of the storage device. + * Return the name of the network interface. */ - public val name: String + String getName(); /** - * The capacity of the storage device in MBs. + * Return the capacity of the storage device in MBs. */ - public val capacity: Double + double getCapacity(); /** - * The resource provider for the read operations of the storage device. + * Return the inlet for the read operations of the storage device. */ - public val read: FlowConsumer + Inlet getRead(); /** - * The resource consumer for the write operation of the storage device. + * Return the inlet for the write operation of the storage device. */ - public val write: FlowConsumer + Inlet getWrite(); } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimNetworkAdapter.kt b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimNetworkAdapter.java index dfb4ecf3..1c16ceff 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimNetworkAdapter.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimNetworkAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 AtLarge Research + * 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 @@ -20,17 +20,17 @@ * SOFTWARE. */ -package org.opendc.simulator.compute.device +package org.opendc.simulator.compute.device; -import org.opendc.simulator.compute.SimMachine -import org.opendc.simulator.network.SimNetworkPort +import org.opendc.simulator.compute.SimMachine; +import org.opendc.simulator.network.SimNetworkPort; /** - * A simulated network interface card (NIC or network adapter) that can be attached to a [SimMachine]. + * A simulated network interface card (NIC or network adapter) that can be attached to a {@link SimMachine}. */ -public abstract class SimNetworkAdapter : SimNetworkPort(), SimPeripheral { +public abstract class SimNetworkAdapter extends SimNetworkPort implements SimPeripheral { /** - * The unidirectional bandwidth of the network adapter in Mbps. + * Return the unidirectional bandwidth of the network adapter (in Mbps). */ - public abstract val bandwidth: Double + public abstract double getBandwidth(); } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimPeripheral.kt b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimPeripheral.java index 268271be..40bd268b 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimPeripheral.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimPeripheral.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 AtLarge Research + * 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 @@ -20,14 +20,14 @@ * SOFTWARE. */ -package org.opendc.simulator.compute.device +package org.opendc.simulator.compute.device; -import org.opendc.simulator.compute.SimMachine +import org.opendc.simulator.compute.SimMachine; /** - * A component that can be attached to a [SimMachine]. - * + * A component that can be attached to a {@link SimMachine}. + * <p> * This interface represents the physical view of the peripheral and should be used to configure the physical properties * of the peripheral. */ -public interface SimPeripheral +public interface SimPeripheral {} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisor.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisor.java new file mode 100644 index 00000000..6e295837 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisor.java @@ -0,0 +1,911 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.kernel; + +import java.time.Clock; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.SplittableRandom; +import java.util.stream.Collectors; +import org.opendc.simulator.compute.SimAbstractMachine; +import org.opendc.simulator.compute.SimMachine; +import org.opendc.simulator.compute.SimMachineContext; +import org.opendc.simulator.compute.SimMemory; +import org.opendc.simulator.compute.SimNetworkInterface; +import org.opendc.simulator.compute.SimProcessingUnit; +import org.opendc.simulator.compute.SimStorageInterface; +import org.opendc.simulator.compute.device.SimPeripheral; +import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernor; +import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernorFactory; +import org.opendc.simulator.compute.kernel.cpufreq.ScalingPolicy; +import org.opendc.simulator.compute.kernel.interference.VmInterferenceDomain; +import org.opendc.simulator.compute.kernel.interference.VmInterferenceMember; +import org.opendc.simulator.compute.kernel.interference.VmInterferenceProfile; +import org.opendc.simulator.compute.model.MachineModel; +import org.opendc.simulator.compute.model.ProcessingUnit; +import org.opendc.simulator.compute.workload.SimWorkload; +import org.opendc.simulator.flow2.FlowGraph; +import org.opendc.simulator.flow2.FlowStage; +import org.opendc.simulator.flow2.FlowStageLogic; +import org.opendc.simulator.flow2.InHandler; +import org.opendc.simulator.flow2.InPort; +import org.opendc.simulator.flow2.Inlet; +import org.opendc.simulator.flow2.OutHandler; +import org.opendc.simulator.flow2.OutPort; +import org.opendc.simulator.flow2.mux.FlowMultiplexer; +import org.opendc.simulator.flow2.mux.FlowMultiplexerFactory; + +/** + * A SimHypervisor facilitates the execution of multiple concurrent {@link SimWorkload}s, while acting as a single + * workload to another {@link SimMachine}. + */ +public final class SimHypervisor implements SimWorkload { + private final FlowMultiplexerFactory muxFactory; + private final SplittableRandom random; + private final ScalingGovernorFactory scalingGovernorFactory; + private final VmInterferenceDomain interferenceDomain; + + private Context activeContext; + private final ArrayList<VirtualMachine> vms = new ArrayList<>(); + private final HvCounters counters = new HvCounters(); + + /** + * Construct a {@link SimHypervisor} instance. + * + * @param muxFactory The factory for the {@link FlowMultiplexer} to multiplex the workloads. + * @param random A randomness generator for the interference calculations. + * @param scalingGovernorFactory The factory for the scaling governor to use for scaling the CPU frequency. + * @param interferenceDomain The interference domain to which the hypervisor belongs. + */ + private SimHypervisor( + FlowMultiplexerFactory muxFactory, + SplittableRandom random, + ScalingGovernorFactory scalingGovernorFactory, + VmInterferenceDomain interferenceDomain) { + this.muxFactory = muxFactory; + this.random = random; + this.scalingGovernorFactory = scalingGovernorFactory; + this.interferenceDomain = interferenceDomain; + } + + /** + * Create a {@link SimHypervisor} instance. + * + * @param muxFactory The factory for the {@link FlowMultiplexer} to multiplex the workloads. + * @param random A randomness generator for the interference calculations. + * @param scalingGovernorFactory The factory for the scaling governor to use for scaling the CPU frequency. + * @param interferenceDomain The interference domain to which the hypervisor belongs. + */ + public static SimHypervisor create( + FlowMultiplexerFactory muxFactory, + SplittableRandom random, + ScalingGovernorFactory scalingGovernorFactory, + VmInterferenceDomain interferenceDomain) { + return new SimHypervisor(muxFactory, random, scalingGovernorFactory, interferenceDomain); + } + + /** + * Create a {@link SimHypervisor} instance with a default interference domain. + * + * @param muxFactory The factory for the {@link FlowMultiplexer} to multiplex the workloads. + * @param random A randomness generator for the interference calculations. + * @param scalingGovernorFactory The factory for the scaling governor to use for scaling the CPU frequency. + */ + public static SimHypervisor create( + FlowMultiplexerFactory muxFactory, SplittableRandom random, ScalingGovernorFactory scalingGovernorFactory) { + return create(muxFactory, random, scalingGovernorFactory, new VmInterferenceDomain()); + } + + /** + * Create a {@link SimHypervisor} instance with a default interference domain and scaling governor. + * + * @param muxFactory The factory for the {@link FlowMultiplexer} to multiplex the workloads. + * @param random A randomness generator for the interference calculations. + */ + public static SimHypervisor create(FlowMultiplexerFactory muxFactory, SplittableRandom random) { + return create(muxFactory, random, null); + } + + /** + * Return the performance counters of the hypervisor. + */ + public SimHypervisorCounters getCounters() { + return counters; + } + + /** + * Return the virtual machines running on this hypervisor. + */ + public List<? extends SimVirtualMachine> getVirtualMachines() { + return Collections.unmodifiableList(vms); + } + + /** + * Create a {@link SimVirtualMachine} instance on which users may run a [SimWorkload]. + * + * @param model The machine to create. + */ + public SimVirtualMachine newMachine(MachineModel model) { + if (!canFit(model)) { + throw new IllegalArgumentException("Machine does not fit"); + } + + VirtualMachine vm = new VirtualMachine(model); + vms.add(vm); + return vm; + } + + /** + * Remove the specified <code>machine</code> from the hypervisor. + * + * @param machine The machine to remove. + */ + public void removeMachine(SimVirtualMachine machine) { + if (vms.remove(machine)) { + // This cast must always succeed, since `_vms` only contains `VirtualMachine` types. + ((VirtualMachine) machine).close(); + } + } + + /** + * Return the CPU capacity of the hypervisor in MHz. + */ + public double getCpuCapacity() { + final Context context = activeContext; + + if (context == null) { + return 0.0; + } + + return context.previousCapacity; + } + + /** + * The CPU demand of the hypervisor in MHz. + */ + public double getCpuDemand() { + final Context context = activeContext; + + if (context == null) { + return 0.0; + } + + return context.previousDemand; + } + + /** + * The CPU usage of the hypervisor in MHz. + */ + public double getCpuUsage() { + final Context context = activeContext; + + if (context == null) { + return 0.0; + } + + return context.previousRate; + } + + /** + * Determine whether the specified machine characterized by <code>model</code> can fit on this hypervisor at this + * moment. + */ + public boolean canFit(MachineModel model) { + final Context context = activeContext; + if (context == null) { + return false; + } + + final FlowMultiplexer multiplexer = context.multiplexer; + return (multiplexer.getMaxInputs() - multiplexer.getInputCount()) + >= model.getCpus().size(); + } + + @Override + public void onStart(SimMachineContext ctx) { + final Context context = new Context(ctx, muxFactory, scalingGovernorFactory, counters); + context.start(); + activeContext = context; + } + + @Override + public void onStop(SimMachineContext ctx) { + final Context context = activeContext; + if (context != null) { + activeContext = null; + context.stop(); + } + } + + /** + * The context which carries the state when the hypervisor is running on a machine. + */ + private static final class Context implements FlowStageLogic { + private final SimMachineContext ctx; + private final FlowMultiplexer multiplexer; + private final FlowStage stage; + private final List<ScalingGovernor> scalingGovernors; + private final Clock clock; + private final HvCounters counters; + + private long lastCounterUpdate; + private final double d; + private float previousDemand; + private float previousRate; + private float previousCapacity; + + private Context( + SimMachineContext ctx, + FlowMultiplexerFactory muxFactory, + ScalingGovernorFactory scalingGovernorFactory, + HvCounters counters) { + + this.ctx = ctx; + this.counters = counters; + + final FlowGraph graph = ctx.getGraph(); + this.multiplexer = muxFactory.newMultiplexer(graph); + this.stage = graph.newStage(this); + this.clock = graph.getEngine().getClock(); + + this.lastCounterUpdate = clock.millis(); + + if (scalingGovernorFactory != null) { + this.scalingGovernors = ctx.getCpus().stream() + .map(cpu -> scalingGovernorFactory.newGovernor(new ScalingPolicyImpl(cpu))) + .collect(Collectors.toList()); + } else { + this.scalingGovernors = Collections.emptyList(); + } + + float cpuCapacity = 0.f; + final List<? extends SimProcessingUnit> cpus = ctx.getCpus(); + for (SimProcessingUnit cpu : cpus) { + cpuCapacity += cpu.getFrequency(); + } + this.d = cpus.size() / cpuCapacity; + } + + /** + * Start the hypervisor on a new machine. + */ + void start() { + final FlowGraph graph = ctx.getGraph(); + final FlowMultiplexer multiplexer = this.multiplexer; + + for (SimProcessingUnit cpu : ctx.getCpus()) { + graph.connect(multiplexer.newOutput(), cpu.getInput()); + } + + for (ScalingGovernor governor : scalingGovernors) { + governor.onStart(); + } + } + + /** + * Stop the hypervisor. + */ + void stop() { + // Synchronize the counters before stopping the hypervisor. Otherwise, the last report is missed. + updateCounters(clock.millis()); + + stage.close(); + } + + /** + * Invalidate the {@link FlowStage} of the hypervisor. + */ + void invalidate() { + stage.invalidate(); + } + + /** + * Update the performance counters of the hypervisor. + * + * @param now The timestamp at which to update the counter. + */ + void updateCounters(long now) { + long lastUpdate = this.lastCounterUpdate; + this.lastCounterUpdate = now; + long delta = now - lastUpdate; + + if (delta > 0) { + final HvCounters counters = this.counters; + + float demand = previousDemand; + float rate = previousRate; + float capacity = previousCapacity; + + final double factor = this.d * delta; + + counters.cpuActiveTime += Math.round(rate * factor); + counters.cpuIdleTime += Math.round((capacity - rate) * factor); + counters.cpuStealTime += Math.round((demand - rate) * factor); + } + } + + /** + * Update the performance counters of the hypervisor. + */ + void updateCounters() { + updateCounters(clock.millis()); + } + + @Override + public long onUpdate(FlowStage ctx, long now) { + updateCounters(now); + + final FlowMultiplexer multiplexer = this.multiplexer; + final List<ScalingGovernor> scalingGovernors = this.scalingGovernors; + + float demand = multiplexer.getDemand(); + float rate = multiplexer.getRate(); + float capacity = multiplexer.getCapacity(); + + this.previousDemand = demand; + this.previousRate = rate; + this.previousCapacity = capacity; + + double load = rate / Math.min(1.0, capacity); + + if (!scalingGovernors.isEmpty()) { + for (ScalingGovernor governor : scalingGovernors) { + governor.onLimit(load); + } + } + + return Long.MAX_VALUE; + } + } + + /** + * A {@link ScalingPolicy} for a physical CPU of the hypervisor. + */ + private static final class ScalingPolicyImpl implements ScalingPolicy { + private final SimProcessingUnit cpu; + + private ScalingPolicyImpl(SimProcessingUnit cpu) { + this.cpu = cpu; + } + + @Override + public SimProcessingUnit getCpu() { + return cpu; + } + + @Override + public double getTarget() { + return cpu.getFrequency(); + } + + @Override + public void setTarget(double target) { + cpu.setFrequency(target); + } + + @Override + public double getMin() { + return 0; + } + + @Override + public double getMax() { + return cpu.getModel().getFrequency(); + } + } + + /** + * A virtual machine running on the hypervisor. + */ + private class VirtualMachine extends SimAbstractMachine implements SimVirtualMachine { + private boolean isClosed; + private final VmCounters counters = new VmCounters(this); + + private VirtualMachine(MachineModel model) { + super(model); + } + + @Override + public SimHypervisorCounters getCounters() { + return counters; + } + + @Override + public double getCpuDemand() { + final VmContext context = (VmContext) getActiveContext(); + + if (context == null) { + return 0.0; + } + + return context.previousDemand; + } + + @Override + public double getCpuUsage() { + final VmContext context = (VmContext) getActiveContext(); + + if (context == null) { + return 0.0; + } + + return context.usage; + } + + @Override + public double getCpuCapacity() { + final VmContext context = (VmContext) getActiveContext(); + + if (context == null) { + return 0.0; + } + + return context.previousCapacity; + } + + @Override + public List<? extends SimPeripheral> getPeripherals() { + return Collections.emptyList(); + } + + @Override + protected Context createContext(SimWorkload workload, Map<String, Object> meta) { + if (isClosed) { + throw new IllegalStateException("Virtual machine does not exist anymore"); + } + + final SimHypervisor.Context context = activeContext; + if (context == null) { + throw new IllegalStateException("Hypervisor is inactive"); + } + + return new VmContext( + context, this, random, interferenceDomain, counters, SimHypervisor.this.counters, workload, meta); + } + + @Override + public Context getActiveContext() { + return super.getActiveContext(); + } + + void close() { + if (isClosed) { + return; + } + + isClosed = true; + cancel(); + } + } + + /** + * A {@link SimAbstractMachine.Context} for a virtual machine instance. + */ + private static final class VmContext extends SimAbstractMachine.Context implements FlowStageLogic { + private final Context context; + private final SplittableRandom random; + private final VmCounters vmCounters; + private final HvCounters hvCounters; + private final VmInterferenceMember interferenceMember; + private final FlowStage stage; + private final FlowMultiplexer multiplexer; + private final Clock clock; + + private final List<VCpu> cpus; + private final SimAbstractMachine.Memory memory; + private final List<SimAbstractMachine.NetworkAdapter> net; + private final List<SimAbstractMachine.StorageDevice> disk; + + private final Inlet[] muxInlets; + private long lastUpdate; + private long lastCounterUpdate; + private final double d; + + private float demand; + private float usage; + private float capacity; + + private float previousDemand; + private float previousCapacity; + + private VmContext( + Context context, + VirtualMachine machine, + SplittableRandom random, + VmInterferenceDomain interferenceDomain, + VmCounters vmCounters, + HvCounters hvCounters, + SimWorkload workload, + Map<String, Object> meta) { + super(machine, workload, meta); + + this.context = context; + this.random = random; + this.vmCounters = vmCounters; + this.hvCounters = hvCounters; + this.clock = context.clock; + + final VmInterferenceProfile interferenceProfile = (VmInterferenceProfile) meta.get("interference-profile"); + VmInterferenceMember interferenceMember = null; + if (interferenceDomain != null && interferenceProfile != null) { + interferenceMember = interferenceDomain.join(interferenceProfile); + interferenceMember.activate(); + } + this.interferenceMember = interferenceMember; + + final FlowGraph graph = context.ctx.getGraph(); + final FlowStage stage = graph.newStage(this); + this.stage = stage; + this.lastUpdate = clock.millis(); + this.lastCounterUpdate = clock.millis(); + + final FlowMultiplexer multiplexer = context.multiplexer; + this.multiplexer = multiplexer; + + final MachineModel model = machine.getModel(); + final List<ProcessingUnit> cpuModels = model.getCpus(); + final Inlet[] muxInlets = new Inlet[cpuModels.size()]; + final ArrayList<VCpu> cpus = new ArrayList<>(); + + this.muxInlets = muxInlets; + this.cpus = cpus; + + float capacity = 0.f; + + for (int i = 0; i < cpuModels.size(); i++) { + final Inlet muxInlet = multiplexer.newInput(); + muxInlets[i] = muxInlet; + + final InPort input = stage.getInlet("cpu" + i); + final OutPort output = stage.getOutlet("mux" + i); + + final Handler handler = new Handler(this, input, output); + input.setHandler(handler); + output.setHandler(handler); + + final ProcessingUnit cpuModel = cpuModels.get(i); + capacity += cpuModel.getFrequency(); + + final VCpu cpu = new VCpu(cpuModel, input); + cpus.add(cpu); + + graph.connect(output, muxInlet); + } + this.d = cpuModels.size() / capacity; + + this.memory = new SimAbstractMachine.Memory(graph, model.getMemory()); + + int netIndex = 0; + final ArrayList<SimAbstractMachine.NetworkAdapter> 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<SimAbstractMachine.StorageDevice> disk = new ArrayList<>(); + this.disk = disk; + for (org.opendc.simulator.compute.model.StorageDevice device : model.getStorage()) { + disk.add(new SimAbstractMachine.StorageDevice(graph, device, diskIndex++)); + } + } + + /** + * Update the performance counters of the virtual machine. + * + * @param now The timestamp at which to update the counter. + */ + void updateCounters(long now) { + long lastUpdate = this.lastCounterUpdate; + this.lastCounterUpdate = now; + long delta = now - lastUpdate; + + if (delta > 0) { + final VmCounters counters = this.vmCounters; + + float demand = this.previousDemand; + float rate = this.usage; + float capacity = this.previousCapacity; + + final double factor = this.d * delta; + final double active = rate * factor; + + counters.cpuActiveTime += Math.round(active); + counters.cpuIdleTime += Math.round((capacity - rate) * factor); + counters.cpuStealTime += Math.round((demand - rate) * factor); + } + } + + /** + * Update the performance counters of the virtual machine. + */ + void updateCounters() { + updateCounters(clock.millis()); + } + + @Override + public FlowGraph getGraph() { + return stage.getGraph(); + } + + @Override + public List<? extends SimProcessingUnit> getCpus() { + return cpus; + } + + @Override + public SimMemory getMemory() { + return memory; + } + + @Override + public List<? extends SimNetworkInterface> getNetworkInterfaces() { + return net; + } + + @Override + public List<? extends SimStorageInterface> getStorageInterfaces() { + return disk; + } + + @Override + public long onUpdate(FlowStage ctx, long now) { + float usage = 0.f; + for (Inlet inlet : muxInlets) { + usage += ((InPort) inlet).getRate(); + } + this.usage = usage; + this.previousDemand = demand; + this.previousCapacity = capacity; + + long lastUpdate = this.lastUpdate; + this.lastUpdate = now; + long delta = now - lastUpdate; + + if (delta > 0) { + final VmInterferenceMember interferenceMember = this.interferenceMember; + double penalty = 0.0; + + if (interferenceMember != null) { + final FlowMultiplexer multiplexer = this.multiplexer; + double load = multiplexer.getRate() / Math.min(1.0, multiplexer.getCapacity()); + penalty = 1 - interferenceMember.apply(random, load); + } + + final double factor = this.d * delta; + final long lostTime = Math.round(factor * usage * penalty); + + this.vmCounters.cpuLostTime += lostTime; + this.hvCounters.cpuLostTime += lostTime; + } + + // Invalidate the FlowStage of the hypervisor to update its counters (via onUpdate) + context.invalidate(); + + return Long.MAX_VALUE; + } + + @Override + protected void doCancel() { + super.doCancel(); + + // Synchronize the counters before stopping the hypervisor. Otherwise, the last report is missed. + updateCounters(clock.millis()); + + stage.close(); + + final FlowMultiplexer multiplexer = this.multiplexer; + for (Inlet muxInlet : muxInlets) { + multiplexer.releaseInput(muxInlet); + } + + final VmInterferenceMember interferenceMember = this.interferenceMember; + if (interferenceMember != null) { + interferenceMember.deactivate(); + } + } + } + + /** + * A {@link SimProcessingUnit} of a virtual machine. + */ + private static final class VCpu implements SimProcessingUnit { + private final ProcessingUnit model; + private final InPort input; + + private VCpu(ProcessingUnit model, InPort input) { + this.model = model; + this.input = input; + + input.pull((float) model.getFrequency()); + } + + @Override + public double getFrequency() { + return input.getCapacity(); + } + + @Override + public void setFrequency(double frequency) { + input.pull((float) frequency); + } + + @Override + public double getDemand() { + return input.getDemand(); + } + + @Override + public double getSpeed() { + return input.getRate(); + } + + @Override + public ProcessingUnit getModel() { + return model; + } + + @Override + public Inlet getInput() { + return input; + } + + @Override + public String toString() { + return "SimHypervisor.VCpu[model" + model + "]"; + } + } + + /** + * A handler for forwarding flow between an inlet and outlet. + */ + private static class Handler implements InHandler, OutHandler { + private final InPort input; + private final OutPort output; + private final VmContext context; + + private Handler(VmContext context, InPort input, OutPort output) { + this.context = context; + this.input = input; + this.output = output; + } + + @Override + public void onPush(InPort port, float demand) { + context.demand += -port.getDemand() + demand; + + output.push(demand); + } + + @Override + public void onUpstreamFinish(InPort port, Throwable cause) { + context.demand -= port.getDemand(); + + output.push(0.f); + } + + @Override + public float getRate(InPort port) { + return output.getRate(); + } + + @Override + public void onPull(OutPort port, float capacity) { + context.capacity += -port.getCapacity() + capacity; + + input.pull(capacity); + } + + @Override + public void onDownstreamFinish(OutPort port, Throwable cause) { + context.capacity -= port.getCapacity(); + + input.pull(0.f); + } + } + + /** + * Implementation of {@link SimHypervisorCounters} for the hypervisor. + */ + private class HvCounters implements SimHypervisorCounters { + private long cpuActiveTime; + private long cpuIdleTime; + private long cpuStealTime; + private long cpuLostTime; + + @Override + public long getCpuActiveTime() { + return cpuActiveTime; + } + + @Override + public long getCpuIdleTime() { + return cpuIdleTime; + } + + @Override + public long getCpuStealTime() { + return cpuStealTime; + } + + @Override + public long getCpuLostTime() { + return cpuLostTime; + } + + @Override + public void sync() { + final Context context = activeContext; + + if (context != null) { + context.updateCounters(); + } + } + } + + /** + * Implementation of {@link SimHypervisorCounters} for the virtual machine. + */ + private static class VmCounters implements SimHypervisorCounters { + private final VirtualMachine vm; + private long cpuActiveTime; + private long cpuIdleTime; + private long cpuStealTime; + private long cpuLostTime; + + private VmCounters(VirtualMachine vm) { + this.vm = vm; + } + + @Override + public long getCpuActiveTime() { + return cpuActiveTime; + } + + @Override + public long getCpuIdleTime() { + return cpuIdleTime; + } + + @Override + public long getCpuStealTime() { + return cpuStealTime; + } + + @Override + public long getCpuLostTime() { + return cpuLostTime; + } + + @Override + public void sync() { + final VmContext context = (VmContext) vm.getActiveContext(); + + if (context != null) { + context.updateCounters(); + } + } + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisorCounters.kt b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisorCounters.java index 63fee507..fc77e9d6 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisorCounters.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisorCounters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 AtLarge Research + * 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 @@ -20,34 +20,34 @@ * SOFTWARE. */ -package org.opendc.simulator.compute.kernel +package org.opendc.simulator.compute.kernel; /** - * Performance counters of a [SimHypervisor]. + * Performance counters of a {@link SimHypervisor}. */ public interface SimHypervisorCounters { /** - * The amount of time (in milliseconds) the CPUs of the hypervisor were actively running. + * Return the amount of time (in milliseconds) the CPUs of the hypervisor were actively running. */ - public val cpuActiveTime: Long + long getCpuActiveTime(); /** - * The amount of time (in milliseconds) the CPUs of the hypervisor were idle. + * Return the amount of time (in milliseconds) the CPUs of the hypervisor were idle. */ - public val cpuIdleTime: Long + long getCpuIdleTime(); /** - * The amount of CPU time (in milliseconds) that virtual machines were ready to run, but were not able to. + * Return the amount of CPU time (in milliseconds) that virtual machines were ready to run, but were not able to. */ - public val cpuStealTime: Long + long getCpuStealTime(); /** - * The amount of CPU time (in milliseconds) that was lost due to interference between virtual machines. + * Return the amount of CPU time (in milliseconds) that was lost due to interference between virtual machines. */ - public val cpuLostTime: Long + long getCpuLostTime(); /** - * Flush the counter values. + * Synchronize the counter values. */ - public fun flush() + void sync(); } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimVirtualMachine.kt b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimVirtualMachine.java index 36219ef2..fdf5e47f 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimVirtualMachine.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimVirtualMachine.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 AtLarge Research + * 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 @@ -20,31 +20,31 @@ * SOFTWARE. */ -package org.opendc.simulator.compute.kernel +package org.opendc.simulator.compute.kernel; -import org.opendc.simulator.compute.SimMachine +import org.opendc.simulator.compute.SimMachine; /** - * A virtual [SimMachine] running on top of another [SimMachine]. + * A virtual {@link SimMachine} running on top of another {@link SimMachine}. */ -public interface SimVirtualMachine : SimMachine { +public interface SimVirtualMachine extends SimMachine { /** - * The resource counters associated with the virtual machine. + * Return the performance counters associated with the virtual machine. */ - public val counters: SimHypervisorCounters + SimHypervisorCounters getCounters(); /** - * The CPU usage of the VM in MHz. + * Return the CPU usage of the VM in MHz. */ - public val cpuUsage: Double + double getCpuUsage(); /** - * The CPU usage of the VM in MHz. + * Return the CPU usage of the VM in MHz. */ - public val cpuDemand: Double + double getCpuDemand(); /** - * The CPU capacity of the VM in MHz. + * Return the CPU capacity of the VM in MHz. */ - public val cpuCapacity: Double + double getCpuCapacity(); } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernor.kt b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernor.java index d33827db..69a371e1 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernor.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 AtLarge Research + * 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 @@ -20,37 +20,27 @@ * SOFTWARE. */ -package org.opendc.simulator.compute.kernel.cpufreq +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. * + * <p> * Each of the scaling governors implements a single, possibly parametrized, performance scaling algorithm. * - * For more information, see the documentation of the Linux CPUFreq subsystem: - * https://www.kernel.org/doc/html/latest/admin-guide/pm/cpufreq.html + * @see <a href="https://www.kernel.org/doc/html/latest/admin-guide/pm/cpufreq.html">documentation of the Linux CPUFreq subsystem</a>. */ public interface ScalingGovernor { /** - * Create the scaling logic for the specified [policy] + * This method is invoked when the governor is started. */ - public fun createLogic(policy: ScalingPolicy): Logic + default void onStart() {} /** - * The logic of the scaling governor. + * This method is invoked when the governor should re-decide the frequency limits. + * + * @param load The load of the system. */ - public interface Logic { - /** - * This method is invoked when the governor is started. - */ - public fun onStart() {} - - /** - * This method is invoked when the governor should re-decide the frequency limits. - * - * @param load The load of the system. - */ - public fun onLimit(load: Double) {} - } + default void onLimit(double load) {} } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ConstantPowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernorFactory.java index 0fe32b0d..97a49879 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ConstantPowerModel.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernorFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 AtLarge Research + * 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 @@ -20,13 +20,14 @@ * SOFTWARE. */ -package org.opendc.simulator.compute.power +package org.opendc.simulator.compute.kernel.cpufreq; /** - * A power model which produces a constant value [power]. + * Factory interface for a {@link ScalingGovernor}. */ -public class ConstantPowerModel(private val power: Double) : PowerModel { - public override fun computePower(utilization: Double): Double = power - - override fun toString(): String = "ConstantPowerModel[power=$power]" +public interface ScalingGovernorFactory { + /** + * Create the scaling logic for the specified {@link ScalingPolicy}. + */ + ScalingGovernor newGovernor(ScalingPolicy policy); } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernors.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernors.java new file mode 100644 index 00000000..2b10ae59 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernors.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.kernel.cpufreq; + +/** + * Collection of common {@link ScalingGovernor} implementations. + */ +public class ScalingGovernors { + private ScalingGovernors() {} + + /** + * Return a {@link ScalingGovernorFactory} for the <code>performance</code> scaling governor. + * + * <p> + * 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 <code>powersave</code> scaling governor. + * + * <p> + * 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 <code>conservative</code> 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 <code>conservative</code> 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 <code>ondemand</code> 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/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ScalingPolicy.kt b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingPolicy.java index f9351896..0cdb7a0b 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ScalingPolicy.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingPolicy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 AtLarge Research + * 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 @@ -20,32 +20,37 @@ * SOFTWARE. */ -package org.opendc.simulator.compute.kernel.cpufreq +package org.opendc.simulator.compute.kernel.cpufreq; -import org.opendc.simulator.compute.SimProcessingUnit +import org.opendc.simulator.compute.SimProcessingUnit; /** - * An interface that holds the state managed by a [ScalingGovernor] and used by the underlying machine to control the - * CPU frequencies. + * 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. */ - public val cpu: SimProcessingUnit + SimProcessingUnit getCpu(); /** - * The target frequency which the CPU should attempt to attain. + * Return the target frequency which the CPU should attempt to attain. */ - public var target: Double + double getTarget(); /** - * The minimum frequency to which the CPU may scale. + * Set the target frequency which the CPU should attempt to attain. */ - public val min: Double + void setTarget(double target); /** - * The maximum frequency to which the CPU may scale. + * Return the minimum frequency to which the CPU may scale. */ - public val max: Double + double getMin(); + + /** + * Return the maximum frequency to which the CPU may scale. + */ + double getMax(); } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.java new file mode 100644 index 00000000..cc671379 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.kernel.interference; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collections; +import java.util.WeakHashMap; + +/** + * A domain where virtual machines may incur performance variability due to operating on the same resource and + * therefore causing interference. + */ +public final class VmInterferenceDomain { + /** + * A cache to maintain a mapping between the active profiles in this domain. + */ + private final WeakHashMap<VmInterferenceProfile, VmInterferenceMember> cache = new WeakHashMap<>(); + + /** + * The set of members active in this domain. + */ + private final ArrayList<VmInterferenceMember> activeKeys = new ArrayList<>(); + + /** + * Queue of participants that will be removed or added to the active groups. + */ + private final ArrayDeque<VmInterferenceMember> participants = new ArrayDeque<>(); + + /** + * Join this interference domain with the specified <code>profile</code> 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 <code>member</code> as active in this interference domain. + */ + void activate(VmInterferenceMember member) { + final ArrayList<VmInterferenceMember> activeKeys = this.activeKeys; + int pos = Collections.binarySearch(activeKeys, member); + if (pos < 0) { + activeKeys.add(-pos - 1, member); + } + + computeActiveGroups(activeKeys, member); + } + + /** + * Mark the specified <code>member</code> as inactive in this interference domain. + */ + void deactivate(VmInterferenceMember member) { + final ArrayList<VmInterferenceMember> activeKeys = this.activeKeys; + activeKeys.remove(member); + computeActiveGroups(activeKeys, member); + } + + /** + * (Re-)compute the active groups. + */ + private void computeActiveGroups(ArrayList<VmInterferenceMember> activeKeys, VmInterferenceMember member) { + if (activeKeys.isEmpty()) { + return; + } + + final int[] groups = member.membership; + final int[][] members = member.members; + final ArrayDeque<VmInterferenceMember> participants = this.participants; + + for (int group : groups) { + int[] groupMembers = members[group]; + + int i = 0; + int j = 0; + int intersection = 0; + + // Compute the intersection of the group members and the current active members + while (i < groupMembers.length && j < activeKeys.size()) { + int l = groupMembers[i]; + final VmInterferenceMember rightEntry = activeKeys.get(j); + int r = rightEntry.id; + + if (l < r) { + i++; + } else if (l > r) { + j++; + } else { + if (++intersection > 1) { + rightEntry.addGroup(group); + } else { + participants.add(rightEntry); + } + + i++; + j++; + } + } + + while (true) { + VmInterferenceMember participant = participants.poll(); + + if (participant == null) { + break; + } + + if (intersection <= 1) { + participant.removeGroup(group); + } else { + participant.addGroup(group); + } + } + } + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceMember.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceMember.java new file mode 100644 index 00000000..64cd5077 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceMember.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.kernel.interference; + +import java.util.Arrays; +import java.util.SplittableRandom; +import org.jetbrains.annotations.NotNull; + +/** + * A participant of an interference domain. + */ +public final class VmInterferenceMember implements Comparable<VmInterferenceMember> { + private final VmInterferenceDomain domain; + private final VmInterferenceModel model; + final int id; + final int[] membership; + final int[][] members; + private final double[] targets; + private final double[] scores; + + private int[] groups = new int[2]; + private int groupsSize = 0; + + private int refCount = 0; + + VmInterferenceMember( + VmInterferenceDomain domain, + VmInterferenceModel model, + int id, + int[] membership, + int[][] members, + double[] targets, + double[] scores) { + this.domain = domain; + this.model = model; + this.id = id; + this.membership = membership; + this.members = members; + this.targets = targets; + this.scores = scores; + } + + /** + * Mark this member as active in this interference domain. + */ + public void activate() { + if (refCount++ <= 0) { + domain.activate(this); + } + } + + /** + * Mark this member as inactive in this interference domain. + */ + public void deactivate() { + if (--refCount <= 0) { + domain.deactivate(this); + } + } + + /** + * Compute the performance score of the member in this interference domain. + * + * @param random The source of randomness to apply when computing the performance score. + * @param load The overall load on the interference domain. + * @return A score representing the performance score to be applied to the member, with 1 + * meaning no influence, <1 means that performance degrades, and >1 means that performance improves. + */ + public double apply(SplittableRandom random, double load) { + int groupsSize = this.groupsSize; + + if (groupsSize == 0) { + return 1.0; + } + + int[] groups = this.groups; + double[] targets = this.targets; + + int low = 0; + int high = groupsSize - 1; + int group = -1; + + // Perform binary search over the groups based on target load + while (low <= high) { + int mid = low + high >>> 1; + int midGroup = groups[mid]; + double target = targets[midGroup]; + + if (target < load) { + low = mid + 1; + group = midGroup; + } else if (target > load) { + high = mid - 1; + } else { + group = midGroup; + break; + } + } + + if (group >= 0 && random.nextInt(members[group].length) == 0) { + return scores[group]; + } + + return 1.0; + } + + /** + * Add an active group to this member. + */ + void addGroup(int group) { + int[] groups = this.groups; + int groupsSize = this.groupsSize; + int pos = Arrays.binarySearch(groups, 0, groupsSize, group); + + if (pos >= 0) { + return; + } + + int idx = -pos - 1; + + if (groups.length == groupsSize) { + int newSize = groupsSize + (groupsSize >> 1); + groups = Arrays.copyOf(groups, newSize); + this.groups = groups; + } + + System.arraycopy(groups, idx, groups, idx + 1, groupsSize - idx); + groups[idx] = group; + this.groupsSize += 1; + } + + /** + * Remove an active group from this member. + */ + void removeGroup(int group) { + int[] groups = this.groups; + int groupsSize = this.groupsSize; + int pos = Arrays.binarySearch(groups, 0, groupsSize, group); + + if (pos < 0) { + return; + } + + System.arraycopy(groups, pos + 1, groups, pos, groupsSize - pos - 1); + this.groupsSize -= 1; + } + + @Override + public int compareTo(@NotNull VmInterferenceMember member) { + int cmp = Integer.compare(model.hashCode(), member.model.hashCode()); + if (cmp != 0) { + return cmp; + } + + return Integer.compare(id, member.id); + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.java new file mode 100644 index 00000000..e2093266 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.kernel.interference; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import org.jetbrains.annotations.Nullable; + +/** + * An interference model that models the resource interference between virtual machines on a host. + */ +public final class VmInterferenceModel { + private final Map<String, Integer> idMapping; + private final int[][] members; + private final int[][] membership; + private final double[] targets; + private final double[] scores; + + private VmInterferenceModel( + Map<String, Integer> 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 <code>id</code>. + * + * @param id The identifier of the virtual machine. + * @return A {@link VmInterferenceProfile} representing the virtual machine as part of interference model or + * <code>null</code> 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<Set<String>> members; + private final TreeSet<String> 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<String> 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<Set<String>> 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<String, Integer> idMapping = new HashMap<>(); + TreeMap<String, ArrayList<Integer>> 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<String> 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<Integer> 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/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceProfile.kt b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceProfile.java index 004dbd07..3f0c0a88 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceProfile.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceProfile.java @@ -20,32 +20,41 @@ * SOFTWARE. */ -package org.opendc.simulator.compute.kernel.interference +package org.opendc.simulator.compute.kernel.interference; /** * A profile of a particular virtual machine describing its interference pattern with other virtual machines. - * - * @param model The model to which this profile belongs. - * @property id The identifier of the profile inside the model. - * @property membership The membership of the profile in the groups. - * @param members The members in the model. - * @param targets The targets in the model. - * @param scores The scores in the model. */ -public class VmInterferenceProfile internal constructor( - private val model: VmInterferenceModel, - private val id: Int, - private val membership: IntArray, - private val members: Array<IntArray>, - private val targets: DoubleArray, - private val scores: DoubleArray -) { +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; + /** - * Create a new [VmInterferenceMember] based on this profile for the specified [domain]. + * Construct a {@link VmInterferenceProfile}. */ - internal fun newMember(domain: VmInterferenceDomain): VmInterferenceMember { - return VmInterferenceMember(domain, model, id, membership, members, targets, scores) + 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; } - override fun toString(): String = "VmInterferenceProfile[id=$id]" + /** + * Create a new {@link VmInterferenceMember} based on this profile for the specified <code>domain</code>. + */ + VmInterferenceMember newMember(VmInterferenceDomain domain) { + return new VmInterferenceMember(domain, model, id, membership, members, targets, scores); + } + + @Override + public String toString() { + return "VmInterferenceProfile[id=" + id + "]"; + } } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/MachineModel.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/MachineModel.java new file mode 100644 index 00000000..2c625fce --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/MachineModel.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.model; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * A description of the physical or virtual machine on which a bootable image runs. + */ +public final class MachineModel { + private final List<ProcessingUnit> cpus; + private final List<MemoryUnit> memory; + private final List<NetworkAdapter> net; + private final List<StorageDevice> storage; + + /** + * Construct a {@link MachineModel} instance. + * + * @param cpus The list of processing units available to the image. + * @param memory The list of memory units available to the image. + * @param net A list of network adapters available to the machine. + * @param storage A list of storage devices available to the machine. + */ + public MachineModel( + Iterable<ProcessingUnit> cpus, + Iterable<MemoryUnit> memory, + Iterable<NetworkAdapter> net, + Iterable<StorageDevice> storage) { + this.cpus = new ArrayList<>(); + cpus.forEach(this.cpus::add); + + this.memory = new ArrayList<>(); + memory.forEach(this.memory::add); + + this.net = new ArrayList<>(); + net.forEach(this.net::add); + + this.storage = new ArrayList<>(); + storage.forEach(this.storage::add); + } + + /** + * Construct a {@link MachineModel} instance. + * + * @param cpus The list of processing units available to the image. + * @param memory The list of memory units available to the image. + */ + public MachineModel(Iterable<ProcessingUnit> cpus, Iterable<MemoryUnit> memory) { + this(cpus, memory, Collections.emptyList(), Collections.emptyList()); + } + + /** + * Optimize the [MachineModel] by merging all resources of the same type into a single resource with the combined + * capacity. Such configurations can be simulated more efficiently by OpenDC. + */ + public MachineModel optimize() { + ProcessingUnit originalCpu = cpus.get(0); + + double freq = 0.0; + for (ProcessingUnit cpu : cpus) { + freq += cpu.getFrequency(); + } + + ProcessingNode originalNode = originalCpu.getNode(); + ProcessingNode processingNode = new ProcessingNode( + originalNode.getVendor(), originalNode.getModelName(), originalNode.getArchitecture(), 1); + ProcessingUnit processingUnit = new ProcessingUnit(processingNode, originalCpu.getId(), freq); + + long memorySize = 0; + for (MemoryUnit mem : memory) { + memorySize += mem.getSize(); + } + MemoryUnit memoryUnit = new MemoryUnit("Generic", "Generic", 3200.0, memorySize); + + return new MachineModel(List.of(processingUnit), List.of(memoryUnit)); + } + + /** + * Return the processing units of this machine. + */ + public List<ProcessingUnit> getCpus() { + return Collections.unmodifiableList(cpus); + } + + /** + * Return the memory units of this machine. + */ + public List<MemoryUnit> getMemory() { + return Collections.unmodifiableList(memory); + } + + /** + * Return the network adapters of this machine. + */ + public List<NetworkAdapter> getNetwork() { + return Collections.unmodifiableList(net); + } + + /** + * Return the storage devices of this machine. + */ + public List<StorageDevice> getStorage() { + return Collections.unmodifiableList(storage); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MachineModel that = (MachineModel) o; + return cpus.equals(that.cpus) + && memory.equals(that.memory) + && net.equals(that.net) + && storage.equals(that.storage); + } + + @Override + public int hashCode() { + return Objects.hash(cpus, memory, net, storage); + } + + @Override + public String toString() { + return "MachineModel[cpus=" + cpus + ",memory=" + memory + ",net=" + net + ",storage=" + storage + "]"; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/MemoryUnit.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/MemoryUnit.java new file mode 100644 index 00000000..4250f5a2 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/MemoryUnit.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.model; + +import java.util.Objects; + +/** + * A memory unit of a compute resource, either virtual or physical. + */ +public final class MemoryUnit { + private final String vendor; + private final String modelName; + private final double speed; + private final long size; + + /** + * Construct a {@link ProcessingNode} instance. + * + * @param vendor The vendor of the storage device. + * @param modelName The model name of the device. + * @param speed The access speed of the memory in MHz. + * @param size The size of the memory unit in MBs. + */ + public MemoryUnit(String vendor, String modelName, double speed, long size) { + this.vendor = vendor; + this.modelName = modelName; + this.speed = speed; + this.size = size; + } + + /** + * Return the vendor of the storage device. + */ + public String getVendor() { + return vendor; + } + + /** + * Return the model name of the device. + */ + public String getModelName() { + return modelName; + } + + /** + * Return the access speed of the memory in MHz. + */ + public double getSpeed() { + return speed; + } + + /** + * Return the size of the memory unit in MBs. + */ + public long getSize() { + return size; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MemoryUnit that = (MemoryUnit) o; + return Double.compare(that.speed, speed) == 0 + && size == that.size + && vendor.equals(that.vendor) + && modelName.equals(that.modelName); + } + + @Override + public int hashCode() { + return Objects.hash(vendor, modelName, speed, size); + } + + @Override + public String toString() { + return "ProcessingNode[vendor='" + vendor + "',modelName='" + modelName + "',speed=" + speed + "MHz,size=" + + size + "MB]"; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/NetworkAdapter.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/NetworkAdapter.java new file mode 100644 index 00000000..ff3daa40 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/NetworkAdapter.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.model; + +import java.util.Objects; + +/** + * A description of a network adapter + */ +public final class NetworkAdapter { + private final String vendor; + private final String modelName; + private final double bandwidth; + + /** + * Construct a {@link NetworkAdapter} instance. + * + * @param vendor The vendor of the storage device. + * @param modelName The model name of the device. + * @param bandwidth The bandwidth of the network adapter in Mbps. + */ + public NetworkAdapter(String vendor, String modelName, double bandwidth) { + this.vendor = vendor; + this.modelName = modelName; + this.bandwidth = bandwidth; + } + + /** + * Return the vendor of the storage device. + */ + public String getVendor() { + return vendor; + } + + /** + * Return the model name of the device. + */ + public String getModelName() { + return modelName; + } + + /** + * Return the bandwidth of the network adapter in Mbps. + */ + public double getBandwidth() { + return bandwidth; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NetworkAdapter that = (NetworkAdapter) o; + return Double.compare(that.bandwidth, bandwidth) == 0 + && vendor.equals(that.vendor) + && modelName.equals(that.modelName); + } + + @Override + public int hashCode() { + return Objects.hash(vendor, modelName, bandwidth); + } + + @Override + public String toString() { + return "NetworkAdapter[vendor='" + vendor + "',modelName='" + modelName + "',bandwidth=" + bandwidth + "Mbps]"; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/ProcessingNode.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/ProcessingNode.java new file mode 100644 index 00000000..01a87b96 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/ProcessingNode.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.model; + +import java.util.Objects; + +/** + * A processing node/package/socket containing possibly several CPU cores. + */ +public final class ProcessingNode { + private final String vendor; + private final String modelName; + private final String arch; + private final int coreCount; + + /** + * Construct a {@link ProcessingNode} instance. + * + * @param vendor The vendor of the storage device. + * @param modelName The model name of the device. + * @param arch The micro-architecture of the processor node. + * @param coreCount The number of logical CPUs in the processor node. + */ + public ProcessingNode(String vendor, String modelName, String arch, int coreCount) { + this.vendor = vendor; + this.modelName = modelName; + this.arch = arch; + this.coreCount = coreCount; + } + + /** + * Return the vendor of the storage device. + */ + public String getVendor() { + return vendor; + } + + /** + * Return the model name of the device. + */ + public String getModelName() { + return modelName; + } + + /** + * Return the micro-architecture of the processor node. + */ + public String getArchitecture() { + return arch; + } + + /** + * Return the number of logical CPUs in the processor node. + */ + public int getCoreCount() { + return coreCount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ProcessingNode that = (ProcessingNode) o; + return coreCount == that.coreCount + && vendor.equals(that.vendor) + && modelName.equals(that.modelName) + && arch.equals(that.arch); + } + + @Override + public int hashCode() { + return Objects.hash(vendor, modelName, arch, coreCount); + } + + @Override + public String toString() { + return "ProcessingNode[vendor='" + vendor + "',modelName='" + modelName + "',arch=" + arch + ",coreCount=" + + coreCount + "]"; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/ProcessingUnit.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/ProcessingUnit.java new file mode 100644 index 00000000..51a045d1 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/ProcessingUnit.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.model; + +import java.util.Objects; + +/** + * A single logical compute unit of processor node, either virtual or physical. + */ +public final class ProcessingUnit { + private final ProcessingNode node; + private final int id; + private final double frequency; + + /** + * Construct a {@link ProcessingUnit} instance. + * + * @param node The processing node containing the CPU core. + * @param id The identifier of the CPU core within the processing node. + * @param frequency The clock rate of the CPU in MHz. + */ + public ProcessingUnit(ProcessingNode node, int id, double frequency) { + this.node = node; + this.id = id; + this.frequency = frequency; + } + + /** + * Return the processing node containing the CPU core. + */ + public ProcessingNode getNode() { + return node; + } + + /** + * Return the identifier of the CPU core within the processing node. + */ + public int getId() { + return id; + } + + /** + * Return the clock rate of the CPU in MHz. + */ + public double getFrequency() { + return frequency; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ProcessingUnit that = (ProcessingUnit) o; + return id == that.id && Double.compare(that.frequency, frequency) == 0 && Objects.equals(node, that.node); + } + + @Override + public int hashCode() { + return Objects.hash(node, id, frequency); + } + + @Override + public String toString() { + return "ProcessingUnit[node=" + node + ",id=" + id + ",frequency=" + frequency + "]"; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/StorageDevice.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/StorageDevice.java new file mode 100644 index 00000000..549ccc7e --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/StorageDevice.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.model; + +import java.util.Objects; + +/** + * Model for a physical storage device attached to a machine. + */ +public final class StorageDevice { + private final String vendor; + private final String modelName; + private final double capacity; + private final double readBandwidth; + private final double writeBandwidth; + + /** + * Construct a {@link StorageDevice} instance. + * + * @param vendor The vendor of the storage device. + * @param modelName The model name of the device. + * @param capacity The capacity of the device. + * @param readBandwidth The read bandwidth of the device in MBps. + * @param writeBandwidth The write bandwidth of the device in MBps. + */ + public StorageDevice( + String vendor, String modelName, double capacity, double readBandwidth, double writeBandwidth) { + this.vendor = vendor; + this.modelName = modelName; + this.capacity = capacity; + this.readBandwidth = readBandwidth; + this.writeBandwidth = writeBandwidth; + } + + /** + * Return the vendor of the storage device. + */ + public String getVendor() { + return vendor; + } + + /** + * Return the model name of the device. + */ + public String getModelName() { + return modelName; + } + + /** + * Return the capacity of the device. + */ + public double getCapacity() { + return capacity; + } + + /** + * Return the read bandwidth of the device in MBps. + */ + public double getReadBandwidth() { + return readBandwidth; + } + + /** + * Return the write bandwidth of the device in MBps. + */ + public double getWriteBandwidth() { + return writeBandwidth; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StorageDevice that = (StorageDevice) o; + return Double.compare(that.capacity, capacity) == 0 + && Double.compare(that.readBandwidth, readBandwidth) == 0 + && Double.compare(that.writeBandwidth, writeBandwidth) == 0 + && vendor.equals(that.vendor) + && modelName.equals(that.modelName); + } + + @Override + public int hashCode() { + return Objects.hash(vendor, modelName, capacity, readBandwidth, writeBandwidth); + } + + @Override + public String toString() { + return "StorageDevice[vendor='" + vendor + "',modelName='" + modelName + "',capacity=" + capacity + + ",readBandwidth=" + readBandwidth + ",writeBandwidth=" + writeBandwidth + "]"; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CpuPowerModel.java index decb2420..e023d098 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerModel.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CpuPowerModel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 AtLarge Research + * 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 @@ -20,19 +20,19 @@ * SOFTWARE. */ -package org.opendc.simulator.compute.power +package org.opendc.simulator.compute.power; -import org.opendc.simulator.compute.SimMachine +import org.opendc.simulator.compute.SimMachine; /** - * A model for estimating the power usage of a [SimMachine]. + * A model for estimating the power usage of a {@link SimMachine} based on the CPU usage. */ -public interface PowerModel { +public interface CpuPowerModel { /** * Computes CPU power consumption for each host. * * @param utilization The CPU utilization percentage. - * @return A [Double] value of CPU power consumption. + * @return A double value of CPU power consumption (in W). */ - public fun computePower(utilization: Double): Double + double computePower(double utilization); } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CpuPowerModels.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CpuPowerModels.java new file mode 100644 index 00000000..5d3d936b --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CpuPowerModels.java @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.power; + +import java.util.Arrays; + +/** + * A collection {@link CpuPowerModel} implementations. + */ +public class CpuPowerModels { + private CpuPowerModels() {} + + /** + * Construct a constant {@link CpuPowerModel}. + * + * @param power The power consumption fo the server at all times (in W). + */ + public static CpuPowerModel constant(double power) { + return new ConstantPowerModel(power); + } + + /** + * Construct a square root {@link CpuPowerModel} that is adapted from CloudSim. + * + * @param maxPower The maximum power draw of the server in W. + * @param idlePower The power draw of the server at its lowest utilization level in W. + */ + public static CpuPowerModel sqrt(double maxPower, double idlePower) { + return new SqrtPowerModel(maxPower, idlePower); + } + + /** + * Construct a linear {@link CpuPowerModel} that is adapted from CloudSim. + * + * @param maxPower The maximum power draw of the server in W. + * @param idlePower The power draw of the server at its lowest utilization level in W. + */ + public static CpuPowerModel linear(double maxPower, double idlePower) { + return new LinearPowerModel(maxPower, idlePower); + } + + /** + * Construct a square {@link CpuPowerModel} that is adapted from CloudSim. + * + * @param maxPower The maximum power draw of the server in W. + * @param idlePower The power draw of the server at its lowest utilization level in W. + */ + public static CpuPowerModel square(double maxPower, double idlePower) { + return new SquarePowerModel(maxPower, idlePower); + } + + /** + * Construct a cubic {@link CpuPowerModel} that is adapted from CloudSim. + * + * @param maxPower The maximum power draw of the server in W. + * @param idlePower The power draw of the server at its lowest utilization level in W. + */ + public static CpuPowerModel cubic(double maxPower, double idlePower) { + return new CubicPowerModel(maxPower, idlePower); + } + + /** + * Construct a {@link CpuPowerModel} that minimizes the mean squared error (MSE) + * to the actual power measurement by tuning the calibration parameter. + * + * @param maxPower The maximum power draw of the server in W. + * @param idlePower The power draw of the server at its lowest utilization level in W. + * @param calibrationFactor The parameter set to minimize the MSE. + * @see <a href="https://dl.acm.org/doi/abs/10.1145/1273440.1250665"> + * Fan et al., Power provisioning for a warehouse-sized computer, ACM SIGARCH'07</a> + */ + public static CpuPowerModel mse(double maxPower, double idlePower, double calibrationFactor) { + return new MsePowerModel(maxPower, idlePower, calibrationFactor); + } + + /** + * Construct an asymptotic {@link CpuPowerModel} adapted from GreenCloud. + * + * @param maxPower The maximum power draw of the server in W. + * @param idlePower The power draw of the server at its lowest utilization level in W. + * @param asymUtil A utilization level at which the server attains asymptotic, + * i.e., close to linear power consumption versus the offered load. + * For most of the CPUs,a is in [0.2, 0.5]. + * @param dvfs A flag indicates whether DVFS is enabled. + */ + public static CpuPowerModel asymptotic(double maxPower, double idlePower, double asymUtil, boolean dvfs) { + return new AsymptoticPowerModel(maxPower, idlePower, asymUtil, dvfs); + } + + /** + * Construct a linear interpolation model {@link CpuPowerModel} that is adapted from CloudSim. + * + * <p> + * 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 <a href="http://www.spec.org/power_ssj2008/results/res2011q1/">Machines used in the SPEC benchmark</a> + */ + public static CpuPowerModel interpolate(double... powerLevels) { + return new InterpolationPowerModel(powerLevels.clone()); + } + + /** + * Decorate an existing {@link CpuPowerModel} to ensure that zero power consumption is reported when there is no + * utilization. + * + * @param delegate The existing {@link CpuPowerModel} to decorate. + */ + public static CpuPowerModel zeroIdle(CpuPowerModel delegate) { + return new ZeroIdlePowerDecorator(delegate); + } + + private static final class ConstantPowerModel implements CpuPowerModel { + private final double power; + + ConstantPowerModel(double power) { + this.power = power; + } + + @Override + public double computePower(double utilization) { + return power; + } + + @Override + public String toString() { + return "ConstantPowerModel[power=" + power + "]"; + } + } + + private abstract static class MaxIdlePowerModel implements CpuPowerModel { + protected final double maxPower; + protected final double idlePower; + + MaxIdlePowerModel(double maxPower, double idlePower) { + this.maxPower = maxPower; + this.idlePower = idlePower; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[max=" + maxPower + ",idle=" + idlePower + "]"; + } + } + + private static final class SqrtPowerModel extends MaxIdlePowerModel { + private final double factor; + + SqrtPowerModel(double maxPower, double idlePower) { + super(maxPower, idlePower); + this.factor = (maxPower - idlePower) / Math.sqrt(100); + } + + @Override + public double computePower(double utilization) { + return idlePower + factor * Math.sqrt(utilization * 100); + } + } + + private static final class LinearPowerModel extends MaxIdlePowerModel { + private final double factor; + + LinearPowerModel(double maxPower, double idlePower) { + super(maxPower, idlePower); + this.factor = (maxPower - idlePower) / 100; + } + + @Override + public double computePower(double utilization) { + return idlePower + factor * utilization * 100; + } + } + + private static final class SquarePowerModel extends MaxIdlePowerModel { + private final double factor; + + SquarePowerModel(double maxPower, double idlePower) { + super(maxPower, idlePower); + this.factor = (maxPower - idlePower) / Math.pow(100, 2); + } + + @Override + public double computePower(double utilization) { + return idlePower + factor * Math.pow(utilization * 100, 2); + } + } + + private static final class CubicPowerModel extends MaxIdlePowerModel { + private final double factor; + + CubicPowerModel(double maxPower, double idlePower) { + super(maxPower, idlePower); + this.factor = (maxPower - idlePower) / Math.pow(100, 3); + } + + @Override + public double computePower(double utilization) { + return idlePower + factor * Math.pow(utilization * 100, 3); + } + } + + private static final class MsePowerModel extends MaxIdlePowerModel { + private final double calibrationFactor; + private final double factor; + + MsePowerModel(double maxPower, double idlePower, double calibrationFactor) { + super(maxPower, idlePower); + this.calibrationFactor = calibrationFactor; + this.factor = (maxPower - idlePower) / 100; + } + + @Override + public double computePower(double utilization) { + return idlePower + factor * (2 * utilization - Math.pow(utilization, calibrationFactor)) * 100; + } + + @Override + public String toString() { + return "MsePowerModel[max=" + maxPower + ",idle=" + idlePower + ",calibrationFactor=" + calibrationFactor + + "]"; + } + } + + private static final class AsymptoticPowerModel extends MaxIdlePowerModel { + private final double asymUtil; + private final boolean dvfs; + private final double factor; + + AsymptoticPowerModel(double maxPower, double idlePower, double asymUtil, boolean dvfs) { + super(maxPower, idlePower); + this.asymUtil = asymUtil; + this.dvfs = dvfs; + this.factor = (maxPower - idlePower) / 100; + } + + @Override + public double computePower(double utilization) { + if (dvfs) { + return idlePower + + (factor * 100) + / 2 + * (1 + + Math.pow(utilization, 3) + - Math.pow(Math.E, -Math.pow(utilization, 3) / asymUtil)); + } else { + return idlePower + (factor * 100) / 2 * (1 + utilization - Math.pow(Math.E, -utilization / asymUtil)); + } + } + + @Override + public String toString() { + return "AsymptoticPowerModel[max=" + maxPower + ",idle=" + idlePower + ",asymUtil=" + asymUtil + ",dvfs=" + + dvfs + "]"; + } + } + + private static final class InterpolationPowerModel implements CpuPowerModel { + private final double[] powerLevels; + + InterpolationPowerModel(double[] powerLevels) { + this.powerLevels = powerLevels; + } + + @Override + public double computePower(double utilization) { + final double[] powerLevels = this.powerLevels; + double clampedUtilization = Math.min(1.0, Math.max(0.0, utilization)); + + if (utilization % 0.1 == 0.0) { + return powerLevels[(int) (clampedUtilization * 10)]; + } + + int utilizationFlr = (int) Math.floor(clampedUtilization * 10); + int utilizationCil = (int) Math.ceil(clampedUtilization * 10); + double powerFlr = powerLevels[utilizationFlr]; + double powerCil = powerLevels[utilizationCil]; + double delta = (powerCil - powerFlr) / 10; + + return powerFlr + delta * (clampedUtilization - utilizationFlr / 10.0) * 100; + } + + @Override + public String toString() { + return "InterpolationPowerModel[levels=" + Arrays.toString(powerLevels) + "]"; + } + } + + private static final class ZeroIdlePowerDecorator implements CpuPowerModel { + private final CpuPowerModel delegate; + + ZeroIdlePowerDecorator(CpuPowerModel delegate) { + this.delegate = delegate; + } + + @Override + public double computePower(double utilization) { + if (utilization == 0.0) { + return 0.0; + } + + return delegate.computePower(utilization); + } + + @Override + public String toString() { + return "ZeroIdlePowerDecorator[delegate=" + delegate + "]"; + } + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimFlopsWorkload.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimFlopsWorkload.java new file mode 100644 index 00000000..255fd1b2 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimFlopsWorkload.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.workload; + +import java.util.List; +import org.opendc.simulator.compute.SimMachineContext; +import org.opendc.simulator.compute.SimProcessingUnit; +import org.opendc.simulator.flow2.FlowGraph; +import org.opendc.simulator.flow2.FlowStage; +import org.opendc.simulator.flow2.FlowStageLogic; +import org.opendc.simulator.flow2.OutPort; + +/** + * A {@link SimWorkload} that models applications as a static number of floating point operations executed on + * multiple cores of a compute resource. + */ +public class SimFlopsWorkload implements SimWorkload, FlowStageLogic { + private final long flops; + private final double utilization; + + private SimMachineContext ctx; + private FlowStage stage; + private OutPort[] outputs; + + private float remainingAmount; + private long lastUpdate; + + /** + * Construct a new {@link SimFlopsWorkload}. + * + * @param flops The number of floating point operations to perform for this task in MFLOPs. + * @param utilization A model of the CPU utilization of the application. + */ + public SimFlopsWorkload(long flops, double utilization) { + if (flops < 0) { + throw new IllegalArgumentException("Number of FLOPs must be positive"); + } else if (utilization <= 0.0 || utilization > 1.0) { + throw new IllegalArgumentException("Utilization must be in (0, 1]"); + } + + this.flops = flops; + this.utilization = utilization; + } + + @Override + public void onStart(SimMachineContext ctx) { + this.ctx = ctx; + + final FlowGraph graph = ctx.getGraph(); + final FlowStage stage = graph.newStage(this); + this.stage = stage; + + final List<? extends SimProcessingUnit> cpus = ctx.getCpus(); + final OutPort[] outputs = new OutPort[cpus.size()]; + this.outputs = outputs; + + for (int i = 0; i < cpus.size(); i++) { + final SimProcessingUnit cpu = cpus.get(i); + final OutPort output = stage.getOutlet("cpu" + i); + + graph.connect(output, cpu.getInput()); + outputs[i] = output; + } + + this.remainingAmount = flops; + this.lastUpdate = graph.getEngine().getClock().millis(); + } + + @Override + public void onStop(SimMachineContext ctx) { + this.ctx = null; + + final FlowStage stage = this.stage; + if (stage != null) { + this.stage = null; + stage.close(); + } + } + + @Override + public String toString() { + return "SimFlopsWorkload[FLOPs=" + flops + ",utilization=" + utilization + "]"; + } + + @Override + public long onUpdate(FlowStage ctx, long now) { + long lastUpdate = this.lastUpdate; + this.lastUpdate = now; + + long delta = Math.max(0, now - lastUpdate); + + float consumed = 0.f; + float limit = 0.f; + + for (final OutPort output : outputs) { + consumed += output.getRate() * delta; + + float outputLimit = (float) (output.getCapacity() * utilization); + limit += outputLimit; + + output.push(outputLimit); + } + consumed = (float) (consumed * 0.001); + + float remainingAmount = this.remainingAmount - consumed; + this.remainingAmount = remainingAmount; + + long duration = (long) Math.ceil(remainingAmount / limit * 1000); + + if (duration <= 0) { + final SimMachineContext machineContext = this.ctx; + if (machineContext != null) { + machineContext.shutdown(); + } + ctx.close(); + return Long.MAX_VALUE; + } + + return now + duration; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimRuntimeWorkload.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimRuntimeWorkload.java new file mode 100644 index 00000000..c3380b31 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimRuntimeWorkload.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.workload; + +import java.util.List; +import org.opendc.simulator.compute.SimMachineContext; +import org.opendc.simulator.compute.SimProcessingUnit; +import org.opendc.simulator.flow2.FlowGraph; +import org.opendc.simulator.flow2.FlowStage; +import org.opendc.simulator.flow2.FlowStageLogic; +import org.opendc.simulator.flow2.OutPort; + +/** + * A [SimWorkload] that models application execution as a single duration. + */ +public class SimRuntimeWorkload implements SimWorkload, FlowStageLogic { + private final long duration; + private final double utilization; + + private SimMachineContext ctx; + private FlowStage stage; + private OutPort[] outputs; + + private long remainingDuration; + private long lastUpdate; + + /** + * Construct a new {@link SimRuntimeWorkload}. + * + * @param duration The duration of the workload in milliseconds. + * @param utilization A model of the CPU utilization of the application. + */ + public SimRuntimeWorkload(long duration, double utilization) { + if (duration < 0) { + throw new IllegalArgumentException("Duration must be positive"); + } else if (utilization <= 0.0 || utilization > 1.0) { + throw new IllegalArgumentException("Utilization must be in (0, 1]"); + } + + this.duration = duration; + this.utilization = utilization; + } + + @Override + public void onStart(SimMachineContext ctx) { + this.ctx = ctx; + + final FlowGraph graph = ctx.getGraph(); + final FlowStage stage = graph.newStage(this); + this.stage = stage; + + final List<? extends SimProcessingUnit> cpus = ctx.getCpus(); + final OutPort[] outputs = new OutPort[cpus.size()]; + this.outputs = outputs; + + for (int i = 0; i < cpus.size(); i++) { + final SimProcessingUnit cpu = cpus.get(i); + final OutPort output = stage.getOutlet("cpu" + i); + + graph.connect(output, cpu.getInput()); + outputs[i] = output; + } + + this.remainingDuration = duration; + this.lastUpdate = graph.getEngine().getClock().millis(); + } + + @Override + public void onStop(SimMachineContext ctx) { + this.ctx = null; + + final FlowStage stage = this.stage; + if (stage != null) { + this.stage = null; + this.outputs = null; + stage.close(); + } + } + + @Override + public long onUpdate(FlowStage ctx, long now) { + long lastUpdate = this.lastUpdate; + this.lastUpdate = now; + + long delta = now - lastUpdate; + long duration = this.remainingDuration - delta; + + if (duration <= 0) { + final SimMachineContext machineContext = this.ctx; + if (machineContext != null) { + machineContext.shutdown(); + } + ctx.close(); + return Long.MAX_VALUE; + } + + this.remainingDuration = duration; + + for (final OutPort output : outputs) { + float limit = (float) (output.getCapacity() * utilization); + output.push(limit); + } + + return now + duration; + } + + @Override + public String toString() { + return "SimDurationWorkload[duration=" + duration + "ms,utilization=" + utilization + "]"; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTrace.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTrace.java new file mode 100644 index 00000000..0bd2b2eb --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTrace.java @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.workload; + +import java.util.Arrays; +import java.util.List; +import org.opendc.simulator.compute.SimMachineContext; +import org.opendc.simulator.compute.SimProcessingUnit; +import org.opendc.simulator.flow2.FlowGraph; +import org.opendc.simulator.flow2.FlowStage; +import org.opendc.simulator.flow2.FlowStageLogic; +import org.opendc.simulator.flow2.OutPort; + +/** + * A workload trace that describes the resource utilization over time in a collection of {@link SimTraceFragment}s. + */ +public final class SimTrace { + private final double[] usageCol; + private final long[] deadlineCol; + private final int[] coresCol; + private final int size; + + /** + * Construct a {@link SimTrace} instance. + * + * @param usageCol The column containing the CPU usage of each fragment (in MHz). + * @param deadlineCol The column containing the ending timestamp for each fragment (in epoch millis). + * @param coresCol The column containing the utilized cores. + * @param size The number of fragments in the trace. + */ + private SimTrace(double[] usageCol, long[] deadlineCol, int[] coresCol, int size) { + if (size < 0) { + throw new IllegalArgumentException("Invalid trace size"); + } else if (usageCol.length < size) { + throw new IllegalArgumentException("Invalid number of usage entries"); + } else if (deadlineCol.length < size) { + throw new IllegalArgumentException("Invalid number of deadline entries"); + } else if (coresCol.length < size) { + throw new IllegalArgumentException("Invalid number of core entries"); + } + + this.usageCol = usageCol; + this.deadlineCol = deadlineCol; + this.coresCol = coresCol; + this.size = size; + } + + /** + * Construct a {@link SimWorkload} for this trace. + * + * @param offset The offset for the timestamps. + */ + public SimWorkload createWorkload(long offset) { + return new Workload(offset, usageCol, deadlineCol, coresCol, size); + } + + /** + * Create a new {@link Builder} instance with the specified initial capacity. + */ + public static Builder builder(int initialCapacity) { + return new Builder(initialCapacity); + } + + /** + * Create a new {@link Builder} instance with a default initial capacity. + */ + public static Builder builder() { + return builder(256); + } + + /** + * Construct a {@link SimTrace} from the specified fragments. + * + * @param fragments The array of fragments to construct the trace from. + */ + public static SimTrace ofFragments(SimTraceFragment... fragments) { + final Builder builder = builder(fragments.length); + + for (SimTraceFragment fragment : fragments) { + builder.add(fragment.timestamp + fragment.duration, fragment.usage, fragment.cores); + } + + return builder.build(); + } + + /** + * Construct a {@link SimTrace} from the specified fragments. + * + * @param fragments The fragments to construct the trace from. + */ + public static SimTrace ofFragments(List<SimTraceFragment> fragments) { + final Builder builder = builder(fragments.size()); + + for (SimTraceFragment fragment : fragments) { + builder.add(fragment.timestamp + fragment.duration, fragment.usage, fragment.cores); + } + + return builder.build(); + } + + /** + * Builder class for a {@link SimTrace}. + */ + public static final class Builder { + private double[] usageCol; + private long[] deadlineCol; + private int[] coresCol; + + private int size; + private boolean isBuilt; + + /** + * Construct a new {@link Builder} instance. + */ + private Builder(int initialCapacity) { + this.usageCol = new double[initialCapacity]; + this.deadlineCol = new long[initialCapacity]; + this.coresCol = new int[initialCapacity]; + } + + /** + * Add a fragment to the trace. + * + * @param deadline The timestamp at which the fragment ends (in epoch millis). + * @param usage The CPU usage at this fragment. + * @param cores The number of cores used during this fragment. + */ + public void add(long deadline, double usage, int cores) { + if (isBuilt) { + recreate(); + } + + int size = this.size; + double[] usageCol = this.usageCol; + + if (size == usageCol.length) { + grow(); + usageCol = this.usageCol; + } + + deadlineCol[size] = deadline; + usageCol[size] = usage; + coresCol[size] = cores; + + this.size++; + } + + /** + * Build the {@link SimTrace} instance. + */ + public SimTrace build() { + isBuilt = true; + return new SimTrace(usageCol, deadlineCol, coresCol, size); + } + + /** + * Helper method to grow the capacity of the trace. + */ + private void grow() { + int arraySize = usageCol.length; + int newSize = arraySize + (arraySize >> 1); + + usageCol = Arrays.copyOf(usageCol, newSize); + deadlineCol = Arrays.copyOf(deadlineCol, newSize); + coresCol = Arrays.copyOf(coresCol, newSize); + } + + /** + * Clone the columns of the trace. + * + * <p> + * This is necessary when a {@link SimTrace} has been built already, but the user is again adding entries to + * the builder. + */ + private void recreate() { + isBuilt = false; + usageCol = usageCol.clone(); + deadlineCol = deadlineCol.clone(); + coresCol = coresCol.clone(); + } + } + + /** + * Implementation of {@link SimWorkload} that executes a trace. + */ + private static class Workload implements SimWorkload, FlowStageLogic { + private SimMachineContext ctx; + private FlowStage stage; + private OutPort[] outputs; + private int index; + private int coreCount; + + private final long offset; + private final double[] usageCol; + private final long[] deadlineCol; + private final int[] coresCol; + private final int size; + + private Workload(long offset, double[] usageCol, long[] deadlineCol, int[] coresCol, int size) { + this.offset = offset; + this.usageCol = usageCol; + this.deadlineCol = deadlineCol; + this.coresCol = coresCol; + this.size = size; + } + + @Override + public void onStart(SimMachineContext ctx) { + this.ctx = ctx; + + final FlowGraph graph = ctx.getGraph(); + final List<? extends SimProcessingUnit> cpus = ctx.getCpus(); + + stage = graph.newStage(this); + coreCount = cpus.size(); + + final OutPort[] outputs = new OutPort[cpus.size()]; + this.outputs = outputs; + + for (int i = 0; i < cpus.size(); i++) { + final SimProcessingUnit cpu = cpus.get(i); + final OutPort output = stage.getOutlet("cpu" + i); + + graph.connect(output, cpu.getInput()); + outputs[i] = output; + } + } + + @Override + public void onStop(SimMachineContext ctx) { + this.ctx = null; + + final FlowStage stage = this.stage; + + if (stage != null) { + this.stage = null; + stage.close(); + } + } + + @Override + public long onUpdate(FlowStage ctx, long now) { + int size = this.size; + long offset = this.offset; + long nowOffset = now - offset; + + int index = this.index; + + long[] deadlines = deadlineCol; + long deadline = deadlines[index]; + + while (deadline <= nowOffset && ++index < size) { + deadline = deadlines[index]; + } + + if (index >= size) { + final SimMachineContext machineContext = this.ctx; + if (machineContext != null) { + machineContext.shutdown(); + } + ctx.close(); + return Long.MAX_VALUE; + } + + this.index = index; + + int cores = Math.min(coreCount, coresCol[index]); + float usage = (float) usageCol[index] / cores; + + final OutPort[] outputs = this.outputs; + + for (int i = 0; i < cores; i++) { + outputs[i].push(usage); + } + + for (int i = cores; i < outputs.length; i++) { + outputs[i].push(0.f); + } + + return deadline + offset; + } + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTraceFragment.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTraceFragment.java new file mode 100644 index 00000000..12c1348d --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTraceFragment.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.workload; + +import java.util.Objects; + +/** + * A fragment of the workload trace. + */ +public final class SimTraceFragment { + final long timestamp; + final long duration; + final double usage; + final int cores; + + /** + * Construct a {@link SimTraceFragment}. + * + * @param timestamp The timestamp at which the fragment starts (in epoch millis). + * @param duration The duration of the fragment (in milliseconds). + * @param usage The CPU usage during the fragment (in MHz). + * @param cores The amount of cores utilized during the fragment. + */ + public SimTraceFragment(long timestamp, long duration, double usage, int cores) { + this.timestamp = timestamp; + this.duration = duration; + this.usage = usage; + this.cores = cores; + } + + /** + * Return the timestamp at which the fragment starts (in epoch millis). + */ + public long getTimestamp() { + return timestamp; + } + + /** + * Return the duration of the fragment (in milliseconds). + */ + public long getDuration() { + return duration; + } + + /** + * Return the CPU usage during the fragment (in MHz). + */ + public double getUsage() { + return usage; + } + + /** + * Return the amount of cores utilized during the fragment. + */ + public int getCores() { + return cores; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SimTraceFragment that = (SimTraceFragment) o; + return timestamp == that.timestamp + && duration == that.duration + && Double.compare(that.usage, usage) == 0 + && cores == that.cores; + } + + @Override + public int hashCode() { + return Objects.hash(timestamp, duration, usage, cores); + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkload.kt b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkload.java index 61c6e2ad..7be51265 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkload.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkload.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 AtLarge Research + * 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 @@ -20,15 +20,16 @@ * SOFTWARE. */ -package org.opendc.simulator.compute.workload +package org.opendc.simulator.compute.workload; -import org.opendc.simulator.compute.SimMachineContext +import org.opendc.simulator.compute.SimMachineContext; /** * A model that characterizes the runtime behavior of some particular workload. * + * <p> * Workloads are stateful objects that may be paused and resumed at a later moment. As such, be careful when using the - * same [SimWorkload] from multiple contexts. + * same {@link SimWorkload} from multiple contexts. */ public interface SimWorkload { /** @@ -36,12 +37,12 @@ public interface SimWorkload { * * @param ctx The execution context in which the machine runs. */ - public fun onStart(ctx: SimMachineContext) + void onStart(SimMachineContext ctx); /** * This method is invoked when the workload is stopped. * * @param ctx The execution context in which the machine runs. */ - public fun onStop(ctx: SimMachineContext) + void onStop(SimMachineContext ctx); } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkloadLifecycle.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkloadLifecycle.java new file mode 100644 index 00000000..f0e2561f --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkloadLifecycle.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2022 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.workload; + +import java.util.HashSet; +import org.opendc.simulator.compute.SimMachineContext; + +/** + * A helper class to manage the lifecycle of a {@link SimWorkload}. + */ +public final class SimWorkloadLifecycle { + private final SimMachineContext ctx; + private final HashSet<Runnable> waiting = new HashSet<>(); + + /** + * Construct a {@link SimWorkloadLifecycle} instance. + * + * @param ctx The {@link SimMachineContext} of the workload. + */ + public SimWorkloadLifecycle(SimMachineContext ctx) { + this.ctx = ctx; + } + + /** + * Register a "completer" callback that must be invoked before ending the lifecycle of the workload. + */ + public Runnable newCompleter() { + Runnable completer = new Runnable() { + @Override + public void run() { + final HashSet<Runnable> waiting = SimWorkloadLifecycle.this.waiting; + if (waiting.remove(this) && waiting.isEmpty()) { + ctx.shutdown(); + } + } + }; + waiting.add(completer); + return completer; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractMachine.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractMachine.kt deleted file mode 100644 index 71784567..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimAbstractMachine.kt +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute - -import mu.KotlinLogging -import org.opendc.simulator.compute.device.SimNetworkAdapter -import org.opendc.simulator.compute.device.SimPeripheral -import org.opendc.simulator.compute.model.MachineModel -import org.opendc.simulator.compute.model.MemoryUnit -import org.opendc.simulator.compute.model.NetworkAdapter -import org.opendc.simulator.compute.model.StorageDevice -import org.opendc.simulator.compute.workload.SimWorkload -import org.opendc.simulator.flow.FlowConsumer -import org.opendc.simulator.flow.FlowConvergenceListener -import org.opendc.simulator.flow.FlowEngine -import org.opendc.simulator.flow.FlowForwarder -import org.opendc.simulator.flow.FlowSink -import org.opendc.simulator.flow.FlowSource -import org.opendc.simulator.flow.batch - -/** - * Abstract implementation of the [SimMachine] interface. - * - * @param engine The engine to manage the machine's resources. - * @param model The model of the machine. - */ -public abstract class SimAbstractMachine( - protected val engine: FlowEngine, - final override val model: MachineModel -) : SimMachine, FlowConvergenceListener { - /** - * The resources allocated for this machine. - */ - public abstract val cpus: List<SimProcessingUnit> - - /** - * The memory interface of the machine. - */ - public val memory: SimMemory = Memory(FlowSink(engine, model.memory.sumOf { it.size }.toDouble()), model.memory) - - /** - * The network interfaces available to the machine. - */ - public val net: List<SimNetworkInterface> = model.net.mapIndexed { i, adapter -> NetworkAdapterImpl(engine, adapter, i) } - - /** - * The network interfaces available to the machine. - */ - public val storage: List<SimStorageInterface> = model.storage.mapIndexed { i, device -> StorageDeviceImpl(engine, device, i) } - - /** - * The peripherals of the machine. - */ - public override val peripherals: List<SimPeripheral> = net.map { it as SimNetworkAdapter } - - /** - * The current active [Context]. - */ - private var _ctx: Context? = null - - override fun startWorkload(workload: SimWorkload, meta: Map<String, Any>): SimMachineContext { - check(_ctx == null) { "A machine cannot run concurrently" } - - val ctx = Context(workload, meta) - ctx.start() - return ctx - } - - override fun cancel() { - _ctx?.close() - } - - override fun onConverge(now: Long) {} - - /** - * The execution context in which the workload runs. - * - * @param workload The workload that is running on the machine. - * @param meta The metadata passed to the workload. - */ - private inner class Context( - private val workload: SimWorkload, - override val meta: Map<String, Any> - ) : SimMachineContext { - /** - * A flag to indicate that the context has been closed. - */ - private var isClosed = false - - val engine: FlowEngine = this@SimAbstractMachine.engine - - /** - * Start this context. - */ - fun start() { - try { - _ctx = this - engine.batch { workload.onStart(this) } - } catch (cause: Throwable) { - logger.warn(cause) { "Workload failed during onStart callback" } - close() - } - } - - override val cpus: List<SimProcessingUnit> = this@SimAbstractMachine.cpus - - override val memory: SimMemory = this@SimAbstractMachine.memory - - override val net: List<SimNetworkInterface> = this@SimAbstractMachine.net - - override val storage: List<SimStorageInterface> = this@SimAbstractMachine.storage - - override fun close() { - if (isClosed) { - return - } - - isClosed = true - assert(_ctx == this) { "Invariant violation: multiple contexts active for a single machine" } - _ctx = null - - // Cancel all the resources associated with the machine - doCancel() - - try { - workload.onStop(this) - } catch (cause: Throwable) { - logger.warn(cause) { "Workload failed during onStop callback" } - } - } - - /** - * Run the stop procedures for the resources associated with the machine. - */ - private fun doCancel() { - engine.batch { - for (cpu in cpus) { - cpu.cancel() - } - - memory.cancel() - - for (ifx in net) { - (ifx as NetworkAdapterImpl).disconnect() - } - - for (storage in storage) { - val impl = storage as StorageDeviceImpl - impl.read.cancel() - impl.write.cancel() - } - } - } - - override fun toString(): String = "SimAbstractMachine.Context" - } - - /** - * The [SimMemory] implementation for a machine. - */ - private class Memory(source: FlowSink, override val models: List<MemoryUnit>) : SimMemory, FlowConsumer by source { - override fun toString(): String = "SimAbstractMachine.Memory" - } - - /** - * The [SimNetworkAdapter] implementation for a machine. - */ - private class NetworkAdapterImpl( - engine: FlowEngine, - model: NetworkAdapter, - index: Int - ) : SimNetworkAdapter(), SimNetworkInterface { - override val name: String = "eth$index" - - override val bandwidth: Double = model.bandwidth - - override val provider: FlowConsumer - get() = _rx - - override fun createConsumer(): FlowSource = _tx - - override val tx: FlowConsumer - get() = _tx - private val _tx = FlowForwarder(engine) - - override val rx: FlowSource - get() = _rx - private val _rx = FlowForwarder(engine) - - override fun toString(): String = "SimAbstractMachine.NetworkAdapterImpl[name=$name,bandwidth=$bandwidth]" - } - - /** - * The [SimStorageInterface] implementation for a machine. - */ - private class StorageDeviceImpl( - engine: FlowEngine, - model: StorageDevice, - index: Int - ) : SimStorageInterface { - override val name: String = "disk$index" - - override val capacity: Double = model.capacity - - override val read: FlowConsumer = FlowSink(engine, model.readBandwidth) - - override val write: FlowConsumer = FlowSink(engine, model.writeBandwidth) - - override fun toString(): String = "SimAbstractMachine.StorageDeviceImpl[name=$name,capacity=$capacity]" - } - - private companion object { - /** - * The logging instance associated with this class. - */ - @JvmStatic - private val logger = KotlinLogging.logger {} - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt deleted file mode 100644 index 4c824440..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2020 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute - -import org.opendc.simulator.compute.device.SimPsu -import org.opendc.simulator.compute.model.MachineModel -import org.opendc.simulator.compute.model.ProcessingUnit -import org.opendc.simulator.compute.power.PowerDriver -import org.opendc.simulator.flow.FlowConsumer -import org.opendc.simulator.flow.FlowEngine -import org.opendc.simulator.flow.FlowSink -import kotlin.math.max - -/** - * A simulated bare-metal machine that is able to run a single workload. - * - * A [SimBareMetalMachine] is a stateful object, and you should be careful when operating this object concurrently. For - * example, the class expects only a single concurrent call to [run]. - * - * @param engine The [FlowEngine] to drive the simulation. - * @param model The machine model to simulate. - * @param powerDriver The power driver to use. - * @param psu The power supply of the machine. - */ -public class SimBareMetalMachine( - engine: FlowEngine, - model: MachineModel, - powerDriver: PowerDriver, - public val psu: SimPsu = SimPsu(500.0, mapOf(1.0 to 1.0)) -) : SimAbstractMachine(engine, model) { - /** - * The current power usage of the machine (without PSU loss) in W. - */ - public val powerUsage: Double - get() = _powerUsage - private var _powerUsage = 0.0 - - /** - * The total energy usage of the machine (without PSU loss) in Joules. - */ - public val energyUsage: Double - get() { - computeEnergyUsage(engine.clock.millis()) - return _energyUsage - } - private var _energyUsage = 0.0 - private var _energyLastComputation = 0L - - /** - * The processing units of the machine. - */ - override val cpus: List<SimProcessingUnit> = model.cpus.map { cpu -> - Cpu(FlowSink(engine, cpu.frequency, this@SimBareMetalMachine), cpu) - } - - /** - * The logic of the power driver. - */ - private val powerDriverLogic = powerDriver.createLogic(this, cpus) - - private var _lastConverge = Long.MAX_VALUE - - override fun onConverge(now: Long) { - // Update the PSU stage - psu.update() - - val lastConverge = _lastConverge - _lastConverge = now - val duration = max(0, now - lastConverge) - if (duration > 0) { - // Compute the power and energy usage of the machine - computeEnergyUsage(now) - } - - _powerUsage = powerDriverLogic.computePower() - } - - init { - psu.connect(powerDriverLogic) - _powerUsage = powerDriverLogic.computePower() - } - - /** - * Helper method to compute total energy usage. - */ - private fun computeEnergyUsage(now: Long) { - val duration = max(0, now - _energyLastComputation) - _energyLastComputation = now - - // Compute the energy usage of the machine - _energyUsage += _powerUsage * (duration / 1000.0) - } - - /** - * A [SimProcessingUnit] of a bare-metal machine. - */ - private class Cpu( - private val source: FlowSink, - override val model: ProcessingUnit - ) : SimProcessingUnit, FlowConsumer by source { - override var capacity: Double - get() = source.capacity - set(value) { - // Clamp the capacity of the CPU between [0.0, maxFreq] - source.capacity = value.coerceIn(0.0, model.frequency) - } - - override fun toString(): String = "SimBareMetalMachine.Cpu[model=$model]" - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimPsu.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimPsu.kt deleted file mode 100644 index 3d3703ae..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/device/SimPsu.kt +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.device - -import org.opendc.simulator.compute.power.PowerDriver -import org.opendc.simulator.flow.FlowConnection -import org.opendc.simulator.flow.FlowSource -import org.opendc.simulator.power.SimPowerInlet -import java.util.TreeMap - -/** - * A power supply of a [SimBareMetalMachine]. - * - * @param ratedOutputPower The rated output power of the PSU. - * @param energyEfficiency The energy efficiency of the PSU for various power draws. - */ -public class SimPsu( - private val ratedOutputPower: Double, - energyEfficiency: Map<Double, Double> -) : SimPowerInlet() { - /** - * The power draw of the machine at this instant. - */ - public val powerDraw: Double - get() = _powerDraw - private var _powerDraw = 0.0 - - /** - * The energy efficiency of the PSU at various power draws. - */ - private val energyEfficiency = TreeMap(energyEfficiency) - - /** - * The consumer context. - */ - private var _ctx: FlowConnection? = null - - /** - * The driver that is connected to the PSU. - */ - private var _driver: PowerDriver.Logic? = null - - init { - require(energyEfficiency.isNotEmpty()) { "Must specify at least one entry for energy efficiency of PSU" } - } - - /** - * Update the power draw of the PSU. - */ - public fun update() { - _ctx?.pull() - } - - /** - * Connect the specified [PowerDriver.Logic] to this PSU. - */ - public fun connect(driver: PowerDriver.Logic) { - check(_driver == null) { "PSU already connected" } - _driver = driver - update() - } - - override fun createSource(): FlowSource = object : FlowSource { - override fun onStart(conn: FlowConnection, now: Long) { - _ctx = conn - conn.shouldSourceConverge = true - } - - override fun onStop(conn: FlowConnection, now: Long) { - _ctx = null - } - - override fun onPull(conn: FlowConnection, now: Long): Long { - val powerDraw = computePowerDraw(_driver?.computePower() ?: 0.0) - conn.push(powerDraw) - return Long.MAX_VALUE - } - - override fun onConverge(conn: FlowConnection, now: Long) { - _powerDraw = conn.rate - } - } - - /** - * Compute the power draw of the PSU including the power loss. - */ - private fun computePowerDraw(load: Double): Double { - val loadPercentage = (load / ratedOutputPower).coerceIn(0.0, 1.0) - val efficiency = energyEfficiency.ceilingEntry(loadPercentage)?.value ?: 1.0 - return load / efficiency - } - - override fun toString(): String = "SimPsu[draw=$_powerDraw]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisor.kt deleted file mode 100644 index e1486d71..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/SimHypervisor.kt +++ /dev/null @@ -1,442 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.kernel - -import org.opendc.simulator.compute.SimAbstractMachine -import org.opendc.simulator.compute.SimMachine -import org.opendc.simulator.compute.SimMachineContext -import org.opendc.simulator.compute.SimProcessingUnit -import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernor -import org.opendc.simulator.compute.kernel.cpufreq.ScalingPolicy -import org.opendc.simulator.compute.kernel.interference.VmInterferenceDomain -import org.opendc.simulator.compute.kernel.interference.VmInterferenceMember -import org.opendc.simulator.compute.kernel.interference.VmInterferenceProfile -import org.opendc.simulator.compute.model.MachineModel -import org.opendc.simulator.compute.model.ProcessingUnit -import org.opendc.simulator.compute.workload.SimWorkload -import org.opendc.simulator.flow.FlowConsumer -import org.opendc.simulator.flow.FlowConvergenceListener -import org.opendc.simulator.flow.FlowEngine -import org.opendc.simulator.flow.mux.FlowMultiplexer -import org.opendc.simulator.flow.mux.FlowMultiplexerFactory -import java.util.SplittableRandom -import kotlin.math.roundToLong - -/** - * A SimHypervisor facilitates the execution of multiple concurrent [SimWorkload]s, while acting as a single workload - * to another [SimMachine]. - * - * @param engine The [FlowEngine] to drive the simulation. - * @param muxFactory The factor for the [FlowMultiplexer] to multiplex the workloads. - * @param random A randomness generator for the interference calculations. - * @param scalingGovernor The scaling governor to use for scaling the CPU frequency of the underlying hardware. - * @param interferenceDomain The interference domain to which the hypervisor belongs. - */ -public class SimHypervisor( - private val engine: FlowEngine, - muxFactory: FlowMultiplexerFactory, - private val random: SplittableRandom, - private val scalingGovernor: ScalingGovernor? = null, - private val interferenceDomain: VmInterferenceDomain = VmInterferenceDomain() -) : SimWorkload, FlowConvergenceListener { - /** - * The [FlowMultiplexer] to multiplex the virtual machines. - */ - private val mux = muxFactory.newMultiplexer(engine, this) - - /** - * The virtual machines running on this hypervisor. - */ - private val _vms = mutableSetOf<VirtualMachine>() - public val vms: Set<SimMachine> - get() = _vms - - /** - * The resource counters associated with the hypervisor. - */ - public val counters: SimHypervisorCounters - get() = _counters - private val _counters = CountersImpl(this) - - /** - * The CPU capacity of the hypervisor in MHz. - */ - public val cpuCapacity: Double - get() = mux.capacity - - /** - * The CPU demand of the hypervisor in MHz. - */ - public val cpuDemand: Double - get() = mux.demand - - /** - * The CPU usage of the hypervisor in MHz. - */ - public val cpuUsage: Double - get() = mux.rate - - /** - * The machine on which the hypervisor runs. - */ - private lateinit var context: SimMachineContext - - /** - * The scaling governors attached to the physical CPUs backing this hypervisor. - */ - private val governors = mutableListOf<ScalingGovernor.Logic>() - - /* SimHypervisor */ - /** - * Create a [SimMachine] instance on which users may run a [SimWorkload]. - * - * @param model The machine to create. - */ - public fun newMachine(model: MachineModel): SimVirtualMachine { - require(canFit(model)) { "Machine does not fit" } - val vm = VirtualMachine(model) - _vms.add(vm) - return vm - } - - /** - * Remove the specified [machine] from the hypervisor. - * - * @param machine The machine to remove. - */ - public fun removeMachine(machine: SimVirtualMachine) { - if (_vms.remove(machine)) { - // This cast must always succeed, since `_vms` only contains `VirtualMachine` types. - (machine as VirtualMachine).close() - } - } - - /** - * Determine whether the specified machine characterized by [model] can fit on this hypervisor at this moment. - */ - public fun canFit(model: MachineModel): Boolean { - return (mux.maxInputs - mux.inputs.size) >= model.cpus.size - } - - /* SimWorkload */ - override fun onStart(ctx: SimMachineContext) { - context = ctx - - _cpuCount = ctx.cpus.size - _cpuCapacity = ctx.cpus.sumOf { it.model.frequency } - _counters.d = _cpuCount / _cpuCapacity * 1000L - - // Clear the existing outputs of the multiplexer - mux.clearOutputs() - - for (cpu in ctx.cpus) { - val governor = scalingGovernor?.createLogic(ScalingPolicyImpl(cpu)) - if (governor != null) { - governors.add(governor) - governor.onStart() - } - - cpu.startConsumer(mux.newOutput()) - } - } - - override fun onStop(ctx: SimMachineContext) {} - - private var _cpuCount = 0 - private var _cpuCapacity = 0.0 - private var _lastConverge = engine.clock.millis() - - /* FlowConvergenceListener */ - override fun onConverge(now: Long) { - val lastConverge = _lastConverge - _lastConverge = now - val delta = now - lastConverge - - if (delta > 0) { - _counters.record() - - val mux = mux - val load = mux.rate / mux.capacity.coerceAtLeast(1.0) - val random = random - - for (vm in _vms) { - vm._counters.record(random, load) - } - } - - val load = cpuDemand / cpuCapacity - for (governor in governors) { - governor.onLimit(load) - } - } - - /** - * A virtual machine running on the hypervisor. - * - * @param model The machine model of the virtual machine. - */ - private inner class VirtualMachine(model: MachineModel) : SimAbstractMachine(engine, model), SimVirtualMachine, AutoCloseable { - /** - * A flag to indicate that the machine is closed. - */ - private var isClosed = false - - /** - * The vCPUs of the machine. - */ - override val cpus = model.cpus.map { cpu -> VCpu(mux, mux.newInput(cpu.frequency), cpu) } - - /** - * The resource counters associated with the hypervisor. - */ - override val counters: SimHypervisorCounters - get() = _counters - - @JvmField val _counters = VmCountersImpl(cpus, null) - - /** - * The CPU capacity of the hypervisor in MHz. - */ - override val cpuCapacity: Double - get() = cpus.sumOf(FlowConsumer::capacity) - - /** - * The CPU demand of the hypervisor in MHz. - */ - override val cpuDemand: Double - get() = cpus.sumOf(FlowConsumer::demand) - - /** - * The CPU usage of the hypervisor in MHz. - */ - override val cpuUsage: Double - get() = cpus.sumOf(FlowConsumer::rate) - - override fun startWorkload(workload: SimWorkload, meta: Map<String, Any>): SimMachineContext { - check(!isClosed) { "Machine is closed" } - - val profile = meta["interference-profile"] as? VmInterferenceProfile - val interferenceMember = if (profile != null) interferenceDomain.join(profile) else null - - val counters = _counters - counters.member = interferenceMember - - return super.startWorkload( - object : SimWorkload { - override fun onStart(ctx: SimMachineContext) { - try { - interferenceMember?.activate() - workload.onStart(ctx) - } catch (cause: Throwable) { - interferenceMember?.deactivate() - throw cause - } - } - - override fun onStop(ctx: SimMachineContext) { - interferenceMember?.deactivate() - counters.member = null - workload.onStop(ctx) - } - }, - meta - ) - } - - override fun close() { - if (isClosed) { - return - } - - isClosed = true - cancel() - - for (cpu in cpus) { - cpu.close() - } - } - } - - /** - * A [SimProcessingUnit] of a virtual machine. - */ - private class VCpu( - private val switch: FlowMultiplexer, - private val source: FlowConsumer, - override val model: ProcessingUnit - ) : SimProcessingUnit, FlowConsumer by source { - override var capacity: Double - get() = source.capacity - set(_) = TODO("Capacity changes on vCPU not supported") - - override fun toString(): String = "SimAbstractHypervisor.VCpu[model=$model]" - - /** - * Close the CPU - */ - fun close() { - switch.removeInput(source) - } - - fun flush() { - switch.flushCounters(source) - } - } - - /** - * A [ScalingPolicy] for a physical CPU of the hypervisor. - */ - private class ScalingPolicyImpl(override val cpu: SimProcessingUnit) : ScalingPolicy { - override var target: Double - get() = cpu.capacity - set(value) { - cpu.capacity = value - } - - override val max: Double = cpu.model.frequency - - override val min: Double = 0.0 - } - - /** - * Implementation of [SimHypervisorCounters]. - */ - private class CountersImpl(private val hv: SimHypervisor) : SimHypervisorCounters { - @JvmField var d = 1.0 // Number of CPUs divided by total CPU capacity - - override val cpuActiveTime: Long - get() = _cpuTime[0] - override val cpuIdleTime: Long - get() = _cpuTime[1] - override val cpuStealTime: Long - get() = _cpuTime[2] - override val cpuLostTime: Long - get() = _cpuTime[3] - - val _cpuTime = LongArray(4) - private val _previous = DoubleArray(3) - - /** - * Record the CPU time of the hypervisor. - */ - fun record() { - val cpuTime = _cpuTime - val previous = _previous - val counters = hv.mux.counters - - val demand = counters.demand - val actual = counters.actual - val remaining = counters.remaining - - val demandDelta = demand - previous[0] - val actualDelta = actual - previous[1] - val remainingDelta = remaining - previous[2] - - previous[0] = demand - previous[1] = actual - previous[2] = remaining - - cpuTime[0] += (actualDelta * d).roundToLong() - cpuTime[1] += (remainingDelta * d).roundToLong() - cpuTime[2] += ((demandDelta - actualDelta) * d).roundToLong() - } - - override fun flush() { - hv.mux.flushCounters() - record() - } - } - - /** - * A [SimHypervisorCounters] implementation for a virtual machine. - */ - private inner class VmCountersImpl( - private val cpus: List<VCpu>, - @JvmField var member: VmInterferenceMember? - ) : SimHypervisorCounters { - private val d = cpus.size / cpus.sumOf { it.model.frequency } * 1000 - - override val cpuActiveTime: Long - get() = _cpuTime[0] - override val cpuIdleTime: Long - get() = _cpuTime[1] - override val cpuStealTime: Long - get() = _cpuTime[2] - override val cpuLostTime: Long - get() = _cpuTime[3] - - private val _cpuTime = LongArray(4) - private val _previous = DoubleArray(3) - - /** - * Record the CPU time of the hypervisor. - */ - fun record(random: SplittableRandom, load: Double) { - val cpuTime = _cpuTime - val previous = _previous - - var demand = 0.0 - var actual = 0.0 - var remaining = 0.0 - - for (cpu in cpus) { - val counters = cpu.counters - - actual += counters.actual - demand += counters.demand - remaining += counters.remaining - } - - val demandDelta = demand - previous[0] - val actualDelta = actual - previous[1] - val remainingDelta = remaining - previous[2] - - previous[0] = demand - previous[1] = actual - previous[2] = remaining - - val d = d - cpuTime[0] += (actualDelta * d).roundToLong() - cpuTime[1] += (remainingDelta * d).roundToLong() - cpuTime[2] += ((demandDelta - actualDelta) * d).roundToLong() - - // Compute the performance penalty due to flow interference - val member = member - if (member != null) { - val penalty = 1 - member.apply(random, load) - val interference = (actualDelta * d * penalty).roundToLong() - - if (interference > 0) { - cpuTime[3] += interference - _counters._cpuTime[3] += interference - } - } - } - - override fun flush() { - for (cpu in cpus) { - cpu.flush() - } - } - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernor.kt deleted file mode 100644 index 1a03221d..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernor.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.kernel.cpufreq - -/** - * A CPUFreq [ScalingGovernor] that models the conservative scaling governor in the Linux kernel. - */ -public class ConservativeScalingGovernor(public val threshold: Double = 0.8, private val stepSize: Double = -1.0) : - ScalingGovernor { - override fun createLogic(policy: ScalingPolicy): ScalingGovernor.Logic = object : ScalingGovernor.Logic { - /** - * The step size to use. - */ - private val stepSize = if (this@ConservativeScalingGovernor.stepSize < 0) { - // https://github.com/torvalds/linux/blob/master/drivers/cpufreq/cpufreq_conservative.c#L33 - policy.max * 0.05 - } else { - this@ConservativeScalingGovernor.stepSize.coerceAtMost(policy.max) - } - - /** - * The previous load of the CPU. - */ - private var previousLoad = threshold - - override fun onStart() { - policy.target = policy.min - } - - override fun onLimit(load: Double) { - val currentTarget = policy.target - if (load > threshold) { - // Check for load increase (see: https://github.com/torvalds/linux/blob/master/drivers/cpufreq/cpufreq_conservative.c#L102) - val step = when { - load > previousLoad -> stepSize - load < previousLoad -> -stepSize - else -> 0.0 - } - policy.target = (currentTarget + step).coerceIn(policy.min, policy.max) - } - previousLoad = load - } - } - - override fun toString(): String = "ConservativeScalingGovernor[threshold=$threshold,stepSize=$stepSize]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernor.kt deleted file mode 100644 index aef15ce9..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernor.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.kernel.cpufreq - -/** - * A CPUFreq [ScalingGovernor] that models the on-demand scaling governor in the Linux kernel. - */ -public class OnDemandScalingGovernor(public val threshold: Double = 0.8) : ScalingGovernor { - override fun createLogic(policy: ScalingPolicy): ScalingGovernor.Logic = object : ScalingGovernor.Logic { - /** - * The multiplier used for the linear frequency scaling. - */ - private val multiplier = (policy.max - policy.min) / 100 - - override fun onStart() { - policy.target = policy.min - } - - override fun onLimit(load: Double) { - policy.target = if (load < threshold) { - /* Proportional scaling (see: https://github.com/torvalds/linux/blob/master/drivers/cpufreq/cpufreq_ondemand.c#L151). */ - policy.min + load * multiplier - } else { - policy.max - } - } - } - - override fun toString(): String = "OnDemandScalingGovernor[threshold=$threshold]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernor.kt deleted file mode 100644 index 13109a9a..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernor.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.kernel.cpufreq - -/** - * A CPUFreq [ScalingGovernor] that causes the highest possible frequency to be requested from the resource. - */ -public class PerformanceScalingGovernor : ScalingGovernor { - override fun createLogic(policy: ScalingPolicy): ScalingGovernor.Logic = object : ScalingGovernor.Logic { - override fun onStart() { - policy.target = policy.max - } - } - - override fun toString(): String = "PerformanceScalingGovernor" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernor.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernor.kt deleted file mode 100644 index 32c0703a..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernor.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.kernel.cpufreq - -/** - * A CPUFreq [ScalingGovernor] that causes the lowest possible frequency to be requested from the resource. - */ -public class PowerSaveScalingGovernor : ScalingGovernor { - override fun createLogic(policy: ScalingPolicy): ScalingGovernor.Logic = object : ScalingGovernor.Logic { - override fun onStart() { - policy.target = policy.min - } - } - - override fun toString(): String = "PowerSaveScalingGovernor" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.kt deleted file mode 100644 index 6861823b..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.kt +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.kernel.interference - -import java.util.ArrayDeque -import java.util.ArrayList -import java.util.WeakHashMap - -/** - * A domain where virtual machines may incur performance variability due to operating on the same resource and - * therefore causing interference. - */ -public class VmInterferenceDomain { - /** - * A cache to maintain a mapping between the active profiles in this domain. - */ - private val cache = WeakHashMap<VmInterferenceProfile, VmInterferenceMember>() - - /** - * The set of members active in this domain. - */ - private val activeKeys = ArrayList<VmInterferenceMember>() - - /** - * Queue of participants that will be removed or added to the active groups. - */ - private val participants = ArrayDeque<VmInterferenceMember>() - - /** - * Join this interference domain with the specified [profile] and return the [VmInterferenceMember] associated with - * the profile. If the member does not exist, it will be created. - */ - public fun join(profile: VmInterferenceProfile): VmInterferenceMember { - return cache.computeIfAbsent(profile) { key -> key.newMember(this) } - } - - /** - * Mark the specified [member] as active in this interference domain. - */ - internal fun activate(member: VmInterferenceMember) { - val activeKeys = activeKeys - val pos = activeKeys.binarySearch(member) - if (pos < 0) { - activeKeys.add(-pos - 1, member) - } - - computeActiveGroups(activeKeys, member) - } - - /** - * Mark the specified [member] as inactive in this interference domain. - */ - internal fun deactivate(member: VmInterferenceMember) { - val activeKeys = activeKeys - activeKeys.remove(member) - computeActiveGroups(activeKeys, member) - } - - /** - * (Re-)compute the active groups. - */ - private fun computeActiveGroups(activeKeys: ArrayList<VmInterferenceMember>, member: VmInterferenceMember) { - if (activeKeys.isEmpty()) { - return - } - - val groups = member.membership - val members = member.members - val participants = participants - - for (group in groups) { - val groupMembers = members[group] - - var i = 0 - var j = 0 - var intersection = 0 - - // Compute the intersection of the group members and the current active members - while (i < groupMembers.size && j < activeKeys.size) { - val l = groupMembers[i] - val rightEntry = activeKeys[j] - val r = rightEntry.id - - if (l < r) { - i++ - } else if (l > r) { - j++ - } else { - if (++intersection > 1) { - rightEntry.addGroup(group) - } else { - participants.add(rightEntry) - } - - i++ - j++ - } - } - - while (true) { - val participant = participants.poll() ?: break - - if (intersection <= 1) { - participant.removeGroup(group) - } else { - participant.addGroup(group) - } - } - } - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceMember.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceMember.kt deleted file mode 100644 index 4b56a058..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceMember.kt +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.kernel.interference - -import java.util.SplittableRandom - -/** - * A participant of an interference domain. - */ -public class VmInterferenceMember( - private val domain: VmInterferenceDomain, - private val model: VmInterferenceModel, - @JvmField internal val id: Int, - @JvmField internal val membership: IntArray, - @JvmField internal val members: Array<IntArray>, - private val targets: DoubleArray, - private val scores: DoubleArray -) : Comparable<VmInterferenceMember> { - /** - * The active groups to which the key belongs. - */ - private var groups: IntArray = IntArray(2) - private var groupsSize: Int = 0 - - /** - * The number of users of the interference key. - */ - private var refCount: Int = 0 - - /** - * Mark this member as active in this interference domain. - */ - public fun activate() { - if (refCount++ <= 0) { - domain.activate(this) - } - } - - /** - * Mark this member as inactive in this interference domain. - */ - public fun deactivate() { - if (--refCount <= 0) { - domain.deactivate(this) - } - } - - /** - * Compute the performance score of the member in this interference domain. - * - * @param random The source of randomness to apply when computing the performance score. - * @param load The overall load on the interference domain. - * @return A score representing the performance score to be applied to the member, with 1 - * meaning no influence, <1 means that performance degrades, and >1 means that performance improves. - */ - public fun apply(random: SplittableRandom, load: Double): Double { - val groupsSize = groupsSize - - if (groupsSize == 0) { - return 1.0 - } - - val groups = groups - val targets = targets - - var low = 0 - var high = groupsSize - 1 - var group = -1 - - // Perform binary search over the groups based on target load - while (low <= high) { - val mid = low + high ushr 1 - val midGroup = groups[mid] - val target = targets[midGroup] - - if (target < load) { - low = mid + 1 - group = midGroup - } else if (target > load) { - high = mid - 1 - } else { - group = midGroup - break - } - } - - return if (group >= 0 && random.nextInt(members[group].size) == 0) { - scores[group] - } else { - 1.0 - } - } - - /** - * Add an active group to this member. - */ - internal fun addGroup(group: Int) { - var groups = groups - val groupsSize = groupsSize - val pos = groups.binarySearch(group, toIndex = groupsSize) - - if (pos >= 0) { - return - } - - val idx = -pos - 1 - - if (groups.size == groupsSize) { - val newSize = groupsSize + (groupsSize shr 1) - groups = groups.copyOf(newSize) - this.groups = groups - } - - groups.copyInto(groups, idx + 1, idx, groupsSize) - groups[idx] = group - this.groupsSize += 1 - } - - /** - * Remove an active group from this member. - */ - internal fun removeGroup(group: Int) { - val groups = groups - val groupsSize = groupsSize - val pos = groups.binarySearch(group, toIndex = groupsSize) - - if (pos < 0) { - return - } - - groups.copyInto(groups, pos, pos + 1, groupsSize) - this.groupsSize -= 1 - } - - override fun compareTo(other: VmInterferenceMember): Int { - val cmp = model.hashCode().compareTo(other.model.hashCode()) - if (cmp != 0) { - return cmp - } - - return id.compareTo(other.id) - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.kt deleted file mode 100644 index 238bffc0..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.kt +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.kernel.interference - -import java.util.TreeMap -import java.util.TreeSet - -/** - * An interference model that models the resource interference between virtual machines on a host. - * - * @param members The target load of each group. - * @param scores The performance score of each group. - * @param members The members belonging to each group. - * @param membership The identifier of each key. - * @param size The number of groups. - */ -public class VmInterferenceModel private constructor( - private val idMapping: Map<String, Int>, - private val members: Array<IntArray>, - private val membership: Array<IntArray>, - private val targets: DoubleArray, - private val scores: DoubleArray, - private val size: Int -) { - /** - * Return the [VmInterferenceProfile] associated with the specified [id]. - * - * @param id The identifier of the virtual machine. - * @return A [VmInterferenceProfile] representing the virtual machine as part of interference model or `null` if - * there is no profile for the virtual machine. - */ - public fun getProfile(id: String): VmInterferenceProfile? { - val intId = idMapping[id] ?: return null - return VmInterferenceProfile(this, intId, membership[intId], members, targets, scores) - } - - public companion object { - /** - * Construct a [Builder] instance. - */ - @JvmStatic - public fun builder(): Builder = Builder() - } - - /** - * Builder class for a [VmInterferenceModel] - */ - public class Builder internal constructor() { - /** - * The target load of each group. - */ - private var _targets = DoubleArray(INITIAL_CAPACITY) { Double.POSITIVE_INFINITY } - - /** - * The performance score of each group. - */ - private var _scores = DoubleArray(INITIAL_CAPACITY) { Double.POSITIVE_INFINITY } - - /** - * The members of each group. - */ - private var _members = ArrayList<Set<String>>(INITIAL_CAPACITY) - - /** - * The mapping from member to group id. - */ - private val ids = TreeSet<String>() - - /** - * The number of groups in the model. - */ - private var size = 0 - - /** - * Add the specified group to the model. - */ - public fun addGroup(members: Set<String>, targetLoad: Double, score: Double): Builder { - val size = size - - if (size == _targets.size) { - grow() - } - - _targets[size] = targetLoad - _scores[size] = score - _members.add(members) - ids.addAll(members) - - this.size++ - - return this - } - - /** - * Build the [VmInterferenceModel]. - */ - public fun build(): VmInterferenceModel { - val size = size - val targets = _targets - val scores = _scores - val members = _members - - val indices = IntArray(size) { it } - indices.sortedWith( - Comparator { l, r -> - var cmp = targets[l].compareTo(targets[r]) // Order by target load - if (cmp != 0) { - return@Comparator cmp - } - - cmp = scores[l].compareTo(scores[r]) // Higher penalty first (this means lower performance score first) - if (cmp != 0) { - cmp - } else { - l.compareTo(r) - } - } - ) - - val newTargets = DoubleArray(size) - val newScores = DoubleArray(size) - val newMembers = arrayOfNulls<IntArray>(size) - - var nextId = 0 - val idMapping = ids.associateWith { nextId++ } - val membership = ids.associateWithTo(TreeMap()) { ArrayList<Int>() } - - for ((group, j) in indices.withIndex()) { - newTargets[group] = targets[j] - newScores[group] = scores[j] - val groupMembers = members[j] - val newGroupMembers = groupMembers.map { idMapping.getValue(it) }.toIntArray() - - newGroupMembers.sort() - newMembers[group] = newGroupMembers - - for (member in groupMembers) { - membership.getValue(member).add(group) - } - } - - @Suppress("UNCHECKED_CAST") - return VmInterferenceModel( - idMapping, - newMembers as Array<IntArray>, - membership.map { it.value.toIntArray() }.toTypedArray(), - newTargets, - newScores, - size - ) - } - - /** - * Helper function to grow the capacity of the internal arrays. - */ - private fun grow() { - val oldSize = _targets.size - val newSize = oldSize + (oldSize shr 1) - - _targets = _targets.copyOf(newSize) - _scores = _scores.copyOf(newSize) - } - - private companion object { - /** - * The initial capacity of the builder. - */ - const val INITIAL_CAPACITY = 256 - } - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/MachineModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/MachineModel.kt deleted file mode 100644 index 22dcaef4..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/MachineModel.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.model - -/** - * A description of the physical or virtual machine on which a bootable image runs. - * - * @property cpus The list of processing units available to the image. - * @property memory The list of memory units available to the image. - * @property net A list of network adapters available to the machine. - * @property storage A list of storage devices available to the machine. - */ -public data class MachineModel( - public val cpus: List<ProcessingUnit>, - public val memory: List<MemoryUnit>, - public val net: List<NetworkAdapter> = emptyList(), - public val storage: List<StorageDevice> = emptyList() -) { - /** - * Optimize the [MachineModel] by merging all resources of the same type into a single resource with the combined - * capacity. Such configurations can be simulated more efficiently by OpenDC. - */ - public fun optimize(): MachineModel { - val originalCpu = cpus[0] - val freq = cpus.sumOf { it.frequency } - val processingNode = originalCpu.node.copy(coreCount = 1) - val processingUnits = listOf(originalCpu.copy(frequency = freq, node = processingNode)) - - val memorySize = memory.sumOf { it.size } - val memoryUnits = listOf(MemoryUnit("Generic", "Generic", 3200.0, memorySize)) - - return MachineModel(processingUnits, memoryUnits) - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/MemoryUnit.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/MemoryUnit.kt deleted file mode 100644 index bcbde5b1..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/MemoryUnit.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2020 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.model - -/** - * A memory unit of a compute resource, either virtual or physical. - * - * @property vendor The vendor string of the memory. - * @property modelName The name of the memory model. - * @property speed The access speed of the memory in MHz. - * @property size The size of the memory unit in MBs. - */ -public data class MemoryUnit( - public val vendor: String, - public val modelName: String, - public val speed: Double, - public val size: Long -) diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/NetworkAdapter.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/NetworkAdapter.kt deleted file mode 100644 index 46472144..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/NetworkAdapter.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.model - -/** - * A description of a network adapter that is - * - * @property vendor The vendor of the network adapter. - * @property modelName The model name of the network adapter. - * @property bandwidth The bandwidth of the network adapter in Mbps. - */ -public data class NetworkAdapter( - public val vendor: String, - public val modelName: String, - public val bandwidth: Double -) diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingNode.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingNode.kt deleted file mode 100644 index 58ed816c..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingNode.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2020 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.model - -/** - * A processing node/package/socket containing possibly several CPU cores. - * - * @property vendor The vendor string of the processor node. - * @property modelName The name of the processor node. - * @property arch The micro-architecture of the processor node. - * @property coreCount The number of logical CPUs in the processor node. - */ -public data class ProcessingNode( - public val vendor: String, - public val arch: String, - public val modelName: String, - public val coreCount: Int -) diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingUnit.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingUnit.kt deleted file mode 100644 index 415e95e6..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/ProcessingUnit.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2020 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.model - -/** - * A single logical compute unit of processor node, either virtual or physical. - * - * @property node The processing node containing the CPU core. - * @property id The identifier of the CPU core within the processing node. - * @property frequency The clock rate of the CPU in MHz. - */ -public data class ProcessingUnit( - public val node: ProcessingNode, - public val id: Int, - public val frequency: Double -) diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/StorageDevice.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/StorageDevice.kt deleted file mode 100644 index 2621ad6d..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/model/StorageDevice.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.model - -/** - * Model for a physical storage device attached to a machine. - * - * @property vendor The vendor of the storage device. - * @property modelName The model name of the device. - * @property capacity The capacity of the device. - * @property readBandwidth The read bandwidth of the device in MBps. - * @property writeBandwidth The write bandwidth of the device in MBps. - */ -public data class StorageDevice( - public val vendor: String, - public val modelName: String, - public val capacity: Double, - public val readBandwidth: Double, - public val writeBandwidth: Double -) diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/AsymptoticPowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/AsymptoticPowerModel.kt deleted file mode 100644 index 46c397fe..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/AsymptoticPowerModel.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.power - -import kotlin.math.E -import kotlin.math.pow - -/** - * The asymptotic power model partially adapted from GreenCloud. - * - * @param maxPower The maximum power draw of the server in W. - * @param idlePower The power draw of the server at its lowest utilization level in W. - * @param asymUtil A utilization level at which the server attains asymptotic, - * i.e., close to linear power consumption versus the offered load. - * For most of the CPUs,a is in [0.2, 0.5]. - * @param isDvfsEnabled A flag indicates whether DVFS is enabled. - */ -public class AsymptoticPowerModel( - private val maxPower: Double, - private val idlePower: Double, - private val asymUtil: Double, - private val isDvfsEnabled: Boolean -) : PowerModel { - private val factor: Double = (maxPower - idlePower) / 100 - - public override fun computePower(utilization: Double): Double = - if (isDvfsEnabled) { - idlePower + (factor * 100) / 2 * (1 + utilization.pow(3) - E.pow(-utilization.pow(3) / asymUtil)) - } else { - idlePower + (factor * 100) / 2 * (1 + utilization - E.pow(-utilization / asymUtil)) - } - - override fun toString(): String = "AsymptoticPowerModel[max=$maxPower,idle=$idlePower,asymptotic=$asymUtil]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/CubicPowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/CubicPowerModel.kt deleted file mode 100644 index 0d3bf6cc..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/CubicPowerModel.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.power - -import kotlin.math.pow - -/** - * The cubic power model partially adapted from CloudSim. - * - * @param maxPower The maximum power draw of the server in W. - * @param idlePower The power draw of the server at its lowest utilization level in W. - */ -public class CubicPowerModel(private val maxPower: Double, private val idlePower: Double) : PowerModel { - private val factor: Double = (maxPower - idlePower) / 100.0.pow(3) - - public override fun computePower(utilization: Double): Double { - return idlePower + factor * (utilization * 100).pow(3) - } - - override fun toString(): String = "CubicPowerModel[max=$maxPower,idle=$idlePower]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/InterpolationPowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/InterpolationPowerModel.kt deleted file mode 100644 index b17b87a9..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/InterpolationPowerModel.kt +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.power - -import kotlin.math.ceil -import kotlin.math.floor -import kotlin.math.max -import kotlin.math.min - -/** - * The linear interpolation power model partially adapted from CloudSim. - * This model is developed to adopt the <a href="http://www.spec.org/power_ssj2008/">SPECpower benchmark</a>. - * - * @param powerValues A [List] of average active power measured by the power analyzer(s) and accumulated by the - * PTDaemon (Power and Temperature Daemon) for this measurement interval, displayed as watts (W). - * @see <a href="http://www.spec.org/power_ssj2008/results/res2011q1/">Machines used in the SPEC benchmark</a> - */ -public class InterpolationPowerModel(private val powerValues: List<Double>) : PowerModel { - public override fun computePower(utilization: Double): Double { - val clampedUtilization = min(1.0, max(0.0, utilization)) - val utilizationFlr = floor(clampedUtilization * 10).toInt() - val utilizationCil = ceil(clampedUtilization * 10).toInt() - val powerFlr: Double = getAveragePowerValue(utilizationFlr) - val powerCil: Double = getAveragePowerValue(utilizationCil) - val delta = (powerCil - powerFlr) / 10 - - return if (utilization % 0.1 == 0.0) { - getAveragePowerValue((clampedUtilization * 10).toInt()) - } else { - powerFlr + delta * (clampedUtilization - utilizationFlr.toDouble() / 10) * 100 - } - } - - override fun toString(): String = "InterpolationPowerModel[entries=${powerValues.size}]" - - /** - * Gets the power consumption for a given utilization percentage. - * - * @param index the utilization percentage in the scale from [0 to 10], - * where 10 means 100% of utilization. - * @return the power consumption for the given utilization percentage - */ - private fun getAveragePowerValue(index: Int): Double = powerValues[index] -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/LinearPowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/LinearPowerModel.kt deleted file mode 100644 index dadc56ec..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/LinearPowerModel.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.power - -/** - * The linear power model partially adapted from CloudSim. - * - * @param maxPower The maximum power draw of the server in W. - * @param idlePower The power draw of the server at its lowest utilization level in W. - */ -public class LinearPowerModel(private val maxPower: Double, private val idlePower: Double) : PowerModel { - /** - * The linear interpolation factor of the model. - */ - private val factor: Double = (maxPower - idlePower) / 100 - - public override fun computePower(utilization: Double): Double { - return idlePower + factor * utilization * 100 - } - - override fun toString(): String = "LinearPowerModel[max=$maxPower,idle=$idlePower]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/MsePowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/MsePowerModel.kt deleted file mode 100644 index e9e72da8..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/MsePowerModel.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.power - -import kotlin.math.pow - -/** - * The power model that minimizes the mean squared error (MSE) - * to the actual power measurement by tuning the calibration parameter. - * @see <a href="https://dl.acm.org/doi/abs/10.1145/1273440.1250665"> - * Fan et al., Power provisioning for a warehouse-sized computer, ACM SIGARCH'07</a> - * - * @param maxPower The maximum power draw of the server in W. - * @param idlePower The power draw of the server at its lowest utilization level in W. - * @param calibrationParam The parameter set to minimize the MSE. - */ -public class MsePowerModel( - private val maxPower: Double, - private val idlePower: Double, - private val calibrationParam: Double -) : PowerModel { - private val factor: Double = (maxPower - idlePower) / 100 - - public override fun computePower(utilization: Double): Double { - return idlePower + factor * (2 * utilization - utilization.pow(calibrationParam)) * 100 - } - - override fun toString(): String = "MsePowerModel[max=$maxPower,idle=$idlePower,MSE_param=$calibrationParam]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PStatePowerDriver.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PStatePowerDriver.kt deleted file mode 100644 index ce7225d2..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PStatePowerDriver.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.power - -import org.opendc.simulator.compute.SimMachine -import org.opendc.simulator.compute.SimProcessingUnit -import java.util.TreeMap -import kotlin.math.max -import kotlin.math.min - -/** - * A [PowerDriver] that computes the power draw using multiple [PowerModel]s based on multiple frequency states. - * - * @param states A map describing the states of the driver. - */ -public class PStatePowerDriver(states: Map<Double, PowerModel>) : PowerDriver { - /** - * The P-States defined by the user and ordered by key. - */ - private val states: TreeMap<Double, PowerModel> = TreeMap(states) - - override fun createLogic(machine: SimMachine, cpus: List<SimProcessingUnit>): PowerDriver.Logic = object : PowerDriver.Logic { - override fun computePower(): Double { - var targetFreq = 0.0 - var totalSpeed = 0.0 - - for (cpu in cpus) { - targetFreq = max(cpu.capacity, targetFreq) - totalSpeed += cpu.rate - } - - val maxFreq = states.lastKey() - val (actualFreq, model) = states.ceilingEntry(min(maxFreq, targetFreq)) - val utilization = totalSpeed / (actualFreq * cpus.size) - return model.computePower(utilization) - } - } - - override fun toString(): String = "PStatePowerDriver[states=$states]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerDriver.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerDriver.kt deleted file mode 100644 index 1a46dd4a..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/PowerDriver.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.power - -import org.opendc.simulator.compute.SimMachine -import org.opendc.simulator.compute.SimProcessingUnit - -/** - * A [PowerDriver] is responsible for tracking the power usage for a component of the machine. - */ -public interface PowerDriver { - /** - * Create the driver logic for the specified [machine]. - */ - public fun createLogic(machine: SimMachine, cpus: List<SimProcessingUnit>): Logic - - /** - * The logic of the power driver. - */ - public interface Logic { - /** - * Compute the power consumption of the component. - * - * @return The power consumption of the component in W. - */ - public fun computePower(): Double - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SimplePowerDriver.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SimplePowerDriver.kt deleted file mode 100644 index 34e91c35..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SimplePowerDriver.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.power - -import org.opendc.simulator.compute.SimMachine -import org.opendc.simulator.compute.SimProcessingUnit - -/** - * A [PowerDriver] that computes the power consumption based on a single specified [power model][model]. - */ -public class SimplePowerDriver(private val model: PowerModel) : PowerDriver { - override fun createLogic(machine: SimMachine, cpus: List<SimProcessingUnit>): PowerDriver.Logic = object : PowerDriver.Logic { - - override fun computePower(): Double { - var targetFreq = 0.0 - var totalSpeed = 0.0 - - for (cpu in cpus) { - targetFreq += cpu.capacity - totalSpeed += cpu.rate - } - - return model.computePower(totalSpeed / targetFreq) - } - } - - override fun toString(): String = "SimplePowerDriver[model=$model]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SqrtPowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SqrtPowerModel.kt deleted file mode 100644 index 0665dbd9..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SqrtPowerModel.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.power - -import kotlin.math.sqrt - -/** - * The square root power model partially adapted from CloudSim. - * - * @param maxPower The maximum power draw of the server in W. - * @param idlePower The power draw of the server at its lowest utilization level in W. - */ -public class SqrtPowerModel(private val maxPower: Double, private val idlePower: Double) : PowerModel { - private val factor: Double = (maxPower - idlePower) / sqrt(100.0) - - override fun computePower(utilization: Double): Double { - return idlePower + factor * sqrt(utilization * 100) - } - - override fun toString(): String = "SqrtPowerModel[max=$maxPower,idle=$idlePower]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SquarePowerModel.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SquarePowerModel.kt deleted file mode 100644 index e4ae88a9..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/SquarePowerModel.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.power - -import kotlin.math.pow - -/** - * The square power model partially adapted from CloudSim. - * - * @param maxPower The maximum power draw of the server in W. - * @param idlePower The power draw of the server at its lowest utilization level in W. - */ -public class SquarePowerModel(private val maxPower: Double, private val idlePower: Double) : PowerModel { - private val factor: Double = (maxPower - idlePower) / 100.0.pow(2) - - override fun computePower(utilization: Double): Double { - return idlePower + factor * (utilization * 100).pow(2) - } - - override fun toString(): String = "SquarePowerModel[max=$maxPower,idle=$idlePower]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ZeroIdlePowerDecorator.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ZeroIdlePowerDecorator.kt deleted file mode 100644 index 05ab4631..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/power/ZeroIdlePowerDecorator.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.power - -/** - * A decorator for ignoring the idle power when computing energy consumption of components. - * - * @param delegate The [PowerModel] to delegate to. - */ -public class ZeroIdlePowerDecorator(private val delegate: PowerModel) : PowerModel { - override fun computePower(utilization: Double): Double { - return if (utilization == 0.0) { - 0.0 - } else { - delegate.computePower(utilization) - } - } - - override fun toString(): String = "ZeroIdlePowerDecorator[delegate=$delegate]" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkload.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkload.kt deleted file mode 100644 index 726d1f56..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkload.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2020 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.workload - -import org.opendc.simulator.compute.SimMachineContext -import org.opendc.simulator.flow.source.FixedFlowSource - -/** - * A [SimWorkload] that models applications as a static number of floating point operations ([flops]) executed on - * multiple cores of a compute resource. - * - * @property flops The number of floating point operations to perform for this task in MFLOPs. - * @property utilization A model of the CPU utilization of the application. - */ -public class SimFlopsWorkload( - public val flops: Long, - public val utilization: Double = 0.8 -) : SimWorkload { - init { - require(flops >= 0) { "Number of FLOPs must be positive" } - require(utilization > 0.0 && utilization <= 1.0) { "Utilization must be in (0, 1]" } - } - - override fun onStart(ctx: SimMachineContext) { - val lifecycle = SimWorkloadLifecycle(ctx) - for (cpu in ctx.cpus) { - cpu.startConsumer(lifecycle.waitFor(FixedFlowSource(flops.toDouble() / ctx.cpus.size, utilization))) - } - } - - override fun onStop(ctx: SimMachineContext) {} - - override fun toString(): String = "SimFlopsWorkload(FLOPs=$flops,utilization=$utilization)" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimRuntimeWorkload.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimRuntimeWorkload.kt deleted file mode 100644 index 8a3f5f84..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimRuntimeWorkload.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2020 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.workload - -import org.opendc.simulator.compute.SimMachineContext -import org.opendc.simulator.flow.source.FixedFlowSource - -/** - * A [SimWorkload] that models application execution as a single duration. - * - * @property duration The duration of the workload. - * @property utilization The utilization of the application during runtime. - */ -public class SimRuntimeWorkload( - public val duration: Long, - public val utilization: Double = 0.8 -) : SimWorkload { - init { - require(duration >= 0) { "Duration must be non-negative" } - require(utilization > 0.0 && utilization <= 1.0) { "Utilization must be in (0, 1]" } - } - - override fun onStart(ctx: SimMachineContext) { - val lifecycle = SimWorkloadLifecycle(ctx) - for (cpu in ctx.cpus) { - val limit = cpu.capacity * utilization - cpu.startConsumer(lifecycle.waitFor(FixedFlowSource((limit / 1000) * duration, utilization))) - } - } - - override fun onStop(ctx: SimMachineContext) {} - - override fun toString(): String = "SimRuntimeWorkload(duration=$duration,utilization=$utilization)" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTrace.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTrace.kt deleted file mode 100644 index db6a4629..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTrace.kt +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.workload - -import org.opendc.simulator.compute.model.ProcessingUnit -import org.opendc.simulator.flow.FlowConnection -import org.opendc.simulator.flow.FlowSource -import kotlin.math.min - -/** - * A workload trace that describes the resource utilization over time in a collection of [SimTraceFragment]s. - * - * @param usageCol The column containing the CPU usage of each fragment (in MHz). - * @param deadlineCol The column containing the ending timestamp for each fragment (in epoch millis). - * @param coresCol The column containing the utilized cores. - * @param size The number of fragments in the trace. - */ -public class SimTrace( - private val usageCol: DoubleArray, - private val deadlineCol: LongArray, - private val coresCol: IntArray, - private val size: Int -) { - init { - require(size >= 0) { "Invalid trace size" } - require(usageCol.size >= size) { "Invalid number of usage entries" } - require(deadlineCol.size >= size) { "Invalid number of deadline entries" } - require(coresCol.size >= size) { "Invalid number of core entries" } - } - - public companion object { - /** - * Construct a [SimTrace] with the specified fragments. - */ - @JvmStatic - public fun ofFragments(fragments: List<SimTraceFragment>): SimTrace { - val size = fragments.size - val usageCol = DoubleArray(size) - val deadlineCol = LongArray(size) - val coresCol = IntArray(size) - - for (i in fragments.indices) { - val fragment = fragments[i] - usageCol[i] = fragment.usage - deadlineCol[i] = fragment.timestamp + fragment.duration - coresCol[i] = fragment.cores - } - - return SimTrace(usageCol, deadlineCol, coresCol, size) - } - - /** - * Construct a [SimTrace] with the specified fragments. - */ - @JvmStatic - public fun ofFragments(vararg fragments: SimTraceFragment): SimTrace { - val size = fragments.size - val usageCol = DoubleArray(size) - val deadlineCol = LongArray(size) - val coresCol = IntArray(size) - - for (i in fragments.indices) { - val fragment = fragments[i] - usageCol[i] = fragment.usage - deadlineCol[i] = fragment.timestamp + fragment.duration - coresCol[i] = fragment.cores - } - - return SimTrace(usageCol, deadlineCol, coresCol, size) - } - - /** - * Create a [SimTrace.Builder] instance. - */ - @JvmStatic - public fun builder(): Builder = Builder() - } - - /** - * Construct a new [FlowSource] for the specified [cpu]. - * - * @param cpu The [ProcessingUnit] for which to create the source. - * @param offset The time offset to use for the trace. - */ - public fun newSource(cpu: ProcessingUnit, offset: Long): FlowSource { - return CpuConsumer(cpu, offset, usageCol, deadlineCol, coresCol, size) - } - - /** - * A builder class for a [SimTrace]. - */ - public class Builder internal constructor() { - /** - * The columns of the trace. - */ - private var usageCol: DoubleArray = DoubleArray(16) - private var deadlineCol: LongArray = LongArray(16) - private var coresCol: IntArray = IntArray(16) - - /** - * The number of entries in the trace. - */ - private var size = 0 - - /** - * Add the specified [SimTraceFragment] to the trace. - */ - public fun add(fragment: SimTraceFragment) { - add(fragment.timestamp + fragment.duration, fragment.usage, fragment.cores) - } - - /** - * Add a fragment to the trace. - * - * @param deadline Timestamp at which the fragment ends (in epoch millis). - * @param usage CPU usage of this fragment. - * @param cores Number of cores used. - */ - public fun add(deadline: Long, usage: Double, cores: Int) { - val size = size - - if (size == usageCol.size) { - grow() - } - - deadlineCol[size] = deadline - usageCol[size] = usage - coresCol[size] = cores - - this.size++ - } - - /** - * Helper function to grow the capacity of the column arrays. - */ - private fun grow() { - val arraySize = usageCol.size - val newSize = arraySize + (arraySize shr 1) - - usageCol = usageCol.copyOf(newSize) - deadlineCol = deadlineCol.copyOf(newSize) - coresCol = coresCol.copyOf(newSize) - } - - /** - * Construct the immutable [SimTrace]. - */ - public fun build(): SimTrace { - return SimTrace(usageCol, deadlineCol, coresCol, size) - } - } - - /** - * A CPU consumer for the trace workload. - */ - private class CpuConsumer( - cpu: ProcessingUnit, - private val offset: Long, - private val usageCol: DoubleArray, - private val deadlineCol: LongArray, - private val coresCol: IntArray, - private val size: Int - ) : FlowSource { - private val id = cpu.id - private val coreCount = cpu.node.coreCount - - /** - * The index in the trace. - */ - private var _idx = 0 - - override fun onPull(conn: FlowConnection, now: Long): Long { - val size = size - val nowOffset = now - offset - - var idx = _idx - val deadlines = deadlineCol - var deadline = deadlines[idx] - - while (deadline <= nowOffset && ++idx < size) { - deadline = deadlines[idx] - } - - if (idx >= size) { - conn.close() - return Long.MAX_VALUE - } - - _idx = idx - - val cores = min(coreCount, coresCol[idx]) - val usage = usageCol[idx] - - conn.push(if (id < cores) usage / cores else 0.0) - return deadline - nowOffset - } - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceFragment.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceFragment.kt deleted file mode 100644 index 5285847f..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceFragment.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.workload - -/** - * A fragment of the workload trace. - * - * @param timestamp The timestamp at which the fragment starts (in epoch millis). - * @param duration The duration of the fragment (in milliseconds). - * @param usage The CPU usage during the fragment (in MHz). - * @param cores The amount of cores utilized during the fragment. - */ -public data class SimTraceFragment( - @JvmField val timestamp: Long, - @JvmField val duration: Long, - @JvmField val usage: Double, - @JvmField val cores: Int -) diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt deleted file mode 100644 index ce04a790..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkload.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2020 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.workload - -import org.opendc.simulator.compute.SimMachineContext - -/** - * A [SimWorkload] that replays a workload trace consisting of multiple fragments, each indicating the resource - * consumption for some period of time. - * - * @param trace The trace of fragments to use. - * @param offset The offset for the timestamps. - */ -public class SimTraceWorkload(private val trace: SimTrace, private val offset: Long = 0L) : SimWorkload { - override fun onStart(ctx: SimMachineContext) { - val lifecycle = SimWorkloadLifecycle(ctx) - - for (cpu in ctx.cpus) { - cpu.startConsumer(lifecycle.waitFor(trace.newSource(cpu.model, offset))) - } - } - - override fun onStop(ctx: SimMachineContext) {} - - override fun toString(): String = "SimTraceWorkload" -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkloadLifecycle.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkloadLifecycle.kt deleted file mode 100644 index 46113bb0..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/workload/SimWorkloadLifecycle.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.workload - -import org.opendc.simulator.compute.SimMachineContext -import org.opendc.simulator.flow.FlowConnection -import org.opendc.simulator.flow.FlowSource - -/** - * A helper class to manage the lifecycle of a [SimWorkload] - */ -public class SimWorkloadLifecycle(private val ctx: SimMachineContext) { - /** - * The resource consumers which represent the lifecycle of the workload. - */ - private val waiting = HashSet<Wrapper>() - - /** - * Wait for the specified [source] to complete before ending the lifecycle of the workload. - */ - public fun waitFor(source: FlowSource): FlowSource { - val wrapper = Wrapper(source) - waiting.add(wrapper) - return wrapper - } - - /** - * Complete the specified [Wrapper]. - */ - private fun complete(wrapper: Wrapper) { - if (waiting.remove(wrapper) && waiting.isEmpty()) { - ctx.close() - } - } - - /** - * A [FlowSource] that wraps [delegate] and informs [SimWorkloadLifecycle] that is has completed. - */ - private inner class Wrapper(private val delegate: FlowSource) : FlowSource { - override fun onStart(conn: FlowConnection, now: Long) { - delegate.onStart(conn, now) - } - - override fun onPull(conn: FlowConnection, now: Long): Long { - return delegate.onPull(conn, now) - } - - override fun onConverge(conn: FlowConnection, now: Long) { - delegate.onConverge(conn, now) - } - - override fun onStop(conn: FlowConnection, now: Long) { - try { - delegate.onStop(conn, now) - } finally { - complete(this) - } - } - - override fun toString(): String = "SimWorkloadLifecycle.Wrapper[delegate=$delegate]" - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt index 1eddf82c..f0aae15b 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt @@ -26,6 +26,7 @@ import kotlinx.coroutines.CancellationException import kotlinx.coroutines.cancel import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch +import kotlinx.coroutines.yield import org.junit.jupiter.api.Assertions.assertAll import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach @@ -38,17 +39,16 @@ import org.opendc.simulator.compute.model.NetworkAdapter import org.opendc.simulator.compute.model.ProcessingNode import org.opendc.simulator.compute.model.ProcessingUnit import org.opendc.simulator.compute.model.StorageDevice -import org.opendc.simulator.compute.power.ConstantPowerModel -import org.opendc.simulator.compute.power.LinearPowerModel -import org.opendc.simulator.compute.power.SimplePowerDriver +import org.opendc.simulator.compute.power.CpuPowerModels import org.opendc.simulator.compute.workload.SimFlopsWorkload +import org.opendc.simulator.compute.workload.SimTrace import org.opendc.simulator.compute.workload.SimWorkload -import org.opendc.simulator.compute.workload.SimWorkloadLifecycle -import org.opendc.simulator.flow.FlowEngine -import org.opendc.simulator.flow.source.FixedFlowSource +import org.opendc.simulator.flow2.FlowEngine +import org.opendc.simulator.flow2.source.SimpleFlowSource import org.opendc.simulator.kotlin.runSimulation import org.opendc.simulator.network.SimNetworkSink import org.opendc.simulator.power.SimPowerSource +import java.util.concurrent.ThreadLocalRandom /** * Test suite for the [SimBareMetalMachine] class. @@ -61,41 +61,69 @@ class SimMachineTest { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) machineModel = MachineModel( - cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, - memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }, - net = listOf(NetworkAdapter("Mellanox", "ConnectX-5", 25000.0)), - storage = listOf(StorageDevice("Samsung", "EVO", 1000.0, 250.0, 250.0)) + /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, + /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }, + /*net*/ listOf(NetworkAdapter("Mellanox", "ConnectX-5", 25000.0)), + /*storage*/ listOf(StorageDevice("Samsung", "EVO", 1000.0, 250.0, 250.0)) ) } @Test fun testFlopsWorkload() = runSimulation { - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) - machine.runWorkload(SimFlopsWorkload(2_000, utilization = 1.0)) + machine.runWorkload(SimFlopsWorkload(2_000, /*utilization*/ 1.0)) // Two cores execute 1000 MFlOps per second (1000 ms) assertEquals(1000, clock.millis()) } @Test + fun testTraceWorkload() = runSimulation { + val random = ThreadLocalRandom.current() + val builder = SimTrace.builder() + repeat(1000000) { + val timestamp = it.toLong() * 1000 + val deadline = timestamp + 1000 + builder.add(deadline, random.nextDouble(0.0, 4500.0), 1) + } + val trace = builder.build() + + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + val machine = SimBareMetalMachine.create( + graph, + machineModel + ) + + machine.runWorkload(trace.createWorkload(0)) + + // Two cores execute 1000 MFlOps per second (1000 ms) + assertEquals(1000000000, clock.millis()) + } + + @Test fun testDualSocketMachine() = runSimulation { + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + val cpuNode = machineModel.cpus[0].node val machineModel = MachineModel( - cpus = List(cpuNode.coreCount * 2) { ProcessingUnit(cpuNode, it % 2, 1000.0) }, - memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + /*cpus*/ List(cpuNode.coreCount * 2) { ProcessingUnit(cpuNode, it % 2, 1000.0) }, + /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } ) - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val machine = SimBareMetalMachine.create( + graph, + machineModel ) - machine.runWorkload(SimFlopsWorkload(2_000, utilization = 1.0)) + machine.runWorkload(SimFlopsWorkload(2_000, /*utilization*/ 1.0)) // Two sockets with two cores execute 2000 MFlOps per second (500 ms) assertEquals(500, clock.millis()) @@ -103,42 +131,47 @@ class SimMachineTest { @Test fun testPower() = runSimulation { - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine( - engine, + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + val machine = SimBareMetalMachine.create( + graph, machineModel, - SimplePowerDriver(LinearPowerModel(100.0, 50.0)) + SimPsuFactories.simple(CpuPowerModels.linear(100.0, 50.0)) ) - val source = SimPowerSource(engine, capacity = 1000.0) + val source = SimPowerSource(graph, /*capacity*/ 1000.0f) source.connect(machine.psu) coroutineScope { - launch { machine.runWorkload(SimFlopsWorkload(2_000, utilization = 1.0)) } + launch { machine.runWorkload(SimFlopsWorkload(2_000, /*utilization*/ 1.0)) } + + yield() assertAll( - { assertEquals(100.0, machine.psu.powerDraw) }, - { assertEquals(100.0, source.powerDraw) } + { assertEquals(100.0, machine.psu.powerUsage) }, + { assertEquals(100.0f, source.powerDraw) } ) } } @Test fun testCapacityClamp() = runSimulation { - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) machine.runWorkload(object : SimWorkload { override fun onStart(ctx: SimMachineContext) { val cpu = ctx.cpus[0] - cpu.capacity = cpu.model.frequency + 1000.0 - assertEquals(cpu.model.frequency, cpu.capacity) - cpu.capacity = -1.0 - assertEquals(0.0, cpu.capacity) + cpu.frequency = (cpu.model.frequency + 1000.0) + assertEquals(cpu.model.frequency, cpu.frequency) + cpu.frequency = -1.0 + assertEquals(0.0, cpu.frequency) - ctx.close() + ctx.shutdown() } override fun onStop(ctx: SimMachineContext) {} @@ -147,16 +180,18 @@ class SimMachineTest { @Test fun testMemory() = runSimulation { - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) machine.runWorkload(object : SimWorkload { override fun onStart(ctx: SimMachineContext) { assertEquals(32_000 * 4.0, ctx.memory.capacity) - ctx.close() + ctx.shutdown() } override fun onStop(ctx: SimMachineContext) {} @@ -165,104 +200,111 @@ class SimMachineTest { @Test fun testMemoryUsage() = runSimulation { - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) machine.runWorkload(object : SimWorkload { override fun onStart(ctx: SimMachineContext) { - val lifecycle = SimWorkloadLifecycle(ctx) - ctx.memory.startConsumer(lifecycle.waitFor(FixedFlowSource(ctx.memory.capacity, utilization = 0.8))) + val source = SimpleFlowSource(ctx.graph, ctx.memory.capacity.toFloat(), 1.0f) { ctx.shutdown() } + ctx.graph.connect(source.output, ctx.memory.input) } override fun onStop(ctx: SimMachineContext) {} }) - assertEquals(1250, clock.millis()) + assertEquals(1000, clock.millis()) } @Test fun testNetUsage() = runSimulation { - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine( - engine, - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) val adapter = (machine.peripherals[0] as SimNetworkAdapter) - adapter.connect(SimNetworkSink(engine, adapter.bandwidth)) + adapter.connect(SimNetworkSink(graph, adapter.bandwidth.toFloat())) machine.runWorkload(object : SimWorkload { override fun onStart(ctx: SimMachineContext) { - val lifecycle = SimWorkloadLifecycle(ctx) - val iface = ctx.net[0] - iface.tx.startConsumer(lifecycle.waitFor(FixedFlowSource(iface.bandwidth, utilization = 0.8))) + val iface = ctx.networkInterfaces[0] + val source = SimpleFlowSource(ctx.graph, 800.0f, 0.8f) { ctx.shutdown(); it.close(); } + ctx.graph.connect(source.output, iface.tx) } override fun onStop(ctx: SimMachineContext) {} }) - assertEquals(1250, clock.millis()) + assertEquals(40, clock.millis()) } @Test fun testDiskReadUsage() = runSimulation { - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine( - engine, - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) machine.runWorkload(object : SimWorkload { override fun onStart(ctx: SimMachineContext) { - val lifecycle = SimWorkloadLifecycle(ctx) - val disk = ctx.storage[0] - disk.read.startConsumer(lifecycle.waitFor(FixedFlowSource(disk.read.capacity, utilization = 0.8))) + val disk = ctx.storageInterfaces[0] + val source = SimpleFlowSource(ctx.graph, 800.0f, 0.8f) { ctx.shutdown() } + ctx.graph.connect(source.output, disk.read) } override fun onStop(ctx: SimMachineContext) {} }) - assertEquals(1250, clock.millis()) + assertEquals(4000, clock.millis()) } @Test fun testDiskWriteUsage() = runSimulation { - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine( - engine, - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) machine.runWorkload(object : SimWorkload { override fun onStart(ctx: SimMachineContext) { - val lifecycle = SimWorkloadLifecycle(ctx) - val disk = ctx.storage[0] - disk.write.startConsumer(lifecycle.waitFor(FixedFlowSource(disk.write.capacity, utilization = 0.8))) + val disk = ctx.storageInterfaces[0] + val source = SimpleFlowSource(ctx.graph, 800.0f, 0.8f) { ctx.shutdown() } + ctx.graph.connect(source.output, disk.write) } override fun onStop(ctx: SimMachineContext) {} }) - assertEquals(1250, clock.millis()) + assertEquals(4000, clock.millis()) } @Test fun testCancellation() = runSimulation { - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) try { coroutineScope { - launch { machine.runWorkload(SimFlopsWorkload(2_000, utilization = 1.0)) } + launch { machine.runWorkload(SimFlopsWorkload(2_000, /*utilization*/ 1.0)) } cancel() } } catch (_: CancellationException) { @@ -274,19 +316,21 @@ class SimMachineTest { @Test fun testConcurrentRuns() = runSimulation { - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) coroutineScope { launch { - machine.runWorkload(SimFlopsWorkload(2_000, utilization = 1.0)) + machine.runWorkload(SimFlopsWorkload(2_000, /*utilization*/ 1.0)) } assertThrows<IllegalStateException> { - machine.runWorkload(SimFlopsWorkload(2_000, utilization = 1.0)) + machine.runWorkload(SimFlopsWorkload(2_000, /*utilization*/ 1.0)) } } } diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/device/SimPsuTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/device/SimPsuTest.kt deleted file mode 100644 index 0a6cb29f..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/device/SimPsuTest.kt +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.device - -import io.mockk.every -import io.mockk.mockk -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.opendc.simulator.compute.power.PowerDriver -import org.opendc.simulator.flow.FlowEngine -import org.opendc.simulator.kotlin.runSimulation -import org.opendc.simulator.power.SimPowerSource - -/** - * Test suite for [SimPsu] - */ -internal class SimPsuTest { - - @Test - fun testInvalidInput() { - assertThrows<IllegalArgumentException> { SimPsu(1.0, emptyMap()) } - } - - @Test - fun testDoubleConnect() { - val psu = SimPsu(1.0, mapOf(0.0 to 1.0)) - val cpuLogic = mockk<PowerDriver.Logic>() - psu.connect(cpuLogic) - assertThrows<IllegalStateException> { psu.connect(mockk()) } - } - - @Test - fun testPsuIdle() = runSimulation { - val ratedOutputPower = 240.0 - val energyEfficiency = mapOf(0.0 to 1.0) - - val engine = FlowEngine(coroutineContext, clock) - val source = SimPowerSource(engine, capacity = ratedOutputPower) - - val cpuLogic = mockk<PowerDriver.Logic>() - every { cpuLogic.computePower() } returns 0.0 - - val psu = SimPsu(ratedOutputPower, energyEfficiency) - psu.connect(cpuLogic) - source.connect(psu) - - assertEquals(0.0, source.powerDraw, 0.01) - } - - @Test - fun testPsuPowerLoss() = runSimulation { - val ratedOutputPower = 240.0 - // Efficiency of 80 Plus Titanium PSU - val energyEfficiency = sortedMapOf( - 0.3 to 0.9, - 0.7 to 0.92, - 1.0 to 0.94 - ) - - val engine = FlowEngine(coroutineContext, clock) - val source = SimPowerSource(engine, capacity = ratedOutputPower) - - val cpuLogic = mockk<PowerDriver.Logic>() - every { cpuLogic.computePower() } returnsMany listOf(50.0, 100.0, 150.0, 200.0) - - val psu = SimPsu(ratedOutputPower, energyEfficiency) - psu.connect(cpuLogic) - source.connect(psu) - - assertEquals(55.55, source.powerDraw, 0.01) - - psu.update() - assertEquals(108.695, source.powerDraw, 0.01) - - psu.update() - assertEquals(163.043, source.powerDraw, 0.01) - - psu.update() - assertEquals(212.765, source.powerDraw, 0.01) - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt index 6b498119..79669d40 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt @@ -31,20 +31,17 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertAll import org.junit.jupiter.api.assertDoesNotThrow import org.opendc.simulator.compute.SimBareMetalMachine -import org.opendc.simulator.compute.kernel.cpufreq.PerformanceScalingGovernor +import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernors import org.opendc.simulator.compute.kernel.interference.VmInterferenceModel import org.opendc.simulator.compute.model.MachineModel import org.opendc.simulator.compute.model.MemoryUnit import org.opendc.simulator.compute.model.ProcessingNode import org.opendc.simulator.compute.model.ProcessingUnit -import org.opendc.simulator.compute.power.ConstantPowerModel -import org.opendc.simulator.compute.power.SimplePowerDriver import org.opendc.simulator.compute.runWorkload import org.opendc.simulator.compute.workload.SimTrace import org.opendc.simulator.compute.workload.SimTraceFragment -import org.opendc.simulator.compute.workload.SimTraceWorkload -import org.opendc.simulator.flow.FlowEngine -import org.opendc.simulator.flow.mux.FlowMultiplexerFactory +import org.opendc.simulator.flow2.FlowEngine +import org.opendc.simulator.flow2.mux.FlowMultiplexerFactory import org.opendc.simulator.kotlin.runSimulation import java.util.SplittableRandom @@ -58,8 +55,8 @@ internal class SimFairShareHypervisorTest { fun setUp() { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 1) model = MachineModel( - cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, - memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, + /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } ) } @@ -70,23 +67,20 @@ internal class SimFairShareHypervisorTest { fun testOvercommittedSingle() = runSimulation { val duration = 5 * 60L val workloadA = - SimTraceWorkload( - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 28.0, 1), - SimTraceFragment(duration * 1000, duration * 1000, 3500.0, 1), - SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), - SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1) - ) - ) - - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, model, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1), PerformanceScalingGovernor()) + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 28.0, 1), + SimTraceFragment(duration * 1000, duration * 1000, 3500.0, 1), + SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), + SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1) + ).createWorkload(0) - launch { - machine.runWorkload(hypervisor) - println("Hypervisor finished") - } + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, model) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(0L), ScalingGovernors.performance()) + + launch { machine.runWorkload(hypervisor) } yield() val vm = hypervisor.newMachine(model) @@ -110,31 +104,27 @@ internal class SimFairShareHypervisorTest { fun testOvercommittedDual() = runSimulation { val duration = 5 * 60L val workloadA = - SimTraceWorkload( - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 28.0, 1), - SimTraceFragment(duration * 1000, duration * 1000, 3500.0, 1), - SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), - SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1) - ) - ) + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 28.0, 1), + SimTraceFragment(duration * 1000, duration * 1000, 3500.0, 1), + SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), + SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1) + ).createWorkload(0) val workloadB = - SimTraceWorkload( - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 28.0, 1), - SimTraceFragment(duration * 1000, duration * 1000, 3100.0, 1), - SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), - SimTraceFragment(duration * 3000, duration * 1000, 73.0, 1) - ) - ) - - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, model, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1), null) + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 28.0, 1), + SimTraceFragment(duration * 1000, duration * 1000, 3100.0, 1), + SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), + SimTraceFragment(duration * 3000, duration * 1000, 73.0, 1) + ).createWorkload(0) - launch { - machine.runWorkload(hypervisor) - } + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, model) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(0L), ScalingGovernors.performance()) + + launch { machine.runWorkload(hypervisor) } yield() coroutineScope { @@ -163,18 +153,18 @@ internal class SimFairShareHypervisorTest { fun testMultipleCPUs() = runSimulation { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) val model = MachineModel( - cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, - memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, + /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } ) - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, model, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1), null) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, model) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(0L), ScalingGovernors.performance()) assertDoesNotThrow { - launch { - machine.runWorkload(hypervisor) - } + launch { machine.runWorkload(hypervisor) } } machine.cancel() @@ -184,39 +174,37 @@ internal class SimFairShareHypervisorTest { fun testInterference() = runSimulation { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) val model = MachineModel( - cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, - memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, + /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } ) val interferenceModel = VmInterferenceModel.builder() - .addGroup(targetLoad = 0.0, score = 0.9, members = setOf("a", "b")) - .addGroup(targetLoad = 0.0, score = 0.6, members = setOf("a", "c")) - .addGroup(targetLoad = 0.1, score = 0.8, members = setOf("a", "n")) + .addGroup(setOf("a", "b"), 0.0, 0.9) + .addGroup(setOf("a", "c"), 0.0, 0.6) + .addGroup(setOf("a", "n"), 0.1, 0.8) .build() - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, model, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1), null) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, model) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(0L)) val duration = 5 * 60L val workloadA = - SimTraceWorkload( - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 0.0, 1), - SimTraceFragment(duration * 1000, duration * 1000, 28.0, 1), - SimTraceFragment(duration * 2000, duration * 1000, 3500.0, 1), - SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1) - ) - ) + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 0.0, 1), + SimTraceFragment(duration * 1000, duration * 1000, 28.0, 1), + SimTraceFragment(duration * 2000, duration * 1000, 3500.0, 1), + SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1) + ).createWorkload(0) val workloadB = - SimTraceWorkload( - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 0.0, 1), - SimTraceFragment(duration * 1000, duration * 1000, 28.0, 1), - SimTraceFragment(duration * 2000, duration * 1000, 3100.0, 1), - SimTraceFragment(duration * 3000, duration * 1000, 73.0, 1) - ) - ) + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 0.0, 1), + SimTraceFragment(duration * 1000, duration * 1000, 28.0, 1), + SimTraceFragment(duration * 2000, duration * 1000, 3100.0, 1), + SimTraceFragment(duration * 3000, duration * 1000, 73.0, 1) + ).createWorkload(0) launch { machine.runWorkload(hypervisor) diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt index 57fe3766..ba5a5c68 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt @@ -37,16 +37,13 @@ import org.opendc.simulator.compute.model.MachineModel import org.opendc.simulator.compute.model.MemoryUnit import org.opendc.simulator.compute.model.ProcessingNode import org.opendc.simulator.compute.model.ProcessingUnit -import org.opendc.simulator.compute.power.ConstantPowerModel -import org.opendc.simulator.compute.power.SimplePowerDriver import org.opendc.simulator.compute.runWorkload import org.opendc.simulator.compute.workload.SimFlopsWorkload import org.opendc.simulator.compute.workload.SimRuntimeWorkload import org.opendc.simulator.compute.workload.SimTrace import org.opendc.simulator.compute.workload.SimTraceFragment -import org.opendc.simulator.compute.workload.SimTraceWorkload -import org.opendc.simulator.flow.FlowEngine -import org.opendc.simulator.flow.mux.FlowMultiplexerFactory +import org.opendc.simulator.flow2.FlowEngine +import org.opendc.simulator.flow2.mux.FlowMultiplexerFactory import org.opendc.simulator.kotlin.runSimulation import java.util.SplittableRandom @@ -60,8 +57,8 @@ internal class SimSpaceSharedHypervisorTest { fun setUp() { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 1) machineModel = MachineModel( - cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, - memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 3200.0) }, + /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } ) } @@ -72,18 +69,18 @@ internal class SimSpaceSharedHypervisorTest { fun testTrace() = runSimulation { val duration = 5 * 60L val workloadA = - SimTraceWorkload( - SimTrace.ofFragments( - SimTraceFragment(0, duration * 1000, 28.0, 1), - SimTraceFragment(duration * 1000, duration * 1000, 3500.0, 1), - SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), - SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1) - ) - ) - - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(1), null) + SimTrace.ofFragments( + SimTraceFragment(0, duration * 1000, 28.0, 1), + SimTraceFragment(duration * 1000, duration * 1000, 3500.0, 1), + SimTraceFragment(duration * 2000, duration * 1000, 0.0, 1), + SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1) + ).createWorkload(0) + + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) launch { machine.runWorkload(hypervisor) } val vm = hypervisor.newMachine(machineModel) @@ -102,10 +99,12 @@ internal class SimSpaceSharedHypervisorTest { @Test fun testRuntimeWorkload() = runSimulation { val duration = 5 * 60L * 1000 - val workload = SimRuntimeWorkload(duration) - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(1), null) + val workload = SimRuntimeWorkload(duration, 1.0) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) launch { machine.runWorkload(hypervisor) } yield() @@ -125,9 +124,11 @@ internal class SimSpaceSharedHypervisorTest { fun testFlopsWorkload() = runSimulation { val duration = 5 * 60L * 1000 val workload = SimFlopsWorkload((duration * 3.2).toLong(), 1.0) - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(1), null) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) launch { machine.runWorkload(hypervisor) } yield() @@ -144,21 +145,23 @@ internal class SimSpaceSharedHypervisorTest { @Test fun testTwoWorkloads() = runSimulation { val duration = 5 * 60L * 1000 - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(1), null) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) launch { machine.runWorkload(hypervisor) } yield() val vm = hypervisor.newMachine(machineModel) - vm.runWorkload(SimRuntimeWorkload(duration)) + vm.runWorkload(SimRuntimeWorkload(duration, 1.0)) hypervisor.removeMachine(vm) yield() val vm2 = hypervisor.newMachine(machineModel) - vm2.runWorkload(SimRuntimeWorkload(duration)) + vm2.runWorkload(SimRuntimeWorkload(duration, 1.0)) hypervisor.removeMachine(vm2) machine.cancel() @@ -171,14 +174,18 @@ internal class SimSpaceSharedHypervisorTest { */ @Test fun testConcurrentWorkloadFails() = runSimulation { - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(1), null) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) launch { machine.runWorkload(hypervisor) } yield() - hypervisor.newMachine(machineModel) + val vm = hypervisor.newMachine(machineModel) + launch { vm.runWorkload(SimFlopsWorkload(10_000, 1.0)) } + yield() assertAll( { assertFalse(hypervisor.canFit(machineModel)) }, @@ -186,6 +193,7 @@ internal class SimSpaceSharedHypervisorTest { ) machine.cancel() + vm.cancel() } /** @@ -193,9 +201,11 @@ internal class SimSpaceSharedHypervisorTest { */ @Test fun testConcurrentWorkloadSucceeds() = runSimulation { - val engine = FlowEngine(coroutineContext, clock) - val machine = SimBareMetalMachine(engine, machineModel, SimplePowerDriver(ConstantPowerModel(0.0))) - val hypervisor = SimHypervisor(engine, FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(1), null) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create(graph, machineModel) + val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(0L)) launch { machine.runWorkload(hypervisor) } yield() diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernorTest.kt index ef354569..6b182f4c 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernorTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/ConservativeScalingGovernorTest.kt @@ -25,11 +25,10 @@ package org.opendc.simulator.compute.kernel.cpufreq import io.mockk.every import io.mockk.mockk import io.mockk.verify -import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test /** - * Test suite for the [ConservativeScalingGovernor] + * Test suite for the conservative [ScalingGovernor]. */ internal class ConservativeScalingGovernorTest { @Test @@ -38,7 +37,7 @@ internal class ConservativeScalingGovernorTest { val minSpeed = cpuCapacity / 2 val defaultThreshold = 0.8 val defaultStepSize = 0.05 * cpuCapacity - val governor = ConservativeScalingGovernor() + val governor = ScalingGovernors.conservative(defaultThreshold) val policy = mockk<ScalingPolicy>(relaxUnitFun = true) every { policy.max } returns cpuCapacity @@ -48,10 +47,8 @@ internal class ConservativeScalingGovernorTest { every { policy.target } answers { target } every { policy.target = any() } propertyType Double::class answers { target = value } - val logic = governor.createLogic(policy) + val logic = governor.newGovernor(policy) logic.onStart() - assertEquals(defaultThreshold, governor.threshold) - logic.onLimit(0.5) /* Upwards scaling */ @@ -71,7 +68,7 @@ internal class ConservativeScalingGovernorTest { val minSpeed = firstPState val threshold = 0.5 val stepSize = 0.02 * cpuCapacity - val governor = ConservativeScalingGovernor(threshold, stepSize) + val governor = ScalingGovernors.conservative(threshold, stepSize) val policy = mockk<ScalingPolicy>(relaxUnitFun = true) every { policy.max } returns cpuCapacity @@ -81,9 +78,8 @@ internal class ConservativeScalingGovernorTest { every { policy.target } answers { target } every { policy.target = any() } propertyType Double::class answers { target = value } - val logic = governor.createLogic(policy) + val logic = governor.newGovernor(policy) logic.onStart() - assertEquals(threshold, governor.threshold) logic.onLimit(0.5) /* Upwards scaling */ diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernorTest.kt index ca759e39..d6a7090b 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernorTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/OnDemandScalingGovernorTest.kt @@ -25,11 +25,10 @@ package org.opendc.simulator.compute.kernel.cpufreq import io.mockk.every import io.mockk.mockk import io.mockk.verify -import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test /** - * Test suite for the [OnDemandScalingGovernor] + * Test suite for the on-demand [ScalingGovernor]. */ internal class OnDemandScalingGovernorTest { @Test @@ -37,15 +36,14 @@ internal class OnDemandScalingGovernorTest { val cpuCapacity = 4100.0 val minSpeed = cpuCapacity / 2 val defaultThreshold = 0.8 - val governor = OnDemandScalingGovernor() + val governor = ScalingGovernors.ondemand(defaultThreshold) val policy = mockk<ScalingPolicy>(relaxUnitFun = true) every { policy.min } returns minSpeed every { policy.max } returns cpuCapacity - val logic = governor.createLogic(policy) + val logic = governor.newGovernor(policy) logic.onStart() - assertEquals(defaultThreshold, governor.threshold) verify(exactly = 1) { policy.target = minSpeed } logic.onLimit(0.5) @@ -60,16 +58,15 @@ internal class OnDemandScalingGovernorTest { val firstPState = 1000.0 val cpuCapacity = 4100.0 val threshold = 0.5 - val governor = OnDemandScalingGovernor(threshold) + val governor = ScalingGovernors.ondemand(threshold) val policy = mockk<ScalingPolicy>(relaxUnitFun = true) every { policy.max } returns cpuCapacity every { policy.min } returns firstPState - val logic = governor.createLogic(policy) + val logic = governor.newGovernor(policy) logic.onStart() - assertEquals(threshold, governor.threshold) verify(exactly = 1) { policy.target = firstPState } logic.onLimit(0.1) diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernorTest.kt index a4bb24f2..f03f41fe 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernorTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PerformanceScalingGovernorTest.kt @@ -34,7 +34,7 @@ internal class PerformanceScalingGovernorTest { @Test fun testSetStartLimit() { val policy = spyk<ScalingPolicy>() - val logic = PerformanceScalingGovernor().createLogic(policy) + val logic = ScalingGovernors.performance().newGovernor(policy) every { policy.max } returns 4100.0 diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernorTest.kt index 662d55fb..4cee8199 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernorTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/cpufreq/PowerSaveScalingGovernorTest.kt @@ -36,7 +36,7 @@ internal class PowerSaveScalingGovernorTest { val cpuCapacity = 4100.0 val minSpeed = cpuCapacity / 2 val policy = mockk<ScalingPolicy>(relaxUnitFun = true) - val logic = PowerSaveScalingGovernor().createLogic(policy) + val logic = ScalingGovernors.powerSave().newGovernor(policy) every { policy.max } returns cpuCapacity every { policy.min } returns minSpeed @@ -55,7 +55,7 @@ internal class PowerSaveScalingGovernorTest { val cpuCapacity = 4100.0 val firstPState = 1000.0 val policy = mockk<ScalingPolicy>(relaxUnitFun = true) - val logic = PowerSaveScalingGovernor().createLogic(policy) + val logic = ScalingGovernors.powerSave().newGovernor(policy) every { policy.max } returns cpuCapacity every { policy.min } returns firstPState diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PStatePowerDriverTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PStatePowerDriverTest.kt deleted file mode 100644 index 3c0a55a6..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PStatePowerDriverTest.kt +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.power - -import io.mockk.every -import io.mockk.mockk -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.opendc.simulator.compute.SimBareMetalMachine -import org.opendc.simulator.compute.SimProcessingUnit - -/** - * Test suite for [PStatePowerDriver]. - */ -internal class PStatePowerDriverTest { - @Test - fun testPowerBaseline() { - val machine = mockk<SimBareMetalMachine>() - - val driver = PStatePowerDriver( - sortedMapOf( - 2800.0 to ConstantPowerModel(200.0), - 3300.0 to ConstantPowerModel(300.0), - 3600.0 to ConstantPowerModel(350.0) - ) - ) - - val logic = driver.createLogic(machine, emptyList()) - assertEquals(200.0, logic.computePower()) - } - - @Test - fun testPowerWithSingleCpu() { - val machine = mockk<SimBareMetalMachine>() - val cpu = mockk<SimProcessingUnit>(relaxUnitFun = true) - - every { cpu.capacity } returns 3200.0 - every { cpu.rate } returns 1200.0 - - val driver = PStatePowerDriver( - sortedMapOf( - 2800.0 to ConstantPowerModel(200.0), - 3300.0 to ConstantPowerModel(300.0), - 3600.0 to ConstantPowerModel(350.0) - ) - ) - - val logic = driver.createLogic(machine, listOf(cpu)) - - assertEquals(300.0, logic.computePower()) - } - - @Test - fun testPowerWithMultipleCpus() { - val machine = mockk<SimBareMetalMachine>() - val cpu = mockk<SimProcessingUnit>(relaxUnitFun = true) - val cpus = listOf(cpu, cpu) - - every { cpus[0].capacity } returns 1000.0 - every { cpus[0].rate } returns 1200.0 - - every { cpus[1].capacity } returns 3500.0 - every { cpus[1].rate } returns 1200.0 - - val driver = PStatePowerDriver( - sortedMapOf( - 2800.0 to ConstantPowerModel(200.0), - 3300.0 to ConstantPowerModel(300.0), - 3600.0 to ConstantPowerModel(350.0) - ) - ) - - val logic = driver.createLogic(machine, cpus) - - assertEquals(350.0, logic.computePower()) - } - - @Test - fun testPowerBasedOnUtilization() { - val machine = mockk<SimBareMetalMachine>() - val cpu = mockk<SimProcessingUnit>(relaxUnitFun = true) - - every { cpu.model.frequency } returns 4200.0 - - val driver = PStatePowerDriver( - sortedMapOf( - 2800.0 to LinearPowerModel(200.0, 100.0), - 3300.0 to LinearPowerModel(250.0, 150.0), - 4000.0 to LinearPowerModel(300.0, 200.0) - ) - ) - - val logic = driver.createLogic(machine, listOf(cpu)) - - every { cpu.rate } returns 1400.0 - every { cpu.capacity } returns 1400.0 - assertEquals(150.0, logic.computePower()) - - every { cpu.rate } returns 1400.0 - every { cpu.capacity } returns 4000.0 - assertEquals(235.0, logic.computePower()) - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PowerModelTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PowerModelTest.kt index 67532d5b..9a6263c5 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PowerModelTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/power/PowerModelTest.kt @@ -38,7 +38,7 @@ internal class PowerModelTest { @ParameterizedTest @MethodSource("MachinePowerModelArgs") fun `compute power consumption given CPU loads`( - powerModel: PowerModel, + powerModel: CpuPowerModel, expectedPowerConsumption: Double ) { val computedPowerConsumption = powerModel.computePower(cpuUtil) @@ -48,10 +48,10 @@ internal class PowerModelTest { @ParameterizedTest @MethodSource("MachinePowerModelArgs") fun `ignore idle power when computing power consumptions`( - powerModel: PowerModel, + powerModel: CpuPowerModel, expectedPowerConsumption: Double ) { - val zeroPowerModel = ZeroIdlePowerDecorator(powerModel) + val zeroPowerModel = CpuPowerModels.zeroIdle(powerModel) assertAll( { assertEquals(expectedPowerConsumption, zeroPowerModel.computePower(cpuUtil), epsilon) }, @@ -61,8 +61,9 @@ internal class PowerModelTest { @Test fun `compute power draw by the SPEC benchmark model`() { - val ibm = listOf(58.4, 98.0, 109.0, 118.0, 128.0, 140.0, 153.0, 170.0, 189.0, 205.0, 222.0) - val powerModel = InterpolationPowerModel(ibm) + val powerModel = CpuPowerModels.interpolate( + 58.4, 98.0, 109.0, 118.0, 128.0, 140.0, 153.0, 170.0, 189.0, 205.0, 222.0 + ) assertAll( { assertEquals(58.4, powerModel.computePower(0.0)) }, @@ -80,14 +81,14 @@ internal class PowerModelTest { private companion object { @JvmStatic fun MachinePowerModelArgs(): Stream<Arguments> = Stream.of( - Arguments.of(ConstantPowerModel(0.0), 0.0), - Arguments.of(LinearPowerModel(350.0, 200.0), 335.0), - Arguments.of(SquarePowerModel(350.0, 200.0), 321.5), - Arguments.of(CubicPowerModel(350.0, 200.0), 309.35), - Arguments.of(SqrtPowerModel(350.0, 200.0), 342.302), - Arguments.of(MsePowerModel(350.0, 200.0, 1.4), 340.571), - Arguments.of(AsymptoticPowerModel(350.0, 200.0, 0.3, false), 338.765), - Arguments.of(AsymptoticPowerModel(350.0, 200.0, 0.3, true), 323.072) + Arguments.of(CpuPowerModels.constant(0.0), 0.0), + Arguments.of(CpuPowerModels.linear(350.0, 200.0), 335.0), + Arguments.of(CpuPowerModels.square(350.0, 200.0), 321.5), + Arguments.of(CpuPowerModels.cubic(350.0, 200.0), 309.35), + Arguments.of(CpuPowerModels.sqrt(350.0, 200.0), 342.302), + Arguments.of(CpuPowerModels.mse(350.0, 200.0, 1.4), 340.571), + Arguments.of(CpuPowerModels.asymptotic(350.0, 200.0, 0.3, false), 338.765), + Arguments.of(CpuPowerModels.asymptotic(350.0, 200.0, 0.3, true), 323.072) ) } } diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkloadTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkloadTest.kt index b3e57453..edbc0571 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkloadTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimFlopsWorkloadTest.kt @@ -32,7 +32,7 @@ class SimFlopsWorkloadTest { @Test fun testFlopsNonNegative() { assertThrows<IllegalArgumentException>("FLOPs must be non-negative") { - SimFlopsWorkload(-1) + SimFlopsWorkload(-1, 1.0) } } diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt index 83e1f81c..e3b6e6c5 100644 --- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt +++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt @@ -31,10 +31,8 @@ import org.opendc.simulator.compute.model.MachineModel import org.opendc.simulator.compute.model.MemoryUnit import org.opendc.simulator.compute.model.ProcessingNode import org.opendc.simulator.compute.model.ProcessingUnit -import org.opendc.simulator.compute.power.ConstantPowerModel -import org.opendc.simulator.compute.power.SimplePowerDriver import org.opendc.simulator.compute.runWorkload -import org.opendc.simulator.flow.FlowEngine +import org.opendc.simulator.flow2.FlowEngine import org.opendc.simulator.kotlin.runSimulation /** @@ -48,28 +46,28 @@ class SimTraceWorkloadTest { val cpuNode = ProcessingNode("Intel", "Xeon", "amd64", 2) machineModel = MachineModel( - cpus = List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, - memory = List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } + /*cpus*/ List(cpuNode.coreCount) { ProcessingUnit(cpuNode, it, 1000.0) }, + /*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) } ) } @Test fun testSmoke() = runSimulation { - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) - val workload = SimTraceWorkload( + val workload = SimTrace.ofFragments( SimTraceFragment(0, 1000, 2 * 28.0, 2), SimTraceFragment(1000, 1000, 2 * 3100.0, 2), SimTraceFragment(2000, 1000, 0.0, 2), SimTraceFragment(3000, 1000, 2 * 73.0, 2) - ), - offset = 0 - ) + ).createWorkload(0) machine.runWorkload(workload) @@ -78,21 +76,21 @@ class SimTraceWorkloadTest { @Test fun testOffset() = runSimulation { - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) - val workload = SimTraceWorkload( + val workload = SimTrace.ofFragments( SimTraceFragment(0, 1000, 2 * 28.0, 2), SimTraceFragment(1000, 1000, 2 * 3100.0, 2), SimTraceFragment(2000, 1000, 0.0, 2), SimTraceFragment(3000, 1000, 2 * 73.0, 2) - ), - offset = 1000 - ) + ).createWorkload(1000) machine.runWorkload(workload) @@ -101,21 +99,21 @@ class SimTraceWorkloadTest { @Test fun testSkipFragment() = runSimulation { - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) - val workload = SimTraceWorkload( + val workload = SimTrace.ofFragments( SimTraceFragment(0, 1000, 2 * 28.0, 2), SimTraceFragment(1000, 1000, 2 * 3100.0, 2), SimTraceFragment(2000, 1000, 0.0, 2), SimTraceFragment(3000, 1000, 2 * 73.0, 2) - ), - offset = 0 - ) + ).createWorkload(0) delay(1000L) machine.runWorkload(workload) @@ -125,21 +123,21 @@ class SimTraceWorkloadTest { @Test fun testZeroCores() = runSimulation { - val machine = SimBareMetalMachine( - FlowEngine(coroutineContext, clock), - machineModel, - SimplePowerDriver(ConstantPowerModel(0.0)) + val engine = FlowEngine.create(coroutineContext, clock) + val graph = engine.newGraph() + + val machine = SimBareMetalMachine.create( + graph, + machineModel ) - val workload = SimTraceWorkload( + val workload = SimTrace.ofFragments( SimTraceFragment(0, 1000, 2 * 28.0, 2), SimTraceFragment(1000, 1000, 2 * 3100.0, 2), SimTraceFragment(2000, 1000, 0.0, 0), SimTraceFragment(3000, 1000, 2 * 73.0, 2) - ), - offset = 0 - ) + ).createWorkload(0) machine.runWorkload(workload) diff --git a/opendc-simulator/opendc-simulator-compute/src/test/resources/spec_machines.yml b/opendc-simulator/opendc-simulator-compute/src/test/resources/spec_machines.yml deleted file mode 100644 index d51cba80..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/test/resources/spec_machines.yml +++ /dev/null @@ -1,29 +0,0 @@ ---- -# The power model of an IBM server x3550 (2 x [Xeon X5675 3067 MHz, 6 cores], 16GB).<br/> -# <a href="http://www.spec.org/power_ssj2008/results/res2011q2/power_ssj2008-20110406-00368.html"> -# http://www.spec.org/power_ssj2008/results/res2011q2/power_ssj2008-20110406-00368.html</a> -IBMx3550M3_XeonX5675: [58.4, 98.0, 109.0, 118.0, 128.0, 140.0, 153.0, 170.0, 189.0, 205.0, 222.0] - # The power model of an IBM server x3550 (2 x [Xeon X5670 2933 MHz, 6 cores], 12GB).<br/> - # <a href="http://www.spec.org/power_ssj2008/results/res2010q2/power_ssj2008-20100315-00239.html"> - # http://www.spec.org/power_ssj2008/results/res2010q2/power_ssj2008-20100315-00239.html</a> -IBMx3550M3_XeonX5670: [66.0, 107.0, 120.0, 131.0, 143.0, 156.0, 173.0, 191.0, 211.0, 229.0, 247.0] - # the power model of an IBM server x3250 (1 x [Xeon X3480 3067 MHz, 4 cores], 8GB).<br/> - # <a href="http://www.spec.org/power_ssj2008/results/res2010q4/power_ssj2008-20101001-00297.html"> - # http://www.spec.org/power_ssj2008/results/res2010q4/power_ssj2008-20101001-00297.html</a> -IBMx3250M3_XeonX3480: [42.3, 46.7, 49.7, 55.4, 61.8, 69.3, 76.1, 87.0, 96.1, 106.0, 113.0] - # The power model of an IBM server x3250 (1 x [Xeon X3470 2933 MHz, 4 cores], 8GB).<br/> - # <a href="http://www.spec.org/power_ssj2008/results/res2009q4/power_ssj2008-20091104-00213.html"> - # http://www.spec.org/power_ssj2008/results/res2009q4/power_ssj2008-20091104-00213.html</a> -IBMx3250M3_XeonX3470: [41.6, 46.7, 52.3, 57.9, 65.4, 73.0, 80.7, 89.5, 99.6, 105.0, 113.0] - # The power model of an HP ProLiant ML110 G5 (1 x [Xeon 3075 2660 MHz, 2 cores], 4GB).<br/> - # <a href="http://www.spec.org/power_ssj2008/results/res2011q1/power_ssj2008-20110124-00339.html"> - # http://www.spec.org/power_ssj2008/results/res2011q1/power_ssj2008-20110124-00339.html</a> -HPProLiantMl110G5_Xeon3075: [93.7, 97.0, 101.0, 105.0, 110.0, 116.0, 121.0, 125.0, 129.0, 133.0, 135.0] - # The power model of an HP ProLiant ML110 G4 (1 x [Xeon 3040 1860 MHz, 2 cores], 4GB).<br/> - # <a href="http://www.spec.org/power_ssj2008/results/res2011q1/power_ssj2008-20110127-00342.html"> - # http://www.spec.org/power_ssj2008/results/res2011q1/power_ssj2008-20110127-00342.html</a> -HPProLiantMl110G4_Xeon3040: [86.0, 89.4, 92.6, 96.0, 99.5, 102.0, 106.0, 108.0, 112.0, 114.0, 117.0] - # The power model of an HP ProLiant ML110 G3 (1 x [Pentium D930 3000 MHz, 2 cores], 4GB).<br/> - # <a href="http://www.spec.org/power_ssj2008/results/res2011q1/power_ssj2008-20110127-00342.html"> - # http://www.spec.org/power_ssj2008/results/res2011q1/power_ssj2008-20110127-00342.html</a> -HPProLiantMl110G3_PentiumD930: [105.0, 112.0, 118.0, 125.0, 131.0, 137.0, 147.0, 153.0, 157.0, 164.0, 169.0] |
