diff options
| author | Dante Niewenhuis <d.niewenhuis@hotmail.com> | 2024-10-25 13:32:41 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-10-25 13:32:41 +0200 |
| commit | 5a365dbc068f2a8cdfa9813c39cc84bb30e15637 (patch) | |
| tree | 72716d562787b85e03cdc7fe1d30c827054d25a0 /opendc-simulator/opendc-simulator-compute/src/main/java | |
| parent | 27f5b7dcb05aefdab9b762175d538931face0aba (diff) | |
Rewrote the FlowEngine (#256)
* Removed unused components. Updated tests.
Improved checkpointing model
Improved model, started with SimPowerSource
implemented FailureModels and Checkpointing
First working version
midway commit
first update
All simulation are now run with a single CPU and single MemoryUnit. multi CPUs are combined into one. This is for performance and explainability.
* fixed merge conflicts
* Updated M3SA paths.
* Fixed small typo
Diffstat (limited to 'opendc-simulator/opendc-simulator-compute/src/main/java')
49 files changed, 2007 insertions, 4748 deletions
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimAbstractMachine.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimAbstractMachine.java deleted file mode 100644 index 3a9e35c1..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimAbstractMachine.java +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Consumer; -import org.opendc.simulator.compute.device.SimNetworkAdapter; -import org.opendc.simulator.compute.model.MachineModel; -import org.opendc.simulator.compute.model.MemoryUnit; -import org.opendc.simulator.compute.workload.SimWorkload; -import org.opendc.simulator.flow2.FlowGraph; -import org.opendc.simulator.flow2.Inlet; -import org.opendc.simulator.flow2.Outlet; -import org.opendc.simulator.flow2.sink.SimpleFlowSink; -import org.opendc.simulator.flow2.util.FlowTransformer; -import org.opendc.simulator.flow2.util.FlowTransforms; - -/** - * Abstract implementation of the {@link SimMachine} interface. - */ -public abstract class SimAbstractMachine implements SimMachine { - private final MachineModel model; - - private SimAbstractMachineContext activeContext; - - /** - * Construct a {@link SimAbstractMachine} instance. - * - * @param model The model of the machine. - */ - public SimAbstractMachine(MachineModel model) { - this.model = model; - } - - @Override - public final MachineModel getModel() { - return model; - } - - @Override - public final SimMachineContext startWorkload( - SimWorkload workload, Map<String, Object> meta, Consumer<Exception> completion) { - if (activeContext != null) { - throw new IllegalStateException("A machine cannot run multiple workloads concurrently"); - } - - final SimAbstractMachineContext ctx = createContext(workload, new HashMap<>(meta), completion); - ctx.start(); - return ctx; - } - - @Override - public final void cancel() { - final SimAbstractMachineContext context = activeContext; - if (context != null) { - context.shutdown(); - } - } - - /** - * Construct a new {@link SimAbstractMachineContext} instance representing the active execution. - * - * @param workload The workload to start on the machine. - * @param meta The metadata to pass to the workload. - * @param completion A block that is invoked when the workload completes carrying an exception if thrown by the workload. - */ - protected abstract SimAbstractMachineContext createContext( - SimWorkload workload, Map<String, Object> meta, Consumer<Exception> completion); - - /** - * Return the active {@link SimAbstractMachineContext} instance (if any). - */ - protected SimAbstractMachineContext getActiveContext() { - return activeContext; - } - - /** - * The execution context in which the workload runs. - */ - public abstract static class SimAbstractMachineContext implements SimMachineContext { - private final SimAbstractMachine machine; - private final SimWorkload workload; - private final Map<String, Object> meta; - private final Consumer<Exception> completion; - private boolean isClosed; - private SimWorkload snapshot; - - /** - * Construct a new {@link SimAbstractMachineContext} instance. - * - * @param machine The {@link SimAbstractMachine} to which the context belongs. - * @param workload The {@link SimWorkload} to which the context belongs. - * @param meta The metadata passed to the context. - * @param completion A block that is invoked when the workload completes carrying an exception if thrown by the workload. - */ - public SimAbstractMachineContext( - SimAbstractMachine machine, - SimWorkload workload, - Map<String, Object> meta, - Consumer<Exception> completion) { - this.machine = machine; - this.workload = workload; - this.meta = meta; - this.completion = completion; - } - - @Override - public final Map<String, Object> getMeta() { - return meta; - } - - @Override - public void makeSnapshot(long now) { - this.snapshot = workload.getSnapshot(); - } - - @Override - public SimWorkload getSnapshot(long now) { - return this.snapshot; - } - - @Override - public void reset() { - final FlowGraph graph = getMemory().getInput().getGraph(); - - final Inlet inlet = getCpu().getInput(); - graph.disconnect(inlet); - - graph.disconnect(getMemory().getInput()); - - for (SimNetworkInterface ifx : getNetworkInterfaces()) { - ((NetworkAdapter) ifx).disconnect(); - } - - for (SimStorageInterface storage : getStorageInterfaces()) { - StorageDevice impl = (StorageDevice) storage; - graph.disconnect(impl.getRead()); - graph.disconnect(impl.getWrite()); - } - } - - @Override - public final void shutdown() { - shutdown(null); - } - - @Override - public final void shutdown(Exception cause) { - if (isClosed) { - return; - } - - isClosed = true; - final SimAbstractMachine machine = this.machine; - assert machine.activeContext == this : "Invariant violation: multiple contexts active for a single machine"; - machine.activeContext = null; - - // Cancel all the resources associated with the machine - doCancel(); - - try { - workload.onStop(this); - } catch (Exception e) { - if (cause == null) { - cause = e; - } else { - cause.addSuppressed(e); - } - } - - completion.accept(cause); - } - - /** - * Start this context. - */ - final void start() { - try { - machine.activeContext = this; - workload.onStart(this); - } catch (Exception cause) { - shutdown(cause); - } - } - - /** - * Run the stop procedures for the resources associated with the machine. - */ - protected void doCancel() { - reset(); - } - - @Override - public String toString() { - return "SimAbstractMachine.Context"; - } - } - - /** - * The [SimMemory] implementation for a machine. - */ - public static final class Memory implements SimMemory { - private final SimpleFlowSink sink; - private final MemoryUnit memoryUnit; - - public Memory(FlowGraph graph, MemoryUnit memoryUnit) { - - this.memoryUnit = memoryUnit; - this.sink = new SimpleFlowSink(graph, (float) memoryUnit.getSize()); - } - - @Override - public double getCapacity() { - return sink.getCapacity(); - } - - @Override - public MemoryUnit getMemoryUnit() { - return memoryUnit; - } - - @Override - public Inlet getInput() { - return sink.getInput(); - } - - @Override - public String toString() { - return "SimAbstractMachine.Memory"; - } - } - - /** - * A {@link SimNetworkAdapter} implementation for a machine. - */ - public static class NetworkAdapter extends SimNetworkAdapter implements SimNetworkInterface { - private final org.opendc.simulator.compute.model.NetworkAdapter model; - private final FlowTransformer tx; - private final FlowTransformer rx; - private final String name; - - /** - * Construct a {@link NetworkAdapter}. - */ - public NetworkAdapter(FlowGraph graph, org.opendc.simulator.compute.model.NetworkAdapter model, int index) { - this.model = model; - this.tx = new FlowTransformer(graph, FlowTransforms.noop()); - this.rx = new FlowTransformer(graph, FlowTransforms.noop()); - this.name = "eth" + index; - } - - @Override - public String getName() { - return name; - } - - @Override - public Inlet getTx() { - return tx.getInput(); - } - - @Override - public Outlet getRx() { - return rx.getOutput(); - } - - @Override - public double getBandwidth() { - return model.getBandwidth(); - } - - @Override - protected Outlet getOutlet() { - return tx.getOutput(); - } - - @Override - protected Inlet getInlet() { - return rx.getInput(); - } - - @Override - public String toString() { - return "SimAbstractMachine.NetworkAdapterImpl[name=" + name + ", bandwidth=" + model.getBandwidth() - + "Mbps]"; - } - } - - /** - * A {@link SimStorageInterface} implementation for a machine. - */ - public static class StorageDevice implements SimStorageInterface { - private final org.opendc.simulator.compute.model.StorageDevice model; - private final SimpleFlowSink read; - private final SimpleFlowSink write; - private final String name; - - /** - * Construct a {@link StorageDevice}. - */ - public StorageDevice(FlowGraph graph, org.opendc.simulator.compute.model.StorageDevice model, int index) { - this.model = model; - this.read = new SimpleFlowSink(graph, (float) model.getReadBandwidth()); - this.write = new SimpleFlowSink(graph, (float) model.getWriteBandwidth()); - this.name = "disk" + index; - } - - @Override - public String getName() { - return name; - } - - @Override - public Inlet getRead() { - return read.getInput(); - } - - @Override - public Inlet getWrite() { - return write.getInput(); - } - - @Override - public double getCapacity() { - return model.getCapacity(); - } - - @Override - public String toString() { - return "SimAbstractMachine.StorageDeviceImpl[name=" + name + ", capacity=" + model.getCapacity() + "MB]"; - } - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimBareMetalMachine.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimBareMetalMachine.java deleted file mode 100644 index 6acc605e..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimBareMetalMachine.java +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -import org.opendc.simulator.compute.device.SimPeripheral; -import org.opendc.simulator.compute.model.Cpu; -import org.opendc.simulator.compute.model.MachineModel; -import org.opendc.simulator.compute.workload.SimWorkload; -import org.opendc.simulator.flow2.FlowGraph; -import org.opendc.simulator.flow2.InPort; -import org.opendc.simulator.flow2.Inlet; - -/** - * A simulated bare-metal machine that is able to run a single workload. - * - * <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, Consumer)} )}. - */ -public final class SimBareMetalMachine extends SimAbstractMachine { - /** - * The {@link FlowGraph} in which the simulation takes places. - */ - private final FlowGraph graph; - - /** - * The {@link SimPsu} of this bare metal machine. - */ - private final SimPsu psu; - - /** - * The resources of this machine. - */ - private final SimCpu cpu; - - private final Memory memory; - private final List<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); - - this.cpu = new SimCpu(psu, model.getCpu(), 0); - 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 SimAbstractMachineContext context = (SimAbstractMachineContext) getActiveContext(); - - if (context == null) { - return 0.0; - } - - return cpu.getFrequency(); - } - - /** - * The CPU demand of the machine in MHz. - */ - public double getCpuDemand() { - final SimAbstractMachineContext context = (SimAbstractMachineContext) getActiveContext(); - - if (context == null) { - return 0.0; - } - - return cpu.getDemand(); - } - - /** - * The CPU usage of the machine in MHz. - */ - public double getCpuUsage() { - final SimAbstractMachineContext context = (SimAbstractMachineContext) getActiveContext(); - - if (context == null) { - return 0.0; - } - - return cpu.getSpeed(); - } - - @Override - protected SimAbstractMachine.SimAbstractMachineContext createContext( - SimWorkload workload, Map<String, Object> meta, Consumer<Exception> completion) { - return new SimAbstractMachineContext(this, workload, meta, completion); - } - - /** - * The execution context for a {@link SimBareMetalMachine}. - */ - private static final class SimAbstractMachineContext extends SimAbstractMachine.SimAbstractMachineContext { - private final FlowGraph graph; - private final SimCpu cpu; - private final Memory memory; - private final List<NetworkAdapter> net; - private final List<StorageDevice> disk; - - private SimAbstractMachineContext( - SimBareMetalMachine machine, - SimWorkload workload, - Map<String, Object> meta, - Consumer<Exception> completion) { - super(machine, workload, meta, completion); - - this.graph = machine.graph; - this.cpu = machine.cpu; - this.memory = machine.memory; - this.net = machine.net; - this.disk = machine.disk; - } - - @Override - public FlowGraph getGraph() { - return graph; - } - - @Override - public SimCpu getCpu() { - return cpu; - } - - @Override - public SimMemory getMemory() { - return memory; - } - - @Override - public List<? 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 SimCpu implements SimProcessingUnit { - private final SimPsu psu; - private final Cpu cpuModel; - private final InPort port; - - private SimCpu(SimPsu psu, Cpu cpuModel, int id) { - this.psu = psu; - this.cpuModel = cpuModel; - this.port = psu.getCpuPower(id, cpuModel); - - this.port.pull((float) cpuModel.getTotalCapacity()); - } - - @Override - public double getFrequency() { - return port.getCapacity(); - } - - @Override - public void setFrequency(double frequency) { - // Clamp the capacity of the CPU between [0.0, maxFreq] - frequency = Math.max(0, Math.min(cpuModel.getTotalCapacity(), frequency)); - psu.setCpuFrequency(port, frequency); - } - - @Override - public double getDemand() { - return port.getDemand(); - } - - @Override - public double getSpeed() { - return port.getRate(); - } - - @Override - public org.opendc.simulator.compute.model.Cpu getCpuModel() { - return cpuModel; - } - - @Override - public Inlet getInput() { - return port; - } - - @Override - public String toString() { - return "SimBareMetalMachine.Cpu[model=" + cpuModel + "]"; - } - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachine.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachine.java deleted file mode 100644 index 1f86aa02..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachine.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute; - -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -import org.opendc.simulator.compute.device.SimPeripheral; -import org.opendc.simulator.compute.model.MachineModel; -import org.opendc.simulator.compute.workload.SimWorkload; - -/** - * A generic machine that is able to execute {@link SimWorkload} objects. - */ -public interface SimMachine { - /** - * Return the model of the machine containing its specifications. - */ - MachineModel getModel(); - - /** - * Return the peripherals attached to the machine. - */ - List<? extends SimPeripheral> getPeripherals(); - - /** - * Start the specified {@link SimWorkload} on this machine. - * - * @param workload The workload to start on the machine. - * @param meta The metadata to pass to the workload. - * @param completion A block that is invoked when the workload completes carrying an exception if thrown by the workload. - * @return A {@link SimMachineContext} that represents the execution context for the workload. - * @throws IllegalStateException if a workload is already active on the machine or if the machine is closed. - */ - SimMachineContext startWorkload(SimWorkload workload, Map<String, Object> meta, Consumer<Exception> completion); - - /** - * Cancel the active workload on this machine (if any). - */ - void cancel(); -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachineContext.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachineContext.java deleted file mode 100644 index 887967fb..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMachineContext.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute; - -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -import org.opendc.simulator.compute.workload.SimWorkload; -import org.opendc.simulator.flow2.FlowGraph; - -/** - * A simulated execution context in which a bootable image runs. - * - * <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 { - /** - * Return the {@link FlowGraph} in which the workload executes. - */ - FlowGraph getGraph(); - - /** - * Return the metadata associated with the context. - * <p> - * Users can pass this metadata to the workload via {@link SimMachine#startWorkload(SimWorkload, Map, Consumer)}. - */ - Map<String, Object> getMeta(); - - /** - * Return the CPUs available on the machine. - */ - SimProcessingUnit getCpu(); - - /** - * Return the memory interface of the machine. - */ - SimMemory getMemory(); - - /** - * Return the network interfaces available to the workload. - */ - List<? extends SimNetworkInterface> getNetworkInterfaces(); - - /** - * Return the storage devices available to the workload. - */ - List<? extends SimStorageInterface> getStorageInterfaces(); - - /** - * Create a snapshot of the {@link SimWorkload} running on this machine. - * - * @throws UnsupportedOperationException if the workload does not support snapshotting. - */ - void makeSnapshot(long now); - - SimWorkload getSnapshot(long now); - - /** - * Reset all resources of the machine. - */ - void reset(); - - /** - * Shutdown the workload. - */ - void shutdown(); - - /** - * Shutdown the workload due to failure. - * - * @param cause The cause for shutting down the workload. - */ - void shutdown(Exception cause); -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMemory.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMemory.java deleted file mode 100644 index 85027f28..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimMemory.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute; - -import org.opendc.simulator.compute.model.MemoryUnit; -import org.opendc.simulator.flow2.sink.FlowSink; - -/** - * An interface to control the memory usage of simulated workloads. - */ -public interface SimMemory extends FlowSink { - /** - * Return the total capacity of the memory (in MBs). - */ - double getCapacity(); - - /** - * Return the models representing the static information of the memory units supporting this interface. - */ - MemoryUnit getMemoryUnit(); -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimProcessingUnit.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimProcessingUnit.java deleted file mode 100644 index 213c3d4f..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimProcessingUnit.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute; - -import org.opendc.simulator.compute.model.Cpu; -import org.opendc.simulator.flow2.sink.FlowSink; - -/** - * A simulated processing unit. - */ -public interface SimProcessingUnit extends FlowSink { - /** - * Return the base clock frequency of the processing unit (in MHz). - */ - double getFrequency(); - - /** - * Adjust the base clock frequency of the processing unit. - * - * <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. - */ - Cpu getCpuModel(); -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsu.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsu.java deleted file mode 100644 index e7718604..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsu.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute; - -import org.opendc.simulator.compute.model.Cpu; -import org.opendc.simulator.flow2.InPort; -import org.opendc.simulator.power.SimPowerInlet; - -/** - * A power supply unit in a {@link SimBareMetalMachine}. - * - * <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 getPowerDraw(); - - /** - * Return the cumulated energy usage of the machine (in J) measured at the inlet of the powers supply. - */ - public abstract double getEnergyUsage(); - - /** - * Return an {@link InPort} that converts processing demand (in MHz) into energy demand (J) for the specified CPU - * <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, Cpu 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 deleted file mode 100644 index 27327616..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactories.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute; - -import java.time.InstantSource; -import org.jetbrains.annotations.NotNull; -import org.opendc.simulator.compute.model.Cpu; -import org.opendc.simulator.compute.power.CpuPowerModel; -import org.opendc.simulator.flow2.FlowGraph; -import org.opendc.simulator.flow2.FlowStage; -import org.opendc.simulator.flow2.FlowStageLogic; -import org.opendc.simulator.flow2.InHandler; -import org.opendc.simulator.flow2.InPort; -import org.opendc.simulator.flow2.OutPort; -import org.opendc.simulator.flow2.Outlet; - -/** - * A collection {@link SimPsu} implementations. - */ -public class SimPsuFactories { - private SimPsuFactories() {} - - /** - * Return a {@link SimPsuFactory} of {@link SimPsu} implementations that do not measure any power consumption. - * - * <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 getPowerDraw() { - return 0; - } - - @Override - public double getEnergyUsage() { - return 0; - } - - @Override - InPort getCpuPower(int id, Cpu model) { - final InPort port = stage.getInlet("cpu" + id); - port.setMask(true); - return port; - } - - @Override - public long onUpdate(FlowStage ctx, long now) { - return Long.MAX_VALUE; - } - - @NotNull - @Override - public Outlet getFlowOutlet() { - return out; - } - } - - /** - * A {@link SimPsu} implementation that estimates the power consumption based on CPU usage. - */ - private static final class SimplePsu extends SimPsu implements FlowStageLogic { - private final FlowStage stage; - private final OutPort out; - private final CpuPowerModel model; - private final InstantSource clock; - - private double targetFreq; - private double totalUsage; - private long lastUpdate; - - private double powerDraw; - private double energyUsage; - - private final InHandler handler = new InHandler() { - @Override - public void onPush(InPort port, float demand) { - totalUsage += -port.getDemand() + demand; - } - - @Override - public void onUpstreamFinish(InPort port, Throwable cause) { - totalUsage -= port.getDemand(); - } - }; - - SimplePsu(FlowGraph graph, CpuPowerModel model) { - this.stage = graph.newStage(this); - this.model = model; - this.clock = graph.getEngine().getClock(); - this.out = stage.getOutlet("out"); - this.out.setMask(true); - - lastUpdate = graph.getEngine().getClock().millis(); - } - - @Override - public double getPowerDemand() { - return totalUsage; - } - - @Override - public double getPowerDraw() { - return powerDraw; - } - - @Override - public double getEnergyUsage() { - updateEnergyUsage(clock.millis()); - return energyUsage; - } - - @Override - InPort getCpuPower(int id, Cpu model) { - targetFreq += model.getTotalCapacity(); - - final InPort port = stage.getInlet("cpu" + id); - port.setHandler(handler); - return port; - } - - @Override - void setCpuFrequency(InPort port, double capacity) { - targetFreq += -port.getCapacity() + capacity; - - super.setCpuFrequency(port, capacity); - } - - @Override - public long onUpdate(FlowStage ctx, long now) { - updateEnergyUsage(now); - - double usage = model.computePower(totalUsage / targetFreq); - out.push((float) usage); - powerDraw = usage; - - return Long.MAX_VALUE; - } - - @NotNull - @Override - public Outlet getFlowOutlet() { - return out; - } - - /** - * Calculate the energy usage up until <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 += powerDraw * duration * 0.001; - } - } - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactory.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactory.java deleted file mode 100644 index 872e7016..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactory.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute; - -import org.opendc.simulator.flow2.FlowGraph; - -/** - * A factory interface for {@link SimPsu} implementations. - */ -public interface SimPsuFactory { - /** - * Construct a new {@link SimPsu} for the specified <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/java/org/opendc/simulator/compute/SimStorageInterface.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimStorageInterface.java deleted file mode 100644 index 341122dc..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimStorageInterface.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute; - -import org.opendc.simulator.flow2.Inlet; - -/** - * A firmware interface to a storage device. - */ -public interface SimStorageInterface { - /** - * Return the name of the network interface. - */ - String getName(); - - /** - * Return the capacity of the storage device in MBs. - */ - double getCapacity(); - - /** - * Return the inlet for the read operations of the storage device. - */ - Inlet getRead(); - - /** - * Return the inlet for the write operation of the storage device. - */ - Inlet getWrite(); -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CPUPowerModelsFactory.kt b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/cpu/CPUPowerModelsFactory.kt index 2c64944c..3600756b 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CPUPowerModelsFactory.kt +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/cpu/CPUPowerModelsFactory.kt @@ -20,7 +20,7 @@ * SOFTWARE. */ -package org.opendc.simulator.compute.power +package org.opendc.simulator.compute.cpu // TODO: couple this correctly public enum class CPUPowerModel { diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CpuPowerModel.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/cpu/CpuPowerModel.java index 73f9357d..4323294e 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/CpuPowerModel.java +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/cpu/CpuPowerModel.java @@ -20,9 +20,9 @@ * SOFTWARE. */ -package org.opendc.simulator.compute.power; +package org.opendc.simulator.compute.cpu; -import org.opendc.simulator.compute.SimMachine; +import org.opendc.simulator.compute.machine.SimMachine; /** * A model for estimating the power usage of a {@link SimMachine} based on the CPU usage. 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/cpu/CpuPowerModels.java index 4e62e67f..b91bd7e2 100644 --- 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/cpu/CpuPowerModels.java @@ -20,7 +20,7 @@ * SOFTWARE. */ -package org.opendc.simulator.compute.power; +package org.opendc.simulator.compute.cpu; import java.util.Arrays; diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/cpu/SimCpu.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/cpu/SimCpu.java new file mode 100644 index 00000000..60c877e9 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/cpu/SimCpu.java @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2024 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.cpu; + +import org.opendc.simulator.compute.machine.PerformanceCounters; +import org.opendc.simulator.compute.models.CpuModel; +import org.opendc.simulator.engine.FlowConsumer; +import org.opendc.simulator.engine.FlowEdge; +import org.opendc.simulator.engine.FlowGraph; +import org.opendc.simulator.engine.FlowNode; +import org.opendc.simulator.engine.FlowSupplier; + +/** + * A {@link SimCpu} of a machine. + */ +public final class SimCpu extends FlowNode implements FlowSupplier, FlowConsumer { + private final CpuModel cpuModel; + + private final CpuPowerModel cpuPowerModel; + + private float currentCpuDemand = 0.0f; // cpu capacity demanded by the mux + private float currentCpuUtilization = 0.0f; + private float currentPowerDemand = 0.0f; // power demanded of the psu + private float currentCpuSupplied = 0.0f; // cpu capacity supplied to the mux + private float currentPowerSupplied = 0.0f; // cpu capacity supplied by the psu + + private float maxCapacity; + + private PerformanceCounters performanceCounters = new PerformanceCounters(); + private long lastCounterUpdate; + private final float cpuFrequencyInv; + + private FlowEdge muxEdge; + private FlowEdge psuEdge; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Basic Getters and Setters + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public double getFrequency() { + return cpuModel.getTotalCapacity(); + } + + public void setFrequency(double frequency) { + // Clamp the capacity of the CPU between [0.0, maxFreq] + frequency = Math.max(0, Math.min(this.maxCapacity, frequency)); + // psu.setCpuFrequency(muxInPort, frequency); + } + + @Override + public float getCapacity() { + return maxCapacity; + } + + public PerformanceCounters getPerformanceCounters() { + return performanceCounters; + } + + public double getPowerDraw() { + return this.currentPowerSupplied; + } + + public double getDemand() { + return this.currentCpuDemand; + } + + public double getSpeed() { + return this.currentCpuSupplied; + } + + public CpuModel getCpuModel() { + return cpuModel; + } + + @Override + public String toString() { + return "SimBareMetalMachine.Cpu[model=" + cpuModel + "]"; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public SimCpu(FlowGraph graph, CpuModel cpuModel, int id) { + super(graph); + this.cpuModel = cpuModel; + this.maxCapacity = this.cpuModel.getTotalCapacity(); + + // TODO: connect this to the front-end + this.cpuPowerModel = CpuPowerModels.linear(400, 200); + + this.lastCounterUpdate = graph.getEngine().getClock().millis(); + + this.cpuFrequencyInv = 1 / this.maxCapacity; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // FlowNode related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public long onUpdate(long now) { + updateCounters(now); + + // Calculate Power Demand and send to PSU + // TODO: look at the float / double thing + float powerDemand = (float) this.cpuPowerModel.computePower((double) this.currentCpuUtilization); + + if (powerDemand != this.currentPowerDemand) { + this.pushDemand(this.psuEdge, powerDemand); + this.currentPowerDemand = powerDemand; + } + + // Calculate the amount of cpu this can provide + // TODO: This should be based on the provided power + float cpuSupply = this.currentCpuDemand; + + if (cpuSupply != this.currentCpuSupplied) { + this.pushSupply(this.muxEdge, cpuSupply); + this.currentCpuSupplied = cpuSupply; + } + + return Long.MAX_VALUE; + } + + public void updateCounters() { + this.updateCounters(this.clock.millis()); + } + + /** + * Update the performance counters of the CPU. + * + * @param now The timestamp at which to update the counter. + */ + public void updateCounters(long now) { + long lastUpdate = this.lastCounterUpdate; + this.lastCounterUpdate = now; + long delta = now - lastUpdate; + + if (delta > 0) { + float demand = this.currentCpuDemand; + float rate = this.currentCpuSupplied; + float capacity = this.maxCapacity; + + final float factor = this.cpuFrequencyInv * delta; + + this.performanceCounters.addCpuActiveTime(Math.round(rate * factor)); + this.performanceCounters.addCpuIdleTime(Math.round((capacity - rate) * factor)); + this.performanceCounters.addCpuStealTime(Math.round((demand - rate) * factor)); + } + + this.performanceCounters.setCpuDemand(this.currentCpuDemand); + this.performanceCounters.setCpuSupply(this.currentCpuSupplied); + this.performanceCounters.setCpuCapacity(this.maxCapacity); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // FlowGraph Related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Push new demand to the psu + */ + @Override + public void pushDemand(FlowEdge supplierEdge, float newPowerDemand) { + this.psuEdge.pushDemand(newPowerDemand); + } + + /** + * Push updated supply to the mux + */ + @Override + public void pushSupply(FlowEdge consumerEdge, float newCpuSupply) { + updateCounters(); + this.currentCpuSupplied = newCpuSupply; + this.muxEdge.pushSupply(newCpuSupply); + } + + /** + * Handle new demand coming in from the mux + */ + @Override + public void handleDemand(FlowEdge consumerEdge, float newCpuDemand) { + if (newCpuDemand == this.currentCpuDemand) { + return; + } + + updateCounters(); + this.currentCpuDemand = newCpuDemand; + this.currentCpuUtilization = this.currentCpuDemand / this.maxCapacity; + + this.invalidate(); + } + + /** + * Handle updated supply from the psu + */ + @Override + public void handleSupply(FlowEdge supplierEdge, float newPowerSupply) { + // TODO: Implement this + updateCounters(); + this.currentPowerSupplied = newPowerSupply; + + this.invalidate(); + } + + /** + * Add a connection to the mux + */ + @Override + public void addConsumerEdge(FlowEdge consumerEdge) { + this.muxEdge = consumerEdge; + } + + /** + * Add a connection to the psu + */ + @Override + public void addSupplierEdge(FlowEdge supplierEdge) { + this.psuEdge = supplierEdge; + } + + /** + * Remove the connection to the mux + */ + @Override + public void removeConsumerEdge(FlowEdge consumerEdge) { + this.muxEdge = null; + this.invalidate(); + } + + /** + * Remove the connection to the psu + */ + @Override + public void removeSupplierEdge(FlowEdge supplierEdge) { + this.psuEdge = null; + this.invalidate(); + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimNetworkAdapter.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimNetworkAdapter.java deleted file mode 100644 index 1c16ceff..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimNetworkAdapter.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.device; - -import org.opendc.simulator.compute.SimMachine; -import org.opendc.simulator.network.SimNetworkPort; - -/** - * A simulated network interface card (NIC or network adapter) that can be attached to a {@link SimMachine}. - */ -public abstract class SimNetworkAdapter extends SimNetworkPort implements SimPeripheral { - /** - * Return the unidirectional bandwidth of the network adapter (in Mbps). - */ - public abstract double getBandwidth(); -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimPeripheral.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimPeripheral.java deleted file mode 100644 index 40bd268b..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/device/SimPeripheral.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.device; - -import org.opendc.simulator.compute.SimMachine; - -/** - * A component that can be attached to a {@link SimMachine}. - * <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 {} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisor.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisor.java deleted file mode 100644 index 42750b0f..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisor.java +++ /dev/null @@ -1,933 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.kernel; - -import java.time.InstantSource; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.SplittableRandom; -import java.util.function.Consumer; -import org.opendc.simulator.compute.SimAbstractMachine; -import org.opendc.simulator.compute.SimMachine; -import org.opendc.simulator.compute.SimMachineContext; -import org.opendc.simulator.compute.SimMemory; -import org.opendc.simulator.compute.SimNetworkInterface; -import org.opendc.simulator.compute.SimProcessingUnit; -import org.opendc.simulator.compute.SimStorageInterface; -import org.opendc.simulator.compute.device.SimPeripheral; -import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernor; -import org.opendc.simulator.compute.kernel.cpufreq.ScalingGovernorFactory; -import org.opendc.simulator.compute.kernel.cpufreq.ScalingPolicy; -import org.opendc.simulator.compute.kernel.interference.VmInterferenceDomain; -import org.opendc.simulator.compute.kernel.interference.VmInterferenceMember; -import org.opendc.simulator.compute.kernel.interference.VmInterferenceProfile; -import org.opendc.simulator.compute.model.Cpu; -import org.opendc.simulator.compute.model.MachineModel; -import org.opendc.simulator.compute.workload.SimWorkload; -import org.opendc.simulator.flow2.FlowGraph; -import org.opendc.simulator.flow2.FlowStage; -import org.opendc.simulator.flow2.FlowStageLogic; -import org.opendc.simulator.flow2.InHandler; -import org.opendc.simulator.flow2.InPort; -import org.opendc.simulator.flow2.Inlet; -import org.opendc.simulator.flow2.OutHandler; -import org.opendc.simulator.flow2.OutPort; -import org.opendc.simulator.flow2.mux.FlowMultiplexer; -import org.opendc.simulator.flow2.mux.FlowMultiplexerFactory; - -/** - * A SimHypervisor facilitates the execution of multiple concurrent {@link SimWorkload}s, while acting as a single - * workload to another {@link SimMachine}. - */ -public final class SimHypervisor implements SimWorkload { - private final FlowMultiplexerFactory muxFactory; - private final SplittableRandom random; - private final ScalingGovernorFactory scalingGovernorFactory; - private final VmInterferenceDomain interferenceDomain; - - private SimHyperVisorContext activeContext; - private final ArrayList<SimVirtualMachine> vms = new ArrayList<>(); - private final HvCounters counters = new HvCounters(); - - @Override - public void setOffset(long now) {} - - /** - * Construct a {@link SimHypervisor} instance. - * - * @param muxFactory The factory for the {@link FlowMultiplexer} to multiplex the workloads. - * @param random A randomness generator for the interference calculations. - * @param scalingGovernorFactory The factory for the scaling governor to use for scaling the CPU frequency. - * @param interferenceDomain The interference domain to which the hypervisor belongs. - */ - private SimHypervisor( - FlowMultiplexerFactory muxFactory, - SplittableRandom random, - ScalingGovernorFactory scalingGovernorFactory, - VmInterferenceDomain interferenceDomain) { - this.muxFactory = muxFactory; - this.random = random; - this.scalingGovernorFactory = scalingGovernorFactory; - this.interferenceDomain = interferenceDomain; - } - - /** - * Create a {@link SimHypervisor} instance. - * - * @param muxFactory The factory for the {@link FlowMultiplexer} to multiplex the workloads. - * @param random A randomness generator for the interference calculations. - * @param scalingGovernorFactory The factory for the scaling governor to use for scaling the CPU frequency. - * @param interferenceDomain The interference domain to which the hypervisor belongs. - */ - public static SimHypervisor create( - FlowMultiplexerFactory muxFactory, - SplittableRandom random, - ScalingGovernorFactory scalingGovernorFactory, - VmInterferenceDomain interferenceDomain) { - return new SimHypervisor(muxFactory, random, scalingGovernorFactory, interferenceDomain); - } - - /** - * Create a {@link SimHypervisor} instance with a default interference domain. - * - * @param muxFactory The factory for the {@link FlowMultiplexer} to multiplex the workloads. - * @param random A randomness generator for the interference calculations. - * @param scalingGovernorFactory The factory for the scaling governor to use for scaling the CPU frequency. - */ - public static SimHypervisor create( - FlowMultiplexerFactory muxFactory, SplittableRandom random, ScalingGovernorFactory scalingGovernorFactory) { - return create(muxFactory, random, scalingGovernorFactory, new VmInterferenceDomain()); - } - - /** - * Create a {@link SimHypervisor} instance with a default interference domain and scaling governor. - * - * @param muxFactory The factory for the {@link FlowMultiplexer} to multiplex the workloads. - * @param random A randomness generator for the interference calculations. - */ - public static SimHypervisor create(FlowMultiplexerFactory muxFactory, SplittableRandom random) { - return create(muxFactory, random, null); - } - - /** - * Return the performance counters of the hypervisor. - */ - public SimHypervisorCounters getCounters() { - return counters; - } - - /** - * Return the virtual machines running on this hypervisor. - */ - public List<? 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"); - } - - SimVirtualMachine vm = new SimVirtualMachine(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. - ((SimVirtualMachine) machine).close(); - } - } - - /** - * Return the CPU capacity of the hypervisor in MHz. - */ - public double getCpuCapacity() { - final SimHyperVisorContext context = activeContext; - - if (context == null) { - return 0.0; - } - - return context.previousCapacity; - } - - /** - * The CPU demand of the hypervisor in MHz. - */ - public double getCpuDemand() { - final SimHyperVisorContext context = activeContext; - - if (context == null) { - return 0.0; - } - - return context.previousDemand; - } - - /** - * The CPU usage of the hypervisor in MHz. - */ - public double getCpuUsage() { - final SimHyperVisorContext context = activeContext; - - if (context == null) { - return 0.0; - } - - return context.previousRate; - } - - /** - * Determine whether the specified machine characterized by <code>model</code> can fit on this hypervisor at this - * moment. - */ - public boolean canFit(MachineModel model) { - final SimHyperVisorContext context = activeContext; - if (context == null) { - return false; - } - - final FlowMultiplexer multiplexer = context.multiplexer; - return (multiplexer.getMaxInputs() - multiplexer.getInputCount()) >= 1; - } - - @Override - public void onStart(SimMachineContext ctx) { - final SimHyperVisorContext context = - new SimHyperVisorContext(ctx, muxFactory, scalingGovernorFactory, counters); - context.start(); - activeContext = context; - } - - @Override - public void onStop(SimMachineContext ctx) { - final SimHyperVisorContext context = activeContext; - if (context != null) { - activeContext = null; - context.stop(); - } - } - - @Override - public void makeSnapshot(long now) { - throw new UnsupportedOperationException("Unable to snapshot hypervisor"); - } - - @Override - public SimWorkload getSnapshot() { - throw new UnsupportedOperationException("Unable to snapshot hypervisor"); - } - - @Override - public void createCheckpointModel() { - throw new UnsupportedOperationException("Unable to create a checkpointing system for a hypervisor"); - } - - @Override - public long getCheckpointInterval() { - return -1; - } - - @Override - public long getCheckpointDuration() { - return -1; - } - - @Override - public double getCheckpointIntervalScaling() { - return -1; - } - - /** - * The context which carries the state when the hypervisor is running on a machine. - */ - private static final class SimHyperVisorContext implements FlowStageLogic { - private final SimMachineContext ctx; - private final FlowMultiplexer multiplexer; - private final FlowStage stage; - private final ScalingGovernor scalingGovernor; - private final InstantSource clock; - private final HvCounters counters; - - private long lastCounterUpdate; - private final double d; - private float previousDemand; - private float previousRate; - private float previousCapacity; - - private SimHyperVisorContext( - SimMachineContext ctx, - FlowMultiplexerFactory muxFactory, - ScalingGovernorFactory scalingGovernorFactory, - HvCounters counters) { - - this.ctx = ctx; - this.counters = counters; - - final FlowGraph graph = ctx.getGraph(); - this.multiplexer = muxFactory.newMultiplexer(graph); - this.stage = graph.newStage(this); - this.clock = graph.getEngine().getClock(); - - this.lastCounterUpdate = clock.millis(); - - final SimProcessingUnit cpu = ctx.getCpu(); - - if (scalingGovernorFactory != null) { - this.scalingGovernor = scalingGovernorFactory.newGovernor(new ScalingPolicyImpl(cpu)); - } else { - this.scalingGovernor = null; - } - - this.d = 1 / cpu.getFrequency(); - } - - /** - * Start the hypervisor on a new machine. - */ - void start() { - final FlowGraph graph = ctx.getGraph(); - final FlowMultiplexer multiplexer = this.multiplexer; - - graph.connect(multiplexer.newOutput(), ctx.getCpu().getInput()); - - if (this.scalingGovernor != null) { - this.scalingGovernor.onStart(); - } - } - - /** - * Stop the hypervisor. - */ - void stop() { - // Synchronize the counters before stopping the hypervisor. Otherwise, the last report is missed. - updateCounters(clock.millis()); - - stage.close(); - } - - /** - * Invalidate the {@link FlowStage} of the hypervisor. - */ - void invalidate() { - stage.invalidate(); - } - - /** - * Update the performance counters of the hypervisor. - * - * @param now The timestamp at which to update the counter. - */ - void updateCounters(long now) { - long lastUpdate = this.lastCounterUpdate; - this.lastCounterUpdate = now; - long delta = now - lastUpdate; - - if (delta > 0) { - final HvCounters counters = this.counters; - - float demand = previousDemand; - float rate = previousRate; - float capacity = previousCapacity; - - final double factor = this.d * delta; - - counters.cpuActiveTime += Math.round(rate * factor); - counters.cpuIdleTime += Math.round((capacity - rate) * factor); - counters.cpuStealTime += Math.round((demand - rate) * factor); - } - } - - /** - * Update the performance counters of the hypervisor. - */ - void updateCounters() { - updateCounters(clock.millis()); - } - - @Override - public long onUpdate(FlowStage ctx, long now) { - updateCounters(now); - - final FlowMultiplexer multiplexer = this.multiplexer; - final ScalingGovernor scalingGovernors = this.scalingGovernor; - - float demand = multiplexer.getDemand(); - float rate = multiplexer.getRate(); - float capacity = multiplexer.getCapacity(); - - this.previousDemand = demand; - this.previousRate = rate; - this.previousCapacity = capacity; - - double load = rate / Math.min(1.0, capacity); - - if (scalingGovernor != null) { - scalingGovernor.onLimit(load); - } - - return Long.MAX_VALUE; - } - } - - /** - * A {@link ScalingPolicy} for a physical CPU of the hypervisor. - */ - private static final class ScalingPolicyImpl implements ScalingPolicy { - private final SimProcessingUnit cpu; - - private ScalingPolicyImpl(SimProcessingUnit cpu) { - this.cpu = cpu; - } - - @Override - public SimProcessingUnit getCpu() { - return cpu; - } - - @Override - public double getTarget() { - return cpu.getFrequency(); - } - - @Override - public void setTarget(double target) { - cpu.setFrequency(target); - } - - @Override - public double getMin() { - return 0; - } - - @Override - public double getMax() { - return cpu.getCpuModel().getTotalCapacity(); - } - } - - /** - * A virtual machine running on the hypervisor. - */ - public class SimVirtualMachine extends SimAbstractMachine { - private boolean isClosed; - private final VmCounters counters = new VmCounters(this); - - private SimVirtualMachine(MachineModel model) { - super(model); - } - - public SimHypervisorCounters getCounters() { - return counters; - } - - public double getCpuDemand() { - final VmContext context = (VmContext) getActiveContext(); - - if (context == null) { - return 0.0; - } - - return context.previousDemand; - } - - public double getCpuUsage() { - final VmContext context = (VmContext) getActiveContext(); - - if (context == null) { - return 0.0; - } - - return context.usage; - } - - public double getCpuCapacity() { - final VmContext context = (VmContext) getActiveContext(); - - if (context == null) { - return 0.0; - } - - return context.previousCapacity; - } - - @Override - public List<? extends SimPeripheral> getPeripherals() { - return Collections.emptyList(); - } - - @Override - protected SimAbstractMachineContext createContext( - SimWorkload workload, Map<String, Object> meta, Consumer<Exception> completion) { - if (isClosed) { - throw new IllegalStateException("Virtual machine does not exist anymore"); - } - - final SimHyperVisorContext context = activeContext; - if (context == null) { - throw new IllegalStateException("Hypervisor is inactive"); - } - - return new VmContext( - context, - this, - random, - interferenceDomain, - counters, - SimHypervisor.this.counters, - workload, - meta, - completion); - } - - @Override - public SimAbstractMachineContext getActiveContext() { - return super.getActiveContext(); - } - - void close() { - if (isClosed) { - return; - } - - isClosed = true; - cancel(); - } - } - - /** - * A {@link SimAbstractMachine.SimAbstractMachineContext} for a virtual machine instance. - */ - private static final class VmContext extends SimAbstractMachine.SimAbstractMachineContext - implements FlowStageLogic { - private final SimHyperVisorContext simHyperVisorContext; - private final SplittableRandom random; - private final VmCounters vmCounters; - private final HvCounters hvCounters; - private final VmInterferenceMember interferenceMember; - private final FlowStage stage; - private final FlowMultiplexer multiplexer; - private final InstantSource clock; - - private final VCpu cpu; - private final SimAbstractMachine.Memory memory; - private final List<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( - SimHyperVisorContext simHyperVisorContext, - SimVirtualMachine machine, - SplittableRandom random, - VmInterferenceDomain interferenceDomain, - VmCounters vmCounters, - HvCounters hvCounters, - SimWorkload workload, - Map<String, Object> meta, - Consumer<Exception> completion) { - super(machine, workload, meta, completion); - - this.simHyperVisorContext = simHyperVisorContext; - this.random = random; - this.vmCounters = vmCounters; - this.hvCounters = hvCounters; - this.clock = simHyperVisorContext.clock; - - final VmInterferenceProfile interferenceProfile = (VmInterferenceProfile) meta.get("interference-profile"); - VmInterferenceMember interferenceMember = null; - if (interferenceDomain != null && interferenceProfile != null) { - interferenceMember = interferenceDomain.join(interferenceProfile); - interferenceMember.activate(); - } - this.interferenceMember = interferenceMember; - - final FlowGraph graph = simHyperVisorContext.ctx.getGraph(); - final FlowStage stage = graph.newStage(this); - this.stage = stage; - this.lastUpdate = clock.millis(); - this.lastCounterUpdate = clock.millis(); - - final FlowMultiplexer multiplexer = simHyperVisorContext.multiplexer; - this.multiplexer = multiplexer; - - final MachineModel model = machine.getModel(); - final Cpu cpuModel = model.getCpu(); - final Inlet[] muxInlets = new Inlet[1]; - - this.muxInlets = muxInlets; - - final Inlet muxInlet = multiplexer.newInput(); - muxInlets[0] = muxInlet; - - final InPort input = stage.getInlet("cpu"); - final OutPort output = stage.getOutlet("mux"); - - final Handler handler = new Handler(this, input, output); - input.setHandler(handler); - output.setHandler(handler); - - this.cpu = new VCpu(cpuModel, input); - - graph.connect(output, muxInlet); - - this.d = 1 / cpuModel.getTotalCapacity(); - - this.memory = new SimAbstractMachine.Memory(graph, model.getMemory()); - - int netIndex = 0; - final ArrayList<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; // time between updates - - if (delta > 0) { - final VmCounters counters = this.vmCounters; - - float demand = this.previousDemand; - float rate = this.usage; - float capacity = this.previousCapacity; - - final double factor = this.d * delta; // time between divided by total capacity - final double active = rate * factor; - - counters.cpuActiveTime += Math.round(active); - counters.cpuIdleTime += Math.round((capacity - rate) * factor); - counters.cpuStealTime += Math.round((demand - rate) * factor); - } - } - - /** - * Update the performance counters of the virtual machine. - */ - void updateCounters() { - updateCounters(clock.millis()); - } - - @Override - public FlowGraph getGraph() { - return stage.getGraph(); - } - - @Override - public SimProcessingUnit getCpu() { - return cpu; - } - - @Override - public SimMemory getMemory() { - return memory; - } - - @Override - public List<? 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) - simHyperVisorContext.invalidate(); - - return Long.MAX_VALUE; - } - - @Override - protected void doCancel() { - super.doCancel(); - - // Synchronize the counters before stopping the hypervisor. Otherwise, the last report is missed. - updateCounters(clock.millis()); - - stage.close(); - - final FlowMultiplexer multiplexer = this.multiplexer; - for (Inlet muxInlet : muxInlets) { - multiplexer.releaseInput(muxInlet); - } - - final VmInterferenceMember interferenceMember = this.interferenceMember; - if (interferenceMember != null) { - interferenceMember.deactivate(); - } - } - } - - /** - * A {@link SimProcessingUnit} of a virtual machine. - */ - private static final class VCpu implements SimProcessingUnit { - private final Cpu model; - private final InPort input; - - private VCpu(Cpu model, InPort input) { - this.model = model; - this.input = input; - - input.pull((float) model.getTotalCapacity()); - } - - @Override - public double getFrequency() { - return input.getCapacity(); - } - - @Override - public void setFrequency(double frequency) { - input.pull((float) frequency); - } - - @Override - public double getDemand() { - return input.getDemand(); - } - - @Override - public double getSpeed() { - return input.getRate(); - } - - @Override - public Cpu getCpuModel() { - return model; - } - - @Override - public Inlet getInput() { - return input; - } - - @Override - public String toString() { - return "SimHypervisor.VCpu[model" + model + "]"; - } - } - - /** - * A handler for forwarding flow between an inlet and outlet. - */ - private static class Handler implements InHandler, OutHandler { - private final InPort input; - private final OutPort output; - private final VmContext context; - - private Handler(VmContext context, InPort input, OutPort output) { - this.context = context; - this.input = input; - this.output = output; - } - - @Override - public void onPush(InPort port, float demand) { - context.demand += -port.getDemand() + demand; - - output.push(demand); - } - - @Override - public void onUpstreamFinish(InPort port, Throwable cause) { - context.demand -= port.getDemand(); - - output.push(0.f); - } - - @Override - public float getRate(InPort port) { - return output.getRate(); - } - - @Override - public void onPull(OutPort port, float capacity) { - context.capacity += -port.getCapacity() + capacity; - - input.pull(capacity); - } - - @Override - public void onDownstreamFinish(OutPort port, Throwable cause) { - context.capacity -= port.getCapacity(); - - input.pull(0.f); - } - } - - /** - * Implementation of {@link SimHypervisorCounters} for the hypervisor. - */ - private class HvCounters implements SimHypervisorCounters { - private long cpuActiveTime; - private long cpuIdleTime; - private long cpuStealTime; - private long cpuLostTime; - - @Override - public long getCpuActiveTime() { - return cpuActiveTime; - } - - @Override - public long getCpuIdleTime() { - return cpuIdleTime; - } - - @Override - public long getCpuStealTime() { - return cpuStealTime; - } - - @Override - public long getCpuLostTime() { - return cpuLostTime; - } - - @Override - public void sync() { - final SimHyperVisorContext context = activeContext; - - if (context != null) { - context.updateCounters(); - } - } - } - - /** - * Implementation of {@link SimHypervisorCounters} for the virtual machine. - */ - private static class VmCounters implements SimHypervisorCounters { - private final SimVirtualMachine vm; - private long cpuActiveTime; - private long cpuIdleTime; - private long cpuStealTime; - private long cpuLostTime; - - private VmCounters(SimVirtualMachine vm) { - this.vm = vm; - } - - @Override - public long getCpuActiveTime() { - return cpuActiveTime; - } - - @Override - public long getCpuIdleTime() { - return cpuIdleTime; - } - - @Override - public long getCpuStealTime() { - return cpuStealTime; - } - - @Override - public long getCpuLostTime() { - return cpuLostTime; - } - - @Override - public void sync() { - final VmContext context = (VmContext) vm.getActiveContext(); - - if (context != null) { - context.updateCounters(); - } - } - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisorCounters.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisorCounters.java deleted file mode 100644 index fc77e9d6..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisorCounters.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.kernel; - -/** - * Performance counters of a {@link SimHypervisor}. - */ -public interface SimHypervisorCounters { - /** - * Return the amount of time (in milliseconds) the CPUs of the hypervisor were actively running. - */ - long getCpuActiveTime(); - - /** - * Return the amount of time (in milliseconds) the CPUs of the hypervisor were idle. - */ - long getCpuIdleTime(); - - /** - * Return the amount of CPU time (in milliseconds) that virtual machines were ready to run, but were not able to. - */ - long getCpuStealTime(); - - /** - * Return the amount of CPU time (in milliseconds) that was lost due to interference between virtual machines. - */ - long getCpuLostTime(); - - /** - * Synchronize the counter values. - */ - void sync(); -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernor.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernor.java deleted file mode 100644 index 69a371e1..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernor.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.kernel.cpufreq; - -/** - * A [ScalingGovernor] in the CPUFreq subsystem of OpenDC is responsible for scaling the frequency of simulated CPUs - * independent of the particular implementation of the CPU. - * - * <p> - * Each of the scaling governors implements a single, possibly parametrized, performance scaling algorithm. - * - * @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 { - /** - * This method is invoked when the governor is started. - */ - default void onStart() {} - - /** - * This method is invoked when the governor should re-decide the frequency limits. - * - * @param load The load of the system. - */ - default void onLimit(double load) {} -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernors.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernors.java deleted file mode 100644 index 2b10ae59..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernors.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.kernel.cpufreq; - -/** - * Collection of common {@link ScalingGovernor} implementations. - */ -public class ScalingGovernors { - private ScalingGovernors() {} - - /** - * Return a {@link ScalingGovernorFactory} for the <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/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingPolicy.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingPolicy.java deleted file mode 100644 index 0cdb7a0b..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingPolicy.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.kernel.cpufreq; - -import org.opendc.simulator.compute.SimProcessingUnit; - -/** - * An interface that holds the state managed by a {@link ScalingGovernor} and used by the underlying machine to control - * the CPU frequencies. - */ -public interface ScalingPolicy { - /** - * The processing unit that is associated with this policy. - */ - SimProcessingUnit getCpu(); - - /** - * Return the target frequency which the CPU should attempt to attain. - */ - double getTarget(); - - /** - * Set the target frequency which the CPU should attempt to attain. - */ - void setTarget(double target); - - /** - * Return the minimum frequency to which the CPU may scale. - */ - double getMin(); - - /** - * Return the maximum frequency to which the CPU may scale. - */ - double getMax(); -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.java deleted file mode 100644 index cc671379..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceDomain.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.kernel.interference; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collections; -import java.util.WeakHashMap; - -/** - * A domain where virtual machines may incur performance variability due to operating on the same resource and - * therefore causing interference. - */ -public final class VmInterferenceDomain { - /** - * A cache to maintain a mapping between the active profiles in this domain. - */ - private final WeakHashMap<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 deleted file mode 100644 index 64cd5077..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceMember.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.kernel.interference; - -import java.util.Arrays; -import java.util.SplittableRandom; -import org.jetbrains.annotations.NotNull; - -/** - * A participant of an interference domain. - */ -public final class VmInterferenceMember implements Comparable<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 deleted file mode 100644 index e2093266..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceModel.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.kernel.interference; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; -import org.jetbrains.annotations.Nullable; - -/** - * An interference model that models the resource interference between virtual machines on a host. - */ -public final class VmInterferenceModel { - private final Map<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/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceProfile.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceProfile.java deleted file mode 100644 index 3f0c0a88..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/interference/VmInterferenceProfile.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.kernel.interference; - -/** - * A profile of a particular virtual machine describing its interference pattern with other virtual machines. - */ -public final class VmInterferenceProfile { - private final VmInterferenceModel model; - private final int id; - private final int[] membership; - private final int[][] members; - private final double[] targets; - private final double[] scores; - - /** - * Construct a {@link VmInterferenceProfile}. - */ - VmInterferenceProfile( - VmInterferenceModel model, int id, int[] membership, int[][] members, double[] targets, double[] scores) { - this.model = model; - this.id = id; - this.membership = membership; - this.members = members; - this.targets = targets; - this.scores = scores; - } - - /** - * Create a new {@link VmInterferenceMember} based on this profile for the specified <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/machine/PerformanceCounters.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/machine/PerformanceCounters.java new file mode 100644 index 00000000..b1e30e5c --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/machine/PerformanceCounters.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2024 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.machine; + +public class PerformanceCounters { + private long cpuActiveTime = 0; + private long cpuIdleTime = 0; + private long cpuStealTime = 0; + private long cpuLostTime = 0; + + private float cpuCapacity = 0.0f; + private float cpuDemand = 0.0f; + private float cpuSupply = 0.0f; + + public long getCpuActiveTime() { + return cpuActiveTime; + } + + public void setCpuActiveTime(long cpuActiveTime) { + this.cpuActiveTime = cpuActiveTime; + } + + public void addCpuActiveTime(long cpuActiveTime) { + this.cpuActiveTime += cpuActiveTime; + } + + public long getCpuIdleTime() { + return cpuIdleTime; + } + + public void setCpuIdleTime(long cpuIdleTime) { + this.cpuIdleTime = cpuIdleTime; + } + + public void addCpuIdleTime(long cpuIdleTime) { + this.cpuIdleTime += cpuIdleTime; + } + + public long getCpuStealTime() { + return cpuStealTime; + } + + public void setCpuStealTime(long cpuStealTime) { + this.cpuStealTime = cpuStealTime; + } + + public void addCpuStealTime(long cpuStealTime) { + this.cpuStealTime += cpuStealTime; + } + + public long getCpuLostTime() { + return cpuLostTime; + } + + public void setCpuLostTime(long cpuLostTime) { + this.cpuLostTime = cpuLostTime; + } + + public float getCpuCapacity() { + return cpuCapacity; + } + + public void setCpuCapacity(float cpuCapacity) { + this.cpuCapacity = cpuCapacity; + } + + public float getCpuDemand() { + return cpuDemand; + } + + public void setCpuDemand(float cpuDemand) { + this.cpuDemand = cpuDemand; + } + + public float getCpuSupply() { + return cpuSupply; + } + + public void setCpuSupply(float cpuSupply) { + this.cpuSupply = cpuSupply; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/machine/SimMachine.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/machine/SimMachine.java new file mode 100644 index 00000000..00a69efe --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/machine/SimMachine.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2024 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.machine; + +import java.time.InstantSource; +import java.util.function.Consumer; +import org.opendc.simulator.Multiplexer; +import org.opendc.simulator.compute.cpu.CpuPowerModel; +import org.opendc.simulator.compute.cpu.SimCpu; +import org.opendc.simulator.compute.memory.Memory; +import org.opendc.simulator.compute.models.MachineModel; +import org.opendc.simulator.compute.power.SimPsu; +import org.opendc.simulator.compute.workload.SimWorkload; +import org.opendc.simulator.compute.workload.Workload; +import org.opendc.simulator.engine.FlowGraph; + +/** + * A machine that is able to execute {@link SimWorkload} objects. + */ +public class SimMachine { + private final MachineModel machineModel; + private final FlowGraph graph; + + private final InstantSource clock; + + private SimCpu cpu; + private Multiplexer cpuMux; + private SimPsu psu; + private Memory memory; + + private Consumer<Exception> completion; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Basic Getters and Setters + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public PerformanceCounters getPerformanceCounters() { + return this.cpu.getPerformanceCounters(); + } + + public MachineModel getMachineModel() { + return machineModel; + } + + public FlowGraph getGraph() { + return graph; + } + + public InstantSource getClock() { + return clock; + } + + public SimCpu getCpu() { + return cpu; + } + + public Multiplexer getCpuMux() { + return cpuMux; + } + + public Memory getMemory() { + return memory; + } + + public SimPsu getPsu() { + return psu; + } + + /** + * Return the CPU capacity of the hypervisor in MHz. + */ + public double getCpuCapacity() { + return 0.0; + } + + /** + * The CPU demand of the hypervisor in MHz. + */ + public double getCpuDemand() { + return 0.0; + } + + /** + * The CPU usage of the hypervisor in MHz. + */ + public double getCpuUsage() { + return 0.0; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public SimMachine( + FlowGraph graph, MachineModel machineModel, CpuPowerModel cpuPowerModel, Consumer<Exception> completion) { + this.graph = graph; + this.machineModel = machineModel; + this.clock = graph.getEngine().getClock(); + + // Create the psu and cpu and connect them + this.psu = new SimPsu(graph); + this.cpu = new SimCpu(graph, this.machineModel.getCpu(), 0); + + graph.addEdge(this.cpu, this.psu); + + this.memory = new Memory(graph, this.machineModel.getMemory()); + + // Create a Multiplexer and add the cpu as supplier + this.cpuMux = new Multiplexer(this.graph); + graph.addEdge(this.cpuMux, this.cpu); + + this.completion = completion; + } + + public void shutdown() { + shutdown(null); + } + + /** + * Close all related hardware + */ + public void shutdown(Exception cause) { + this.graph.removeNode(this.psu); + this.psu = null; + + this.graph.removeNode(this.cpu); + this.cpu = null; + + this.graph.removeNode(this.cpuMux); + this.cpuMux = null; + + this.memory = null; + + this.completion.accept(cause); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Workload related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Determine whether the specified machine characterized by <code>model</code> can fit on this hypervisor at this + * moment. + * TODO: This currently alwasy returns True, maybe remove? + */ + public boolean canFit(MachineModel model) { + return true; + } + + /** + * Create a Virtual Machine, and start the given workload on it. + * + * @param workload + * @param completion + * @return + */ + public VirtualMachine startWorkload(Workload workload, Consumer<Exception> completion) { + final VirtualMachine vm = new VirtualMachine(this); + + vm.startWorkload(workload, completion); + + return vm; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/machine/VirtualMachine.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/machine/VirtualMachine.java new file mode 100644 index 00000000..3bc3d2b4 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/machine/VirtualMachine.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2024 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.machine; + +import java.util.function.Consumer; +import org.opendc.simulator.compute.cpu.SimCpu; +import org.opendc.simulator.compute.workload.SimWorkload; +import org.opendc.simulator.compute.workload.Workload; +import org.opendc.simulator.engine.FlowConsumer; +import org.opendc.simulator.engine.FlowEdge; +import org.opendc.simulator.engine.FlowGraph; +import org.opendc.simulator.engine.FlowNode; +import org.opendc.simulator.engine.FlowSupplier; + +/* + A virtual Machine created to run a single workload +*/ +public class VirtualMachine extends FlowNode implements FlowConsumer, FlowSupplier { + private SimMachine machine; + + private SimWorkload activeWorkload; + + private long lastUpdate; + private final double d; + + private FlowEdge cpuEdge; // The edge to the cpu + private FlowEdge workloadEdge; // The edge to the workload + + private float cpuDemand; + private float cpuSupply; + private float cpuCapacity; + + private PerformanceCounters performanceCounters = new PerformanceCounters(); + + private Consumer<Exception> completion; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Basic Getters and Setters + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public PerformanceCounters getPerformanceCounters() { + return performanceCounters; + } + + public SimWorkload getActiveWorkload() { + return activeWorkload; + } + + public float getDemand() { + return cpuDemand; + } + + public void setDemand(float demand) { + this.cpuDemand = demand; + } + + public float getCpuCapacity() { + return cpuCapacity; + } + + public void setCpuCapacity(float cpuCapacity) { + this.cpuCapacity = cpuCapacity; + } + + public FlowGraph getGraph() { + return this.parentGraph; + } + + public SimCpu getCpu() { + return machine.getCpu(); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public VirtualMachine(SimMachine machine) { + super(machine.getGraph()); + this.machine = machine; + this.clock = this.machine.getClock(); + + this.parentGraph = machine.getGraph(); + this.parentGraph.addEdge(this, this.machine.getCpuMux()); + + this.lastUpdate = clock.millis(); + this.lastUpdate = clock.millis(); + + this.d = 1 / machine.getCpu().getFrequency(); + } + + public void shutdown() { + this.shutdown(null); + } + + public void shutdown(Exception cause) { + if (this.nodeState == NodeState.CLOSED) { + return; + } + + super.closeNode(); + + this.activeWorkload = null; + this.performanceCounters = null; + + this.completion.accept(cause); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Workload related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public void startWorkload(Workload workload, Consumer<Exception> completion) { + this.completion = completion; + this.activeWorkload = workload.startWorkload(this, this.clock.millis()); + } + + public void updateCounters(long now) { + long lastUpdate = this.lastUpdate; + this.lastUpdate = now; + long delta = now - lastUpdate; + + if (delta > 0) { + final double factor = this.d * delta; + + this.performanceCounters.addCpuActiveTime(Math.round(this.cpuSupply * factor)); + this.performanceCounters.setCpuIdleTime(Math.round((this.cpuCapacity - this.cpuSupply) * factor)); + this.performanceCounters.addCpuStealTime(Math.round((this.cpuDemand - this.cpuSupply) * factor)); + } + + this.performanceCounters.setCpuDemand(this.cpuDemand); + this.performanceCounters.setCpuSupply(this.cpuSupply); + this.performanceCounters.setCpuCapacity(this.cpuCapacity); + } + + @Override + public long onUpdate(long now) { + updateCounters(now); + + return Long.MAX_VALUE; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // FlowGraph Related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Add an edge to the workload + * TODO: maybe add a check if there is already an edge + */ + @Override + public void addConsumerEdge(FlowEdge consumerEdge) { + this.workloadEdge = consumerEdge; + } + + /** + * Add an edge to the cpuMux + * TODO: maybe add a check if there is already an edge + */ + @Override + public void addSupplierEdge(FlowEdge supplierEdge) { + this.cpuEdge = supplierEdge; + } + + /** + * Push demand to the cpuMux if the demand has changed + **/ + @Override + public void pushDemand(FlowEdge supplierEdge, float newDemand) { + this.cpuEdge.pushDemand(newDemand); + } + + /** + * Push supply to the workload if the supply has changed + **/ + @Override + public void pushSupply(FlowEdge consumerEdge, float newSupply) { + this.workloadEdge.pushDemand(newSupply); + } + + /** + * Handle new demand from the workload by sending it through to the cpuMux + **/ + @Override + public void handleDemand(FlowEdge consumerEdge, float newDemand) { + if (this.cpuDemand == newDemand) { + return; + } + + updateCounters(this.clock.millis()); + this.cpuDemand = newDemand; + + pushDemand(this.cpuEdge, newDemand); + } + + /** + * Handle a new supply pushed by the cpuMux by sending it through to the workload + **/ + @Override + public void handleSupply(FlowEdge supplierEdge, float newCpuSupply) { + if (newCpuSupply == this.cpuSupply) { + return; + } + + updateCounters(this.clock.millis()); + this.cpuSupply = newCpuSupply; + + pushSupply(this.workloadEdge, newCpuSupply); + } + + @Override + public void removeConsumerEdge(FlowEdge consumerEdge) { + this.workloadEdge = null; + this.shutdown(); + } + + @Override + public float getCapacity() { + return this.cpuCapacity; + } + + @Override + public void removeSupplierEdge(FlowEdge supplierEdge) { + this.cpuEdge = null; + this.shutdown(); + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimNetworkInterface.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/memory/Memory.java index 4b623e59..2656a99a 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimNetworkInterface.java +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/memory/Memory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 AtLarge Research + * Copyright (c) 2024 AtLarge Research * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,32 +20,36 @@ * SOFTWARE. */ -package org.opendc.simulator.compute; +package org.opendc.simulator.compute.memory; -import org.opendc.simulator.flow2.Inlet; -import org.opendc.simulator.flow2.Outlet; +import org.opendc.simulator.compute.models.MemoryUnit; +import org.opendc.simulator.engine.FlowGraph; /** - * A firmware interface to a network adapter. + * The [SimMemory] implementation for a machine. */ -public interface SimNetworkInterface { - /** - * Return the name of the network interface. - */ - String getName(); - - /** - * Return the unidirectional bandwidth of the network interface in Mbps. - */ - double getBandwidth(); - - /** - * Return the inlet for the "transmit" channel of the network interface. - */ - Inlet getTx(); - - /** - * Return the outlet for the "receive" channel of the network interface. - */ - Outlet getRx(); +public final class Memory { + // private final SimpleFlowSink sink; + private final MemoryUnit memoryUnit; + + public Memory(FlowGraph graph, MemoryUnit memoryUnit) { + + this.memoryUnit = memoryUnit; + // TODO: Fix this + // this.sink = new SimpleFlowSink(graph, (float) memoryUnit.getSize()); + } + + public double getCapacity() { + // return sink.getCapacity(); + return 0.0f; + } + + public MemoryUnit getMemoryUnit() { + return memoryUnit; + } + + @Override + public String toString() { + return "SimAbstractMachine.Memory"; + } } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/NetworkAdapter.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/NetworkAdapter.java deleted file mode 100644 index ff3daa40..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/NetworkAdapter.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.model; - -import java.util.Objects; - -/** - * A description of a network adapter - */ -public final class NetworkAdapter { - private final String vendor; - private final String modelName; - private final double bandwidth; - - /** - * Construct a {@link NetworkAdapter} instance. - * - * @param vendor The vendor of the storage device. - * @param modelName The model name of the device. - * @param bandwidth The bandwidth of the network adapter in Mbps. - */ - public NetworkAdapter(String vendor, String modelName, double bandwidth) { - this.vendor = vendor; - this.modelName = modelName; - this.bandwidth = bandwidth; - } - - /** - * Return the vendor of the storage device. - */ - public String getVendor() { - return vendor; - } - - /** - * Return the model name of the device. - */ - public String getModelName() { - return modelName; - } - - /** - * Return the bandwidth of the network adapter in Mbps. - */ - public double getBandwidth() { - return bandwidth; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - NetworkAdapter that = (NetworkAdapter) o; - return Double.compare(that.bandwidth, bandwidth) == 0 - && vendor.equals(that.vendor) - && modelName.equals(that.modelName); - } - - @Override - public int hashCode() { - return Objects.hash(vendor, modelName, bandwidth); - } - - @Override - public String toString() { - return "NetworkAdapter[vendor='" + vendor + "',modelName='" + modelName + "',bandwidth=" + bandwidth + "Mbps]"; - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/ProcessingNode.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/ProcessingNode.java deleted file mode 100644 index 01a87b96..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/ProcessingNode.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.model; - -import java.util.Objects; - -/** - * A processing node/package/socket containing possibly several CPU cores. - */ -public final class ProcessingNode { - private final String vendor; - private final String modelName; - private final String arch; - private final int coreCount; - - /** - * Construct a {@link ProcessingNode} instance. - * - * @param vendor The vendor of the storage device. - * @param modelName The model name of the device. - * @param arch The micro-architecture of the processor node. - * @param coreCount The number of logical CPUs in the processor node. - */ - public ProcessingNode(String vendor, String modelName, String arch, int coreCount) { - this.vendor = vendor; - this.modelName = modelName; - this.arch = arch; - this.coreCount = coreCount; - } - - /** - * Return the vendor of the storage device. - */ - public String getVendor() { - return vendor; - } - - /** - * Return the model name of the device. - */ - public String getModelName() { - return modelName; - } - - /** - * Return the micro-architecture of the processor node. - */ - public String getArchitecture() { - return arch; - } - - /** - * Return the number of logical CPUs in the processor node. - */ - public int getCoreCount() { - return coreCount; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ProcessingNode that = (ProcessingNode) o; - return coreCount == that.coreCount - && vendor.equals(that.vendor) - && modelName.equals(that.modelName) - && arch.equals(that.arch); - } - - @Override - public int hashCode() { - return Objects.hash(vendor, modelName, arch, coreCount); - } - - @Override - public String toString() { - return "ProcessingNode[vendor='" + vendor + "',modelName='" + modelName + "',arch=" + arch + ",coreCount=" - + coreCount + "]"; - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/StorageDevice.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/StorageDevice.java deleted file mode 100644 index 549ccc7e..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/StorageDevice.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.model; - -import java.util.Objects; - -/** - * Model for a physical storage device attached to a machine. - */ -public final class StorageDevice { - private final String vendor; - private final String modelName; - private final double capacity; - private final double readBandwidth; - private final double writeBandwidth; - - /** - * Construct a {@link StorageDevice} instance. - * - * @param vendor The vendor of the storage device. - * @param modelName The model name of the device. - * @param capacity The capacity of the device. - * @param readBandwidth The read bandwidth of the device in MBps. - * @param writeBandwidth The write bandwidth of the device in MBps. - */ - public StorageDevice( - String vendor, String modelName, double capacity, double readBandwidth, double writeBandwidth) { - this.vendor = vendor; - this.modelName = modelName; - this.capacity = capacity; - this.readBandwidth = readBandwidth; - this.writeBandwidth = writeBandwidth; - } - - /** - * Return the vendor of the storage device. - */ - public String getVendor() { - return vendor; - } - - /** - * Return the model name of the device. - */ - public String getModelName() { - return modelName; - } - - /** - * Return the capacity of the device. - */ - public double getCapacity() { - return capacity; - } - - /** - * Return the read bandwidth of the device in MBps. - */ - public double getReadBandwidth() { - return readBandwidth; - } - - /** - * Return the write bandwidth of the device in MBps. - */ - public double getWriteBandwidth() { - return writeBandwidth; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - StorageDevice that = (StorageDevice) o; - return Double.compare(that.capacity, capacity) == 0 - && Double.compare(that.readBandwidth, readBandwidth) == 0 - && Double.compare(that.writeBandwidth, writeBandwidth) == 0 - && vendor.equals(that.vendor) - && modelName.equals(that.modelName); - } - - @Override - public int hashCode() { - return Objects.hash(vendor, modelName, capacity, readBandwidth, writeBandwidth); - } - - @Override - public String toString() { - return "StorageDevice[vendor='" + vendor + "',modelName='" + modelName + "',capacity=" + capacity - + ",readBandwidth=" + readBandwidth + ",writeBandwidth=" + writeBandwidth + "]"; - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/Cpu.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/models/CpuModel.java index c319ae1a..88e17941 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/model/Cpu.java +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/models/CpuModel.java @@ -20,25 +20,25 @@ * SOFTWARE. */ -package org.opendc.simulator.compute.model; +package org.opendc.simulator.compute.models; import java.util.Objects; /** * A single logical compute unit of processor node, either virtual or physical. */ -public final class Cpu { +public final class CpuModel { private final int id; private final int coreCount; - private final double coreSpeed; - private final double totalCapacity; + private final float coreSpeed; + private final float totalCapacity; private final String vendor; private final String modelName; private final String arch; /** - * Construct a {@link Cpu} instance. + * Construct a {@link CpuModel} instance. * * @param id The identifier of the CPU core within the processing node. * @param coreCount The number of cores present in the CPU @@ -47,7 +47,7 @@ public final class Cpu { * @param modelName The name of the CPU * @param arch The architecture of the CPU */ - public Cpu(int id, int coreCount, double coreSpeed, String vendor, String modelName, String arch) { + public CpuModel(int id, int coreCount, float coreSpeed, String vendor, String modelName, String arch) { this.id = id; this.coreCount = coreCount; this.coreSpeed = coreSpeed; @@ -57,7 +57,7 @@ public final class Cpu { this.arch = arch; } - public Cpu(int id, int coreCount, double coreSpeed) { + public CpuModel(int id, int coreCount, float coreSpeed) { this(id, coreCount, coreSpeed, "unkown", "unkown", "unkown"); } @@ -78,14 +78,14 @@ public final class Cpu { /** * Return the clock rate of a single core of the CPU MHz. */ - public double getCoreSpeed() { + public float getCoreSpeed() { return coreSpeed; } /** * Return the clock rate of the CPU in MHz. */ - public double getTotalCapacity() { + public float getTotalCapacity() { return totalCapacity; } @@ -114,7 +114,7 @@ public final class Cpu { public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - Cpu that = (Cpu) o; + CpuModel that = (CpuModel) o; return id == that.id && Double.compare(that.totalCapacity, totalCapacity) == 0 && Double.compare(that.coreSpeed, coreSpeed) == 0 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/models/MachineModel.java index e4019dac..d6d139d7 100644 --- 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/models/MachineModel.java @@ -20,10 +20,8 @@ * SOFTWARE. */ -package org.opendc.simulator.compute.model; +package org.opendc.simulator.compute.models; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Objects; @@ -31,33 +29,18 @@ import java.util.Objects; * A description of the physical or virtual machine on which a bootable image runs. */ public final class MachineModel { - private final Cpu cpu; + private final CpuModel cpuModel; private final MemoryUnit memory; - private final List<NetworkAdapter> net; - private final List<StorageDevice> storage; /** * Construct a {@link MachineModel} instance. * - * @param cpu The cpu available to the image. + * @param cpuModel The cpu available to the image. * @param memory The list of memory units available to the image. - * @param net A list of network adapters available to the machine. - * @param storage A list of storage devices available to the machine. */ - public MachineModel(Cpu cpu, MemoryUnit memory, Iterable<NetworkAdapter> net, Iterable<StorageDevice> storage) { - this.cpu = cpu; - + public MachineModel(CpuModel cpuModel, MemoryUnit memory) { + this.cpuModel = cpuModel; this.memory = memory; - - this.net = new ArrayList<>(); - net.forEach(this.net::add); - - this.storage = new ArrayList<>(); - storage.forEach(this.storage::add); - } - - public MachineModel(Cpu cpu, MemoryUnit memory) { - this(cpu, memory, Collections.emptyList(), Collections.emptyList()); } /** @@ -68,31 +51,24 @@ public final class MachineModel { * @param cpus The list of processing units available to the image. * @param memory The list of memory units available to the image. */ - public MachineModel( - List<Cpu> cpus, MemoryUnit memory, Iterable<NetworkAdapter> net, Iterable<StorageDevice> storage) { + public MachineModel(List<CpuModel> cpus, MemoryUnit memory) { this( - new Cpu( + new CpuModel( cpus.get(0).getId(), cpus.get(0).getCoreCount() * cpus.size(), cpus.get(0).getCoreSpeed(), cpus.get(0).getVendor(), cpus.get(0).getModelName(), cpus.get(0).getArchitecture()), - memory, - net, - storage); - } - - public MachineModel(List<Cpu> cpus, MemoryUnit memory) { - this(cpus, memory, Collections.emptyList(), Collections.emptyList()); + memory); } /** * Return the processing units of this machine. */ - public Cpu getCpu() { - return this.cpu; + public CpuModel getCpu() { + return this.cpuModel; } /** @@ -102,38 +78,21 @@ public final class MachineModel { return 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 cpu.equals(that.cpu) - && memory.equals(that.memory) - && net.equals(that.net) - && storage.equals(that.storage); + return cpuModel.equals(that.cpuModel) && memory.equals(that.memory); } @Override public int hashCode() { - return Objects.hash(cpu, memory, net, storage); + return Objects.hash(cpuModel, memory); } @Override public String toString() { - return "MachineModel[cpus=" + cpu + ",memory=" + memory + ",net=" + net + ",storage=" + storage + "]"; + return "MachineModel[cpus=" + cpuModel + ",memory=" + memory + "]"; } } 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/models/MemoryUnit.java index dbd3f89a..c3af2bcd 100644 --- 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/models/MemoryUnit.java @@ -20,7 +20,7 @@ * SOFTWARE. */ -package org.opendc.simulator.compute.model; +package org.opendc.simulator.compute.models; import java.util.Objects; @@ -34,7 +34,7 @@ public final class MemoryUnit { private final long size; /** - * Construct a {@link ProcessingNode} instance. + * Construct a {@link MemoryUnit} instance. * * @param vendor The vendor of the storage device. * @param modelName The model name of the device. diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/SimPowerSource.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/SimPowerSource.java new file mode 100644 index 00000000..9b4d6a33 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/SimPowerSource.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2024 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.power; + +import java.time.InstantSource; +import org.opendc.simulator.compute.cpu.SimCpu; +import org.opendc.simulator.engine.FlowEdge; +import org.opendc.simulator.engine.FlowGraph; +import org.opendc.simulator.engine.FlowNode; +import org.opendc.simulator.engine.FlowSupplier; + +/** + * A {@link SimPsu} implementation that estimates the power consumption based on CPU usage. + */ +public final class SimPowerSource extends FlowNode implements FlowSupplier { + private final InstantSource clock; + + private long lastUpdate; + + private float powerDemand = 0.0f; + private float powerSupplied = 0.0f; + private float totalEnergyUsage = 0.0f; + + private FlowEdge cpuEdge; + + private float capacity = Long.MAX_VALUE; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Basic Getters and Setters + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Determine whether the InPort is connected to a {@link SimCpu}. + * + * @return <code>true</code> if the InPort is connected to an OutPort, <code>false</code> otherwise. + */ + public boolean isConnected() { + return cpuEdge != null; + } + + /** + * 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 double getPowerDemand() { + return this.powerDemand; + } + + /** + * Return the instantaneous power usage of the machine (in W) measured at the InPort of the power supply. + */ + public float getPowerDraw() { + return this.powerSupplied; + } + + /** + * Return the cumulated energy usage of the machine (in J) measured at the InPort of the powers supply. + */ + public float getEnergyUsage() { + updateCounters(); + return totalEnergyUsage; + } + + @Override + public float getCapacity() { + return this.capacity; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public SimPowerSource(FlowGraph graph) { + super(graph); + + this.clock = graph.getEngine().getClock(); + + lastUpdate = graph.getEngine().getClock().millis(); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // FlowNode related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public long onUpdate(long now) { + updateCounters(); + float powerSupply = this.powerDemand; + + if (powerSupply != this.powerSupplied) { + this.pushSupply(this.cpuEdge, powerSupply); + } + + return Long.MAX_VALUE; + } + + public void updateCounters() { + updateCounters(clock.millis()); + } + + /** + * Calculate the energy usage up until <code>now</code>. + */ + public void updateCounters(long now) { + long lastUpdate = this.lastUpdate; + this.lastUpdate = now; + + long duration = now - lastUpdate; + if (duration > 0) { + // Compute the energy usage of the machine + this.totalEnergyUsage += (float) (this.powerSupplied * duration * 0.001); + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // FlowGraph Related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void handleDemand(FlowEdge consumerEdge, float newPowerDemand) { + if (newPowerDemand == this.powerDemand) { + return; + } + + this.powerDemand = newPowerDemand; + this.invalidate(); + } + + @Override + public void pushSupply(FlowEdge consumerEdge, float newSupply) { + if (newSupply == this.powerSupplied) { + return; + } + + this.powerSupplied = newSupply; + consumerEdge.pushSupply(newSupply); + } + + @Override + public void addConsumerEdge(FlowEdge consumerEdge) { + this.cpuEdge = consumerEdge; + } + + @Override + public void removeConsumerEdge(FlowEdge consumerEdge) { + this.cpuEdge = null; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/SimPsu.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/SimPsu.java new file mode 100644 index 00000000..8f0fb130 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/power/SimPsu.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2024 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.power; + +import org.opendc.simulator.compute.cpu.SimCpu; +import org.opendc.simulator.engine.FlowConsumer; +import org.opendc.simulator.engine.FlowEdge; +import org.opendc.simulator.engine.FlowGraph; +import org.opendc.simulator.engine.FlowNode; +import org.opendc.simulator.engine.FlowSupplier; + +/** + * A {@link SimPsu} implementation that estimates the power consumption based on CPU usage. + */ +public final class SimPsu extends FlowNode implements FlowSupplier, FlowConsumer { + private long lastUpdate; + + private float powerDemand = 0.0f; + private float powerSupplied = 0.0f; + private float totalEnergyUsage = 0.0f; + + private FlowEdge cpuEdge; + private FlowEdge powerEdge; + + private float capacity = Long.MAX_VALUE; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Basic Getters and Setters + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Determine whether the InPort is connected to a {@link SimCpu}. + * + * @return <code>true</code> if the InPort is connected to an OutPort, <code>false</code> otherwise. + */ + public boolean isConnected() { + return cpuEdge != null; + } + + /** + * 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 double getPowerDemand() { + return this.powerDemand; + } + + /** + * Return the instantaneous power usage of the machine (in W) measured at the InPort of the power supply. + */ + public float getPowerDraw() { + return this.powerSupplied; + } + + /** + * Return the cumulated energy usage of the machine (in J) measured at the InPort of the powers supply. + */ + public float getEnergyUsage() { + updateCounters(); + return totalEnergyUsage; + } + + @Override + public float getCapacity() { + return this.capacity; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public SimPsu(FlowGraph graph) { + super(graph); + + lastUpdate = this.clock.millis(); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // FlowNode related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public long onUpdate(long now) { + updateCounters(); + float powerSupply = this.powerDemand; + + if (powerSupply != this.powerSupplied) { + this.pushSupply(this.cpuEdge, powerSupply); + } + + return Long.MAX_VALUE; + } + + public void updateCounters() { + updateCounters(clock.millis()); + } + + /** + * Calculate the energy usage up until <code>now</code>. + */ + public void updateCounters(long now) { + long lastUpdate = this.lastUpdate; + this.lastUpdate = now; + + long duration = now - lastUpdate; + if (duration > 0) { + // Compute the energy usage of the psu + this.totalEnergyUsage += (float) (this.powerSupplied * duration * 0.001); + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // FlowGraph Related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void pushDemand(FlowEdge supplierEdge, float newDemand) { + if (newDemand == this.powerDemand) { + return; + } + + this.powerDemand = newDemand; + powerEdge.pushSupply(newDemand); + } + + @Override + public void pushSupply(FlowEdge consumerEdge, float newSupply) { + if (newSupply == this.powerSupplied) { + return; + } + + this.powerSupplied = newSupply; + cpuEdge.pushSupply(newSupply); + } + + @Override + public void handleDemand(FlowEdge consumerEdge, float newPowerDemand) { + if (newPowerDemand == this.powerDemand) { + return; + } + + this.powerDemand = newPowerDemand; + this.invalidate(); + } + + @Override + public void handleSupply(FlowEdge supplierEdge, float newPowerSupply) { + if (newPowerSupply == this.powerSupplied) { + return; + } + + this.powerSupplied = newPowerSupply; + this.invalidate(); + } + + @Override + public void addConsumerEdge(FlowEdge consumerEdge) { + this.cpuEdge = consumerEdge; + } + + @Override + public void addSupplierEdge(FlowEdge supplierEdge) { + this.powerEdge = supplierEdge; + } + + @Override + public void removeConsumerEdge(FlowEdge consumerEdge) { + this.cpuEdge = null; + } + + @Override + public void removeSupplierEdge(FlowEdge supplierEdge) { + this.powerEdge = null; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/ChainWorkload.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/ChainWorkload.java new file mode 100644 index 00000000..78e8b5d4 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/ChainWorkload.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.workload; + +import java.util.ArrayList; +import org.opendc.simulator.engine.FlowSupplier; + +public class ChainWorkload implements Workload { + private ArrayList<Workload> workloads; + private final long checkpointInterval; + private final long checkpointDuration; + private final double checkpointIntervalScaling; + + public ChainWorkload( + ArrayList<Workload> workloads, + long checkpointInterval, + long checkpointDuration, + double checkpointIntervalScaling) { + this.workloads = workloads; + this.checkpointInterval = checkpointInterval; + this.checkpointDuration = checkpointDuration; + this.checkpointIntervalScaling = checkpointIntervalScaling; + } + + public ArrayList<Workload> getWorkloads() { + return workloads; + } + + public long getCheckpointInterval() { + return checkpointInterval; + } + + public long getCheckpointDuration() { + return checkpointDuration; + } + + public double getCheckpointIntervalScaling() { + return checkpointIntervalScaling; + } + + public void removeWorkloads(int numberOfWorkloads) { + if (numberOfWorkloads <= 0) { + return; + } + this.workloads.subList(0, numberOfWorkloads).clear(); + } + + @Override + public SimWorkload startWorkload(FlowSupplier supplier, long now) { + return new SimChainWorkload(supplier, this, now); + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/CheckpointModel.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/CheckpointModel.java new file mode 100644 index 00000000..723c450d --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/CheckpointModel.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2024 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.workload; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// CheckPoint Model +// TODO: Move this to a separate file +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +import java.time.InstantSource; +import org.jetbrains.annotations.NotNull; +import org.opendc.simulator.engine.FlowGraph; +import org.opendc.simulator.engine.FlowNode; + +public class CheckpointModel extends FlowNode { + private SimWorkload simWorkload; + private long checkpointInterval; + private final long checkpointDuration; + private double checkpointIntervalScaling; + private FlowGraph graph; + + private long startOfInterval; + + public CheckpointModel(@NotNull SimWorkload simWorkload) { + super(simWorkload.getGraph()); + + this.checkpointInterval = simWorkload.getCheckpointInterval(); + this.checkpointDuration = simWorkload.getCheckpointDuration(); + this.checkpointIntervalScaling = simWorkload.getCheckpointIntervalScaling(); + this.simWorkload = simWorkload; + + this.graph = simWorkload.getGraph(); + + InstantSource clock = graph.getEngine().getClock(); + + this.startOfInterval = clock.millis(); + } + + @Override + public long onUpdate(long now) { + if (this.simWorkload == null) { + return Long.MAX_VALUE; + } + + long passedTime = now - startOfInterval; + long remainingTime = this.checkpointInterval - passedTime; + + // Interval not completed + if (remainingTime > 0) { + return now + remainingTime; + } + + simWorkload.makeSnapshot(now); + + // start new fragment + this.startOfInterval = now - passedTime; + + // Scale the interval time between checkpoints based on the provided scaling + this.checkpointInterval = (long) (this.checkpointInterval * this.checkpointIntervalScaling); + + return now + this.checkpointInterval + this.checkpointDuration; + } + + public void start() { + this.invalidate(); + } + + public void close() { + this.closeNode(); + + this.simWorkload = null; + this.graph = null; + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimChainWorkload.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimChainWorkload.java index 1dcb3674..7f1cf060 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimChainWorkload.java +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimChainWorkload.java @@ -22,59 +22,46 @@ package org.opendc.simulator.compute.workload; -import java.time.InstantSource; -import java.util.List; -import java.util.Map; -import org.opendc.simulator.compute.SimMachineContext; -import org.opendc.simulator.compute.SimMemory; -import org.opendc.simulator.compute.SimNetworkInterface; -import org.opendc.simulator.compute.SimProcessingUnit; -import org.opendc.simulator.compute.SimStorageInterface; -import org.opendc.simulator.flow2.FlowGraph; -import org.opendc.simulator.flow2.FlowStage; -import org.opendc.simulator.flow2.FlowStageLogic; +import java.util.LinkedList; +import org.opendc.simulator.engine.FlowEdge; +import org.opendc.simulator.engine.FlowNode; +import org.opendc.simulator.engine.FlowSupplier; /** - * A {@link SimWorkload} that composes two {@link SimWorkload}s. + * A {@link SimChainWorkload} that composes multiple {@link SimWorkload}s. */ -final class SimChainWorkload implements SimWorkload { - private final SimWorkload[] workloads; - private int activeWorkloadIndex; +final class SimChainWorkload extends SimWorkload implements FlowSupplier { + private final LinkedList<Workload> workloads; + private int workloadIndex; - private SimChainWorkloadContext activeContext; + private SimWorkload activeWorkload; + private float demand = 0.0f; + private float supply = 0.0f; + + private FlowEdge workloadEdge; + private FlowEdge machineEdge; + + private float capacity = 0; private long checkpointInterval = 0; private long checkpointDuration = 0; - private double checkpointIntervalScaling = 1.0; - private CheckPointModel checkpointModel; - private SimChainWorkload snapshot; + private CheckpointModel checkpointModel; - /** - * Construct a {@link SimChainWorkload} instance. - * - * @param workloads The workloads to chain. - * @param activeWorkloadIndex The index of the active workload. - */ - SimChainWorkload(SimWorkload[] workloads, int activeWorkloadIndex) { - this.workloads = workloads; + private ChainWorkload snapshot; - if (this.workloads.length > 1) { - checkpointInterval = this.workloads[1].getCheckpointInterval(); - checkpointDuration = this.workloads[1].getCheckpointDuration(); - checkpointIntervalScaling = this.workloads[1].getCheckpointIntervalScaling(); - } + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Basic Getters and Setters + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - this.activeWorkloadIndex = activeWorkloadIndex; + @Override + public float getCapacity() { + return this.capacity; } - /** - * Construct a {@link SimChainWorkload} instance. - * - * @param workloads The workloads to chain. - */ - SimChainWorkload(SimWorkload... workloads) { - this(workloads, 0); + @Override + public ChainWorkload getSnapshot() { + return this.snapshot; } @Override @@ -92,270 +79,202 @@ final class SimChainWorkload implements SimWorkload { return checkpointIntervalScaling; } - @Override - public void setOffset(long now) { - for (SimWorkload workload : this.workloads) { - workload.setOffset(now); - } - } + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - @Override - public void onStart(SimMachineContext ctx) { - final SimWorkload[] workloads = this.workloads; - final int activeWorkloadIndex = this.activeWorkloadIndex; + SimChainWorkload(FlowSupplier supplier, ChainWorkload workload, long now) { + super(((FlowNode) supplier).getGraph()); - if (activeWorkloadIndex >= workloads.length) { - return; - } + this.snapshot = workload; + + this.parentGraph = ((FlowNode) supplier).getGraph(); + this.parentGraph.addEdge(this, supplier); - final SimChainWorkloadContext context = new SimChainWorkloadContext(ctx); - activeContext = context; + this.clock = this.parentGraph.getEngine().getClock(); + this.workloads = new LinkedList<>(workload.getWorkloads()); + this.checkpointInterval = workload.getCheckpointInterval(); + this.checkpointDuration = workload.getCheckpointDuration(); + this.checkpointIntervalScaling = workload.getCheckpointIntervalScaling(); if (checkpointInterval > 0) { this.createCheckpointModel(); - this.checkpointModel.start(); } - tryThrow(context.doStart(workloads[activeWorkloadIndex])); + this.workloadIndex = -1; + + this.onStart(); } - @Override - public void onStop(SimMachineContext ctx) { - final SimWorkload[] workloads = this.workloads; - final int activeWorkloadIndex = this.activeWorkloadIndex; + public Workload getNextWorkload() { + this.workloadIndex++; + return workloads.pop(); + } - if (activeWorkloadIndex >= workloads.length) { + // TODO: Combine with Constructor + public void onStart() { + if (this.workloads.isEmpty()) { return; } - final SimChainWorkloadContext context = activeContext; - activeContext = null; - - if (this.checkpointModel != null) { - this.checkpointModel.stop(); + // Create and start a checkpoint model if initiated + if (checkpointInterval > 0) { + this.checkpointModel.start(); } - tryThrow(context.doStop(workloads[activeWorkloadIndex])); + this.activeWorkload = this.getNextWorkload().startWorkload(this, this.clock.millis()); } @Override - public void makeSnapshot(long now) { - final int activeWorkloadIndex = this.activeWorkloadIndex; - final SimWorkload[] workloads = this.workloads; - final SimWorkload[] newWorkloads = new SimWorkload[workloads.length - activeWorkloadIndex]; + public long onUpdate(long now) { + return Long.MAX_VALUE; + } - for (int i = 0; i < newWorkloads.length; i++) { - workloads[activeWorkloadIndex + i].makeSnapshot(now); - newWorkloads[i] = workloads[activeWorkloadIndex + i].getSnapshot(); + @Override + public void stopWorkload() { + if (this.checkpointModel != null) { + this.checkpointModel.close(); + this.checkpointModel = null; } - this.snapshot = new SimChainWorkload(newWorkloads, 0); - } + if (this.activeWorkload != null) { + this.activeWorkload.stopWorkload(); + this.activeWorkload = null; + } - @Override - public SimChainWorkload getSnapshot() { - return this.snapshot; + this.closeNode(); } + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Checkpoint related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + @Override public void createCheckpointModel() { - this.checkpointModel = new CheckPointModel( - activeContext, this, this.checkpointInterval, this.checkpointDuration, this.checkpointIntervalScaling); + this.checkpointModel = new CheckpointModel(this); } - private class CheckPointModel implements FlowStageLogic { - private SimChainWorkload workload; - private long checkpointInterval; - private long checkpointDuration; - private double checkpointIntervalScaling; - private FlowStage stage; - - private long startOfInterval; - private Boolean firstCheckPoint = true; - - CheckPointModel( - SimChainWorkloadContext context, - SimChainWorkload workload, - long checkpointInterval, - long checkpointDuration, - double checkpointIntervalScaling) { - this.checkpointInterval = checkpointInterval; - this.checkpointDuration = checkpointDuration; - this.checkpointIntervalScaling = checkpointIntervalScaling; - this.workload = workload; - - this.stage = context.getGraph().newStage(this); - - InstantSource clock = this.stage.getGraph().getEngine().getClock(); - - this.startOfInterval = clock.millis(); - } - - @Override - public long onUpdate(FlowStage ctx, long now) { - long passedTime = now - startOfInterval; - long remainingTime = this.checkpointInterval - passedTime; - - if (!this.firstCheckPoint) { - remainingTime += this.checkpointDuration; - } - - // Interval not completed - if (remainingTime > 0) { - return now + remainingTime; - } - - workload.makeSnapshot(now); - if (firstCheckPoint) { - this.firstCheckPoint = false; - } + @Override + public void makeSnapshot(long now) { - // Scale the interval time between checkpoints based on the provided scaling - this.checkpointInterval = (long) (this.checkpointInterval * this.checkpointIntervalScaling); + this.snapshot.removeWorkloads(this.workloadIndex); + this.workloadIndex = 0; - return now + this.checkpointInterval + this.checkpointDuration; - } + activeWorkload.makeSnapshot(now); + } - public void start() { - this.stage.sync(); - } + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // FlowGraph Related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - public void stop() { - this.stage.close(); - } + /** + * Add connection to the active workload + * + * @param consumerEdge + */ + @Override + public void addConsumerEdge(FlowEdge consumerEdge) { + this.workloadEdge = consumerEdge; } /** - * A {@link SimMachineContext} that intercepts the shutdown calls. + * Add Connection to the cpuMux + * @param supplierEdge */ - private class SimChainWorkloadContext implements SimMachineContext { - private final SimMachineContext ctx; - private SimWorkload snapshot; - - private SimChainWorkloadContext(SimMachineContext ctx) { - this.ctx = ctx; - } - - @Override - public FlowGraph getGraph() { - return ctx.getGraph(); - } - - @Override - public Map<String, Object> getMeta() { - return ctx.getMeta(); - } + @Override + public void addSupplierEdge(FlowEdge supplierEdge) { + this.machineEdge = supplierEdge; + this.capacity = supplierEdge.getCapacity(); + } - @Override - public SimProcessingUnit getCpu() { - return ctx.getCpu(); - } + /** + * Push demand to the cpuMux + * + * @param supplierEdge + * @param newDemand + */ + @Override + public void pushDemand(FlowEdge supplierEdge, float newDemand) { + this.machineEdge.pushDemand(newDemand); + } - @Override - public SimMemory getMemory() { - return ctx.getMemory(); - } + /** + * Push supply to the workload + * + * @param consumerEdge + * @param newSupply + */ + @Override + public void pushSupply(FlowEdge consumerEdge, float newSupply) { + this.workloadEdge.pushSupply(newSupply); + } - @Override - public List<? extends SimNetworkInterface> getNetworkInterfaces() { - return ctx.getNetworkInterfaces(); + /** + * Handle new demand coming from the workload + * + * @param consumerEdge + * @param newDemand + */ + @Override + public void handleDemand(FlowEdge consumerEdge, float newDemand) { + if (newDemand == this.demand) { + return; } - @Override - public List<? extends SimStorageInterface> getStorageInterfaces() { - return ctx.getStorageInterfaces(); - } + this.demand = newDemand; + this.pushDemand(this.machineEdge, newDemand); + } - @Override - public void makeSnapshot(long now) { - final SimWorkload workload = workloads[activeWorkloadIndex]; - this.snapshot = workload.getSnapshot(); + /** + * Handle new supply coming from the cpuMux + * + * @param supplierEdge + * @param newSupply + */ + @Override + public void handleSupply(FlowEdge supplierEdge, float newSupply) { + if (newSupply == this.supply) { + return; } - @Override - public SimWorkload getSnapshot(long now) { - this.makeSnapshot(now); - - return this.snapshot; - } + this.pushSupply(this.machineEdge, newSupply); + } - @Override - public void reset() { - ctx.reset(); + /** + * Handle the removal of the workload. + * If there is a next workload available, start this workload + * Otherwise, close this SimChainWorkload + * + * @param consumerEdge + */ + @Override + public void removeConsumerEdge(FlowEdge consumerEdge) { + if (this.workloadEdge == null) { + return; } - @Override - public void shutdown() { - shutdown(null); - } + // Remove the connection to the active workload + this.activeWorkload = null; + this.workloadEdge = null; - @Override - public void shutdown(Exception cause) { - final SimWorkload[] workloads = SimChainWorkload.this.workloads; - final int activeWorkloadIndex = ++SimChainWorkload.this.activeWorkloadIndex; - - final Exception stopException = doStop(workloads[activeWorkloadIndex - 1]); - if (cause == null) { - cause = stopException; - } else if (stopException != null) { - cause.addSuppressed(stopException); - } - - if (stopException == null && activeWorkloadIndex < workloads.length) { - ctx.reset(); - - final Exception startException = doStart(workloads[activeWorkloadIndex]); - - if (startException == null) { - return; - } else if (cause == null) { - cause = startException; - } else { - cause.addSuppressed(startException); - } - } - - if (SimChainWorkload.this.checkpointModel != null) { - SimChainWorkload.this.checkpointModel.stop(); - } - ctx.shutdown(cause); + // Start next workload + if (!this.workloads.isEmpty()) { + this.activeWorkload = getNextWorkload().startWorkload(this, this.clock.millis()); + return; } - /** - * Start the specified workload. - * - * @return The {@link Exception} that occurred while starting the workload or <code>null</code> if the workload - * started successfully. - */ - private Exception doStart(SimWorkload workload) { - try { - workload.onStart(this); - } catch (Exception cause) { - final Exception stopException = doStop(workload); - if (stopException != null) { - cause.addSuppressed(stopException); - } - return cause; - } - - return null; - } + this.stopWorkload(); + } - /** - * Stop the specified workload. - * - * @return The {@link Exception} that occurred while stopping the workload or <code>null</code> if the workload - * stopped successfully. - */ - private Exception doStop(SimWorkload workload) { - try { - workload.onStop(this); - } catch (Exception cause) { - return cause; - } - - return null; - } + /** + * Handle the removal of the connection to the cpuMux + * When this happens, close the SimChainWorkload + * + * @param supplierEdge + */ + @Override + public void removeSupplierEdge(FlowEdge supplierEdge) { + this.stopWorkload(); } @SuppressWarnings("unchecked") diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimFlopsWorkload.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimFlopsWorkload.java deleted file mode 100644 index 5311fa38..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimFlopsWorkload.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.workload; - -import org.opendc.simulator.compute.SimMachineContext; -import org.opendc.simulator.compute.SimProcessingUnit; -import org.opendc.simulator.flow2.FlowGraph; -import org.opendc.simulator.flow2.FlowStage; -import org.opendc.simulator.flow2.FlowStageLogic; -import org.opendc.simulator.flow2.OutPort; - -/** - * A {@link SimWorkload} that models applications as a static number of floating point operations executed on - * multiple cores of a compute resource. - */ -public class SimFlopsWorkload implements SimWorkload, FlowStageLogic { - private final long flops; - private final double utilization; - - private SimMachineContext ctx; - private FlowStage stage; - private OutPort[] outputs; - - private float remainingAmount; - private long lastUpdate; - private SimFlopsWorkload snapshot; - - /** - * Construct a new {@link SimFlopsWorkload}. - * - * @param flops The number of floating point operations to perform for this task in MFLOPs. - * @param utilization The CPU utilization of the workload. - */ - SimFlopsWorkload(long flops, double utilization) { - if (flops < 0) { - throw new IllegalArgumentException("Number of FLOPs must be positive"); - } else if (utilization <= 0.0 || utilization > 1.0) { - throw new IllegalArgumentException("Utilization must be in (0, 1]"); - } - - this.flops = flops; - this.utilization = utilization; - this.remainingAmount = flops; - } - - @Override - public long getCheckpointInterval() { - return -1; - } - ; - - @Override - public long getCheckpointDuration() { - return -1; - } - - @Override - public double getCheckpointIntervalScaling() { - return -1; - } - ; - - @Override - public void setOffset(long now) {} - - @Override - public void onStart(SimMachineContext ctx) { - this.ctx = ctx; - - final FlowGraph graph = ctx.getGraph(); - final FlowStage stage = graph.newStage(this); - this.stage = stage; - - final SimProcessingUnit cpu = ctx.getCpu(); - final OutPort[] outputs = new OutPort[1]; - this.outputs = outputs; - - final OutPort output = stage.getOutlet("cpu"); - - graph.connect(output, cpu.getInput()); - outputs[0] = output; - - this.remainingAmount = flops; - this.lastUpdate = graph.getEngine().getClock().millis(); - } - - @Override - public void onStop(SimMachineContext ctx) { - this.ctx = null; - - final FlowStage stage = this.stage; - if (stage != null) { - this.stage = null; - stage.close(); - } - } - - @Override - public void makeSnapshot(long now) { - final FlowStage stage = this.stage; - if (stage != null) { - stage.sync(); - } - - this.snapshot = new SimFlopsWorkload((long) remainingAmount, utilization); - } - - @Override - public SimFlopsWorkload getSnapshot() { - this.makeSnapshot(0); - - return this.snapshot; - } - - @Override - public void createCheckpointModel() {} - - @Override - public long onUpdate(FlowStage ctx, long now) { - long lastUpdate = this.lastUpdate; - this.lastUpdate = now; - - long delta = Math.max(0, now - lastUpdate); - - float consumed = 0.f; - float limit = 0.f; - - for (final OutPort output : outputs) { - consumed += output.getRate() * delta; - - float outputLimit = (float) (output.getCapacity() * utilization); - limit += outputLimit; - - output.push(outputLimit); - } - consumed = (float) (consumed * 0.001); - - float remainingAmount = this.remainingAmount - consumed; - this.remainingAmount = remainingAmount; - - long duration = (long) Math.ceil(remainingAmount / limit * 1000); - - if (duration <= 0) { - final SimMachineContext machineContext = this.ctx; - if (machineContext != null) { - machineContext.shutdown(); - } - ctx.close(); - return Long.MAX_VALUE; - } - - return now + duration; - } - - @Override - public String toString() { - return "SimFlopsWorkload[FLOPs=" + flops + ",utilization=" + utilization + "]"; - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimRuntimeWorkload.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimRuntimeWorkload.java deleted file mode 100644 index be4cc2f5..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimRuntimeWorkload.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.workload; - -import org.opendc.simulator.compute.SimMachineContext; -import org.opendc.simulator.compute.SimProcessingUnit; -import org.opendc.simulator.flow2.FlowGraph; -import org.opendc.simulator.flow2.FlowStage; -import org.opendc.simulator.flow2.FlowStageLogic; -import org.opendc.simulator.flow2.OutPort; - -/** - * A [SimWorkload] that models application execution as a single duration. - */ -public class SimRuntimeWorkload implements SimWorkload, FlowStageLogic { - private long duration; - private final double utilization; - - private SimMachineContext ctx; - private FlowStage stage; - private OutPort[] outputs; - - private long remainingDuration; - private long lastUpdate; - - private long checkpointDuration; // How long does it take to make a checkpoint? - private long checkpointInterval; // How long to wait until a new checkpoint is made? - private double checkpointIntervalScaling; - private long totalChecks; - private SimRuntimeWorkload snapshot; - - public SimRuntimeWorkload(long duration, double utilization) { - this(duration, utilization, 0, 0); - // if (duration < 0) { - // throw new IllegalArgumentException("Duration must be positive"); - // } else if (utilization <= 0.0 || utilization > 1.0) { - // throw new IllegalArgumentException("Utilization must be in (0, 1]"); - // } - // - // this.checkpointTime = 0L; - // this.checkpointWait = 0L; - // this.duration = duration; - // - // this.utilization = utilization; - // this.remainingDuration = duration; - } - - /** - * Construct a new {@link SimRuntimeWorkload}. - * - * @param duration The duration of the workload in milliseconds. - * @param utilization The CPU utilization of the workload. - */ - public SimRuntimeWorkload(long duration, double utilization, long checkpointInterval, long checkpointDuration) { - if (duration < 0) { - throw new IllegalArgumentException("Duration must be positive"); - } else if (utilization <= 0.0 || utilization > 1.0) { - throw new IllegalArgumentException("Utilization must be in (0, 1]"); - } - - this.checkpointDuration = checkpointDuration; - this.checkpointInterval = checkpointInterval; - this.duration = duration; - - if (this.checkpointInterval > 0) { - // Determine the number of checkpoints that need to be made during the workload - // If the total duration is divisible by the wait time between checkpoints, we can remove the last - // checkpoint - int to_remove = ((this.duration % this.checkpointInterval == 0) ? 1 : 0); - this.totalChecks = this.duration / this.checkpointInterval - to_remove; - this.duration += (this.checkpointDuration * totalChecks); - } - - this.utilization = utilization; - this.remainingDuration = duration; - } - - @Override - public long getCheckpointInterval() { - return checkpointInterval; - } - - @Override - public long getCheckpointDuration() { - return checkpointDuration; - } - - @Override - public double getCheckpointIntervalScaling() { - return checkpointIntervalScaling; - } - - @Override - public void setOffset(long now) {} - - @Override - public void onStart(SimMachineContext ctx) { - this.ctx = ctx; - - final FlowGraph graph = ctx.getGraph(); - final FlowStage stage = graph.newStage(this); - this.stage = stage; - - final OutPort[] outputs = new OutPort[1]; - this.outputs = outputs; - - final SimProcessingUnit cpu = ctx.getCpu(); - final OutPort output = stage.getOutlet("cpu"); - - graph.connect(output, cpu.getInput()); - outputs[0] = output; - - this.remainingDuration = duration; - this.lastUpdate = graph.getEngine().getClock().millis(); - } - - @Override - public void onStop(SimMachineContext ctx) { - this.ctx = null; - - final FlowStage stage = this.stage; - if (stage != null) { - this.stage = null; - this.outputs = null; - stage.close(); - } - } - - @Override - public void makeSnapshot(long now) { - System.out.printf("SimRuntimeWorkload -> makeSnapshot(%d)%n", now); - - final FlowStage stage = this.stage; - if (stage != null) { - stage.sync(); - } - - var remaining_time = this.remainingDuration; - - if (this.checkpointInterval > 0) { - // Calculate last checkpoint - var total_check_time = this.checkpointInterval + this.checkpointDuration; - var processed_time = this.duration - this.remainingDuration; - var processed_checks = (int) (processed_time / total_check_time); - var processed_time_last_check = - (processed_checks * total_check_time); // The processed time after the last checkpoint - - remaining_time = this.duration - - processed_time_last_check; // The remaining duration to process after last checkpoint - var remaining_checks = (int) (remaining_time / total_check_time); - remaining_time -= (remaining_checks * checkpointDuration); - } else { - remaining_time = duration; - } - - this.snapshot = - new SimRuntimeWorkload(remaining_time, utilization, this.checkpointInterval, this.checkpointDuration); - } - - @Override - public SimRuntimeWorkload getSnapshot() { - System.out.println("SimRuntimeWorkload -> getSnapshot()"); - - return this.snapshot; - } - - @Override - public void createCheckpointModel() {} - - @Override - public long onUpdate(FlowStage ctx, long now) { - long lastUpdate = this.lastUpdate; - this.lastUpdate = now; - - long delta = now - lastUpdate; - long duration = this.remainingDuration - delta; - - if (delta == 0 && this.ctx == null) { - // This means the workload has been terminated - // But, has not executed to completion - return Long.MAX_VALUE; - } - - if (duration <= 0) { - final SimMachineContext machineContext = this.ctx; - if (machineContext != null) { - machineContext.shutdown(); - } - ctx.close(); - return Long.MAX_VALUE; - } - - this.remainingDuration = duration; - - for (final OutPort output : outputs) { - float limit = (float) (output.getCapacity() * utilization); - output.push(limit); - } - - return now + duration; - } - - @Override - public String toString() { - return "SimDurationWorkload[duration=" + duration + "ms,utilization=" + utilization + "]"; - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTrace.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTrace.java deleted file mode 100644 index b8445a9c..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTrace.java +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.workload; - -import java.util.ArrayDeque; -import java.util.Iterator; -import java.util.List; -import org.opendc.simulator.compute.SimMachineContext; -import org.opendc.simulator.compute.SimProcessingUnit; -import org.opendc.simulator.flow2.FlowGraph; -import org.opendc.simulator.flow2.FlowStage; -import org.opendc.simulator.flow2.FlowStageLogic; -import org.opendc.simulator.flow2.OutPort; - -/** - * A workload trace that describes the resource utilization over time in a collection of {@link SimTraceFragment}s. - */ -public final class SimTrace { - private final ArrayDeque<SimTraceFragment> fragments; - /** - * Construct a {@link SimTrace} instance. - * - */ - private SimTrace(ArrayDeque<SimTraceFragment> fragments) { - if (fragments.isEmpty()) { - throw new IllegalArgumentException("No Fragments found for the Trace"); - } - this.fragments = fragments; - } - - /** - * Construct a {@link SimWorkload} for this trace. - * - * // * @param offset The offset for the timestamps. - */ - public SimWorkload createWorkload(long start) { - return createWorkload(start, 0, 0, 1); - } - - /** - * Construct a {@link SimWorkload} for this trace. - * - * // * @param offset The offset for the timestamps. - */ - public SimWorkload createWorkload( - long start, long checkpointInterval, long checkpointDuration, double checkpointIntervalScaling) { - return new Workload(start, fragments, checkpointInterval, checkpointDuration, checkpointIntervalScaling); - } - - // /** - // * Create a new {@link Builder} instance with a default initial capacity. - // */ - public static Builder builder() { - return new Builder(); - } - - /** - * Construct a {@link SimTrace} from the specified fragments. - * - * @param fragments The array of fragments to construct the trace from. - */ - public static SimTrace ofFragments(SimTraceFragment... fragments) { - final Builder builder = builder(); - - for (SimTraceFragment fragment : fragments) { - builder.add(fragment.duration(), fragment.cpuUsage(), fragment.coreCount()); - } - - return builder.build(); - } - - /** - * Construct a {@link SimTrace} from the specified fragments. - * - * @param fragments The fragments to construct the trace from. - */ - public static SimTrace ofFragments(List<SimTraceFragment> fragments) { - final Builder builder = builder(); - - for (SimTraceFragment fragment : fragments) { - builder.add(fragment.duration(), fragment.cpuUsage(), fragment.coreCount()); - } - - return builder.build(); - } - - /** - * Builder class for a {@link SimTrace}. - */ - public static final class Builder { - private final ArrayDeque<SimTraceFragment> fragments; - - private boolean isBuilt; - - /** - * Construct a new {@link Builder} instance. - */ - private Builder() { - this.fragments = new ArrayDeque<>(); - } - - /** - * Add a fragment to the trace. - * - * @param duration The timestamp at which the fragment ends (in epoch millis). - * @param usage The CPU usage at this fragment. - * @param cores The number of cores used during this fragment. - */ - public void add(long duration, double usage, int cores) { - if (isBuilt) { - recreate(); - } - - fragments.add(new SimTraceFragment(duration, usage, cores)); - } - - /** - * Build the {@link SimTrace} instance. - */ - public SimTrace build() { - isBuilt = true; - return new SimTrace(fragments); - } - - /** - * Clone the columns of the trace. - * - * <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; - this.fragments.clear(); - } - } - - /** - * Implementation of {@link SimWorkload} that executes a trace. - */ - private static class Workload implements SimWorkload { - private WorkloadStageLogic logic; - - private long offset; - - private final long start; - private ArrayDeque<SimTraceFragment> fragments; - - private long checkpointInterval; // How long to wait until a new checkpoint is made - private long checkpointDuration; // How long does it take to make a checkpoint - private double checkpointIntervalScaling; - private SimWorkload snapshot; - - private Workload( - long start, - ArrayDeque<SimTraceFragment> fragments, - long checkpointInterval, - long checkpointDuration, - double checkpointIntervalScaling) { - this.start = start; - this.checkpointInterval = checkpointInterval; - this.checkpointDuration = checkpointDuration; - this.checkpointIntervalScaling = checkpointIntervalScaling; - - this.fragments = fragments; - - this.snapshot = this; - } - - @Override - public long getCheckpointInterval() { - return checkpointInterval; - } - - @Override - public long getCheckpointDuration() { - return checkpointDuration; - } - - @Override - public double getCheckpointIntervalScaling() { - return checkpointIntervalScaling; - } - - @Override - public void setOffset(long now) { - this.offset = now; - } - - @Override - public void onStart(SimMachineContext ctx) { - final WorkloadStageLogic logic; - logic = new SingleWorkloadLogic(ctx, offset, fragments.iterator()); - this.logic = logic; - } - - @Override - public void onStop(SimMachineContext ctx) { - final WorkloadStageLogic logic = this.logic; - - if (logic != null) { - this.logic = null; - logic.getStage().close(); - } - } - - @Override - public void makeSnapshot(long now) { - final WorkloadStageLogic logic = this.logic; - final ArrayDeque<SimTraceFragment> newFragments = this.fragments; - - if (logic != null) { - int index = logic.getIndex(); - - if (index == 0 && (logic.getPassedTime(now) == 0)) { - this.snapshot = this; - return; - } - - // Remove all finished fragments - for (int i = 0; i < index; i++) { - newFragments.removeFirst(); - } - } else { - return; - } - - // Reduce the current Fragment to a fragment with the remaining time. - SimTraceFragment currentFragment = newFragments.pop(); - long passedTime = logic.getPassedTime(now); - long remainingTime = currentFragment.duration() - passedTime; - - if (remainingTime > 0) { - SimTraceFragment newFragment = - new SimTraceFragment(remainingTime, currentFragment.cpuUsage(), currentFragment.coreCount()); - - newFragments.addFirst(newFragment); - } - - // Add snapshot Fragment - // TODO: improve CPUUsage and coreCount here - SimTraceFragment snapshotFragment = new SimTraceFragment(checkpointDuration, 123456, 1); - newFragments.addFirst(snapshotFragment); - - // Update the logic - this.logic.updateFragments(newFragments.iterator(), now); - - // remove the snapshot Fragment and update fragments - newFragments.removeFirst(); - this.fragments = newFragments; - - this.snapshot = new Workload( - start, this.fragments, checkpointInterval, checkpointDuration, checkpointIntervalScaling); - } - - @Override - public SimWorkload getSnapshot() { - return this.snapshot; - } - - @Override - public void createCheckpointModel() {} - } - - /** - * Interface to represent the {@link FlowStage} that simulates the trace workload. - */ - private interface WorkloadStageLogic extends FlowStageLogic { - /** - * Return the {@link FlowStage} belonging to this instance. - */ - FlowStage getStage(); - - long getPassedTime(long now); - - void updateFragments(Iterator<SimTraceFragment> newFragments, long offset); - - /** - * Return the current index of the workload. - */ - int getIndex(); - } - - /** - * Implementation of {@link FlowStageLogic} for just a single CPU resource. - */ - private static class SingleWorkloadLogic implements WorkloadStageLogic { - private final FlowStage stage; - private final OutPort output; - private int index = 0; - - private final SimMachineContext ctx; - - private Iterator<SimTraceFragment> fragments; - private SimTraceFragment currentFragment; - private long startOffFragment; - - private SingleWorkloadLogic(SimMachineContext ctx, long offset, Iterator<SimTraceFragment> fragments) { - this.ctx = ctx; - - this.fragments = fragments; - - final FlowGraph graph = ctx.getGraph(); - stage = graph.newStage(this); - - final SimProcessingUnit cpu = ctx.getCpu(); - final OutPort output = stage.getOutlet("cpu"); - this.output = output; - - graph.connect(output, cpu.getInput()); - - // Start the first Fragment - this.currentFragment = this.fragments.next(); - this.output.push((float) currentFragment.cpuUsage()); - this.startOffFragment = offset; - } - - public long getPassedTime(long now) { - return now - this.startOffFragment; - } - - @Override - public void updateFragments(Iterator<SimTraceFragment> newFragments, long offset) { - this.fragments = newFragments; - - // Start the first Fragment - this.currentFragment = this.fragments.next(); - this.output.push((float) currentFragment.cpuUsage()); - this.startOffFragment = offset; - - this.index = -1; - - this.stage.invalidate(); - } - - @Override - public long onUpdate(FlowStage ctx, long now) { - long passedTime = getPassedTime(now); - long duration = this.currentFragment.duration(); - - // The current Fragment has not yet been finished, continue - if (passedTime < duration) { - return now + (duration - passedTime); - } - - // Loop through fragments until the passed time is filled. - // We need a while loop to account for skipping of fragments. - while (passedTime >= duration) { - if (!this.fragments.hasNext()) { - return doStop(ctx); - } - - passedTime = passedTime - duration; - - // get next Fragment - this.index++; - currentFragment = this.fragments.next(); - duration = currentFragment.duration(); - } - - // start new fragment - this.startOffFragment = now - passedTime; - - // Change the cpu Usage to the new Fragment - this.output.push((float) currentFragment.cpuUsage()); - - // Return the time when the current fragment will complete - return this.startOffFragment + duration; - } - - @Override - public FlowStage getStage() { - return stage; - } - - @Override - public int getIndex() { - return index; - } - - /** - * Helper method to stop the execution of the workload. - */ - private long doStop(FlowStage ctx) { - final SimMachineContext machineContext = this.ctx; - if (machineContext != null) { - machineContext.shutdown(); - } - ctx.close(); - return Long.MAX_VALUE; - } - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTraceWorkload.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTraceWorkload.java new file mode 100644 index 00000000..b6f98344 --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTraceWorkload.java @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2024 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.workload; + +import java.util.LinkedList; +import org.opendc.simulator.engine.FlowConsumer; +import org.opendc.simulator.engine.FlowEdge; +import org.opendc.simulator.engine.FlowGraph; +import org.opendc.simulator.engine.FlowNode; +import org.opendc.simulator.engine.FlowSupplier; + +public class SimTraceWorkload extends SimWorkload implements FlowConsumer { + private LinkedList<TraceFragment> remainingFragments; + private int fragmentIndex; + + private TraceFragment currentFragment; + private long startOfFragment; + + private FlowEdge machineEdge; + private float currentDemand; + private float currentSupply; + + private long checkpointInterval; + private long checkpointDuration; + private double checkpointIntervalScaling; + + private TraceWorkload snapshot; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Basic Getters and Setters + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public long getPassedTime(long now) { + return now - this.startOfFragment; + } + + public TraceWorkload getSnapshot() { + return snapshot; + } + + @Override + long getCheckpointInterval() { + return 0; + } + + @Override + long getCheckpointDuration() { + return 0; + } + + @Override + double getCheckpointIntervalScaling() { + return 0; + } + + public TraceFragment getNextFragment() { + this.currentFragment = this.remainingFragments.pop(); + this.fragmentIndex++; + + return this.currentFragment; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public SimTraceWorkload(FlowSupplier supplier, TraceWorkload workload, long now) { + super(((FlowNode) supplier).getGraph()); + + this.snapshot = workload; + this.checkpointInterval = workload.getCheckpointInterval(); + this.checkpointDuration = workload.getCheckpointDuration(); + this.checkpointIntervalScaling = workload.getCheckpointIntervalScaling(); + this.remainingFragments = new LinkedList<>(workload.getFragments()); + this.fragmentIndex = 0; + + final FlowGraph graph = ((FlowNode) supplier).getGraph(); + graph.addEdge(this, supplier); + + this.currentFragment = this.getNextFragment(); + pushDemand(machineEdge, (float) this.currentFragment.cpuUsage()); + this.startOfFragment = now; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Fragment related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public long onUpdate(long now) { + long passedTime = getPassedTime(now); + long duration = this.currentFragment.duration(); + + // The current Fragment has not yet been finished, continue + if (passedTime < duration) { + return now + (duration - passedTime); + } + + // Loop through fragments until the passed time is filled. + // We need a while loop to account for skipping of fragments. + while (passedTime >= duration) { + if (this.remainingFragments.isEmpty()) { + this.stopWorkload(); + return Long.MAX_VALUE; + } + + passedTime = passedTime - duration; + + // get next Fragment + currentFragment = this.getNextFragment(); + duration = currentFragment.duration(); + } + + // start new fragment + this.startOfFragment = now - passedTime; + + // Change the cpu Usage to the new Fragment + pushDemand(machineEdge, (float) this.currentFragment.cpuUsage()); + + // Return the time when the current fragment will complete + return this.startOfFragment + duration; + } + + @Override + public void stopWorkload() { + this.closeNode(); + + this.machineEdge = null; + this.remainingFragments = null; + this.currentFragment = null; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Checkpoint related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * SimTraceWorkload does not make a checkpoint, checkpointing is handled by SimChainWorkload + * TODO: Maybe add checkpoint models for SimTraceWorkload + */ + @Override + void createCheckpointModel() {} + + /** + * Create a new snapshot based on the current status of the workload. + * @param now + */ + public void makeSnapshot(long now) { + + // Check if fragments is empty + + // Get remaining time of current fragment + long passedTime = getPassedTime(now); + long remainingTime = currentFragment.duration() - passedTime; + + // Create a new fragment based on the current fragment and remaining duration + TraceFragment newFragment = + new TraceFragment(remainingTime, currentFragment.cpuUsage(), currentFragment.coreCount()); + + // Alter the snapshot by removing finished fragments + this.snapshot.removeFragments(this.fragmentIndex); + this.snapshot.addFirst(newFragment); + + this.remainingFragments.addFirst(newFragment); + + // Create and add a fragment for processing the snapshot process + // TODO: improve the implementation of cpuUsage and coreCount + TraceFragment snapshotFragment = new TraceFragment(this.checkpointDuration, 123456, 1); + this.remainingFragments.addFirst(snapshotFragment); + + this.fragmentIndex = -1; + this.currentFragment = getNextFragment(); + pushDemand(this.machineEdge, (float) this.currentFragment.cpuUsage()); + this.startOfFragment = now; + + this.invalidate(); + } + + /** + * Update the Fragments that are being used by the SimTraceWorkload + * @param newFragments + * @param offset + */ + public void updateFragments(LinkedList<TraceFragment> newFragments, long offset) { + this.remainingFragments = newFragments; + + // Start the first Fragment + this.currentFragment = this.remainingFragments.pop(); + pushDemand(this.machineEdge, (float) this.currentFragment.cpuUsage()); + this.startOfFragment = offset; + + this.invalidate(); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // FlowGraph Related functionality + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle updates in supply from the Virtual Machine + * + * @param supplierEdge + * @param newSupply + */ + @Override + public void handleSupply(FlowEdge supplierEdge, float newSupply) { + if (newSupply == this.currentSupply) { + return; + } + + this.currentSupply = newSupply; + } + + /** + * Push a new demand to the Virtual Machine + * + * @param supplierEdge + * @param newDemand + */ + @Override + public void pushDemand(FlowEdge supplierEdge, float newDemand) { + if (newDemand == this.currentDemand) { + return; + } + + this.currentDemand = newDemand; + this.machineEdge.pushDemand(newDemand); + } + + /** + * Add the connection to the Virtual Machine + * + * @param supplierEdge + */ + @Override + public void addSupplierEdge(FlowEdge supplierEdge) { + this.machineEdge = supplierEdge; + } + + /** + * Handle the removal of the connection to the Virtual Machine + * When the connection to the Virtual Machine is removed, the SimTraceWorkload is removed + * + * @param supplierEdge + */ + @Override + public void removeSupplierEdge(FlowEdge supplierEdge) { + this.stopWorkload(); + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkload.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkload.java index f4f3ff58..b5c89941 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkload.java +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkload.java @@ -22,7 +22,9 @@ package org.opendc.simulator.compute.workload; -import org.opendc.simulator.compute.SimMachineContext; +import org.opendc.simulator.engine.FlowConsumer; +import org.opendc.simulator.engine.FlowGraph; +import org.opendc.simulator.engine.FlowNode; /** * A model that characterizes the runtime behavior of some particular workload. @@ -31,35 +33,33 @@ import org.opendc.simulator.compute.SimMachineContext; * Workloads are stateful objects that may be paused and resumed at a later moment. As such, be careful when using the * same {@link SimWorkload} from multiple contexts. */ -public interface SimWorkload { +public abstract class SimWorkload extends FlowNode implements FlowConsumer { /** - * This method is invoked when the workload is started. + * Construct a new {@link FlowNode} instance. * - * @param ctx The execution context in which the machine runs. + * @param parentGraph The {@link FlowGraph} this stage belongs to. */ - void onStart(SimMachineContext ctx); + public SimWorkload(FlowGraph parentGraph) { + super(parentGraph); + } /** * This method is invoked when the workload is stopped. - * - * @param ctx The execution context in which the machine runs. */ - void onStop(SimMachineContext ctx); + public abstract void stopWorkload(); /** * Create a snapshot of this workload. */ - void makeSnapshot(long now); - - SimWorkload getSnapshot(); + public abstract void makeSnapshot(long now); - void createCheckpointModel(); + public abstract Workload getSnapshot(); - long getCheckpointInterval(); + abstract void createCheckpointModel(); - long getCheckpointDuration(); + abstract long getCheckpointInterval(); - double getCheckpointIntervalScaling(); + abstract long getCheckpointDuration(); - void setOffset(long now); + abstract double getCheckpointIntervalScaling(); } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkloads.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkloads.java deleted file mode 100644 index 34202945..00000000 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimWorkloads.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.simulator.compute.workload; - -import java.time.Duration; - -/** - * Helper methods for constructing {@link SimWorkload}s. - */ -public class SimWorkloads { - private SimWorkloads() {} - - /** - * Create a {@link SimWorkload} that executes a specified number of floating point operations (FLOPs) at the given - * utilization. - * - * @param flops The number of floating point operations to perform for this task in MFLOPs. - * @param utilization The CPU utilization of the workload. - */ - public static SimWorkload flops(long flops, double utilization) { - return new SimFlopsWorkload(flops, utilization); - } - - /** - * Create a {@link SimWorkload} that consumes the CPU resources for a specified duration at the given utilization. - * - * @param duration The duration of the workload in milliseconds. - * @param utilization The CPU utilization of the workload. - */ - public static SimWorkload runtime(long duration, double utilization) { - return runtime(duration, utilization, 0, 0); - } - - /** - * Create a {@link SimWorkload} that consumes the CPU resources for a specified duration at the given utilization. - * - * @param duration The duration of the workload in milliseconds. - * @param utilization The CPU utilization of the workload. - */ - public static SimWorkload runtime( - long duration, double utilization, long checkpointInterval, long checkpointDuration) { - return new SimRuntimeWorkload(duration, utilization, checkpointInterval, checkpointDuration); - } - - /** - * Create a {@link SimWorkload} that consumes the CPU resources for a specified duration at the given utilization. - * - * @param duration The duration of the workload. - * @param utilization The CPU utilization of the workload. - */ - public static SimWorkload runtime( - Duration duration, double utilization, long checkpointInterval, long checkpointDuration) { - return runtime(duration.toMillis(), utilization, checkpointInterval, checkpointDuration); - } - - /** - * Chain the specified <code>workloads</code> into a single {@link SimWorkload}. - */ - public static SimWorkload chain(SimWorkload... workloads) { - return new SimChainWorkload(workloads); - } -} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/SimTraceFragment.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/TraceFragment.java index 374e9732..550c2135 100644 --- 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/TraceFragment.java @@ -22,9 +22,9 @@ package org.opendc.simulator.compute.workload; -public record SimTraceFragment(long duration, double cpuUsage, int coreCount) { +public record TraceFragment(long duration, double cpuUsage, int coreCount) { - public SimTraceFragment(long start, long duration, double cpuUsage, int coreCount) { + public TraceFragment(long start, long duration, double cpuUsage, int coreCount) { this(duration, cpuUsage, coreCount); } } diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/TraceWorkload.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/TraceWorkload.java new file mode 100644 index 00000000..115689df --- /dev/null +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/TraceWorkload.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2024 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.simulator.compute.workload; + +import java.util.ArrayList; +import java.util.List; +import org.opendc.simulator.engine.FlowSupplier; + +public class TraceWorkload implements Workload { + private ArrayList<TraceFragment> fragments; + private final long checkpointInterval; + private final long checkpointDuration; + private final double checkpointIntervalScaling; + + public TraceWorkload( + ArrayList<TraceFragment> fragments, + long checkpointInterval, + long checkpointDuration, + double checkpointIntervalScaling) { + this.fragments = fragments; + this.checkpointInterval = checkpointInterval; + this.checkpointDuration = checkpointDuration; + this.checkpointIntervalScaling = checkpointIntervalScaling; + } + + public ArrayList<TraceFragment> getFragments() { + return fragments; + } + + @Override + public long getCheckpointInterval() { + return checkpointInterval; + } + + @Override + public long getCheckpointDuration() { + return checkpointDuration; + } + + @Override + public double getCheckpointIntervalScaling() { + return checkpointIntervalScaling; + } + + public void removeFragments(int numberOfFragments) { + if (numberOfFragments <= 0) { + return; + } + this.fragments.subList(0, numberOfFragments).clear(); + } + + public void addFirst(TraceFragment fragment) { + this.fragments.add(0, fragment); + } + + @Override + public SimWorkload startWorkload(FlowSupplier supplier, long now) { + return new SimTraceWorkload(supplier, this, now); + } + + public static Builder builder() { + return builder(0L, 0L, 0L); + } + + public static Builder builder(long checkpointInterval, long checkpointDuration, double checkpointIntervalScaling) { + return new Builder(checkpointInterval, checkpointDuration, checkpointIntervalScaling); + } + + /** + * Construct a {@link TraceWorkload} from the specified fragments. + * + * @param fragments The array of fragments to construct the trace from. + */ + public static TraceWorkload ofFragments(TraceFragment... fragments) { + final Builder builder = builder(); + + for (TraceFragment fragment : fragments) { + builder.add(fragment.duration(), fragment.cpuUsage(), fragment.coreCount()); + } + + return builder.build(); + } + + /** + * Construct a {@link TraceWorkload} from the specified fragments. + * + * @param fragments The fragments to construct the trace from. + */ + public static TraceWorkload ofFragments(List<TraceFragment> fragments) { + final Builder builder = builder(); + + for (TraceFragment fragment : fragments) { + builder.add(fragment.duration(), fragment.cpuUsage(), fragment.coreCount()); + } + + return builder.build(); + } + + public static final class Builder { + private final ArrayList<TraceFragment> fragments; + private final long checkpointInterval; + private final long checkpointDuration; + private final double checkpointIntervalScaling; + + /** + * Construct a new {@link Builder} instance. + */ + private Builder(long checkpointInterval, long checkpointDuration, double checkpointIntervalScaling) { + this.fragments = new ArrayList<>(); + this.checkpointInterval = checkpointInterval; + this.checkpointDuration = checkpointDuration; + this.checkpointIntervalScaling = checkpointIntervalScaling; + } + + /** + * Add a fragment to the trace. + * + * @param duration The timestamp at which the fragment ends (in epoch millis). + * @param usage The CPU usage at this fragment. + * @param cores The number of cores used during this fragment. + */ + public void add(long duration, double usage, int cores) { + fragments.add(0, new TraceFragment(duration, usage, cores)); + } + + /** + * Build the {@link TraceWorkload} instance. + */ + public TraceWorkload build() { + return new TraceWorkload( + this.fragments, this.checkpointInterval, this.checkpointDuration, this.checkpointIntervalScaling); + } + } +} diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernorFactory.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/Workload.java index 97a49879..cd34921a 100644 --- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/cpufreq/ScalingGovernorFactory.java +++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/workload/Workload.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 AtLarge Research + * Copyright (c) 2024 AtLarge Research * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,14 +20,17 @@ * SOFTWARE. */ -package org.opendc.simulator.compute.kernel.cpufreq; +package org.opendc.simulator.compute.workload; -/** - * Factory interface for a {@link ScalingGovernor}. - */ -public interface ScalingGovernorFactory { - /** - * Create the scaling logic for the specified {@link ScalingPolicy}. - */ - ScalingGovernor newGovernor(ScalingPolicy policy); +import org.opendc.simulator.engine.FlowSupplier; + +public interface Workload { + + long getCheckpointInterval(); + + long getCheckpointDuration(); + + double getCheckpointIntervalScaling(); + + SimWorkload startWorkload(FlowSupplier supplier, long now); } |
