summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNiels Thiele <noleu66@posteo.net>2025-07-15 16:59:02 +0200
committerGitHub <noreply@github.com>2025-07-15 16:59:02 +0200
commit089c449762950b4322c04f73ef7fe0e10af615df (patch)
treee39d84af37fa821ffabbb94d25ad6c3de1e29f07
parenta5f3c19200026b9476edc39b951eb1003cff0831 (diff)
Implements Virtualization overhead modelling (#357)
-rw-r--r--opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/TopologyFactories.kt3
-rw-r--r--opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/specs/TopologySpecs.kt50
-rw-r--r--opendc-experiments/opendc-experiments-base/src/test/kotlin/org/opendc/experiments/base/VirtualizationOverheadTests.kt275
-rw-r--r--opendc-experiments/opendc-experiments-base/src/test/resources/topologies/Gpus/single_gpu_full.json5
-rw-r--r--opendc-experiments/opendc-experiments-base/src/test/resources/topologies/virtualizationOverhead/single_gpu_constant_overhead.json42
-rw-r--r--opendc-experiments/opendc-experiments-base/src/test/resources/topologies/virtualizationOverhead/single_gpu_custom_constant_overhead.json43
-rw-r--r--opendc-experiments/opendc-experiments-base/src/test/resources/topologies/virtualizationOverhead/single_gpu_no_model.json39
-rw-r--r--opendc-experiments/opendc-experiments-base/src/test/resources/topologies/virtualizationOverhead/single_gpu_no_overhead.json42
-rw-r--r--opendc-experiments/opendc-experiments-base/src/test/resources/topologies/virtualizationOverhead/single_gpu_share_based_overhead.json42
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/gpu/SimGpu.java42
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/machine/SimMachine.java3
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/models/GpuModel.java31
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/virtualization/OverheadModels/ConstantVirtualizationOverhead.java54
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/virtualization/OverheadModels/NoVirtualizationOverHead.java36
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/virtualization/OverheadModels/ShareBasedVirtualizationOverhead.java38
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/virtualization/VirtualizationOverheadModel.java33
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/virtualization/VirtualizationOverheadModelFactory.java88
-rw-r--r--opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/graph/FlowDistributor.java4
-rw-r--r--opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/graph/FlowEdge.java10
-rw-r--r--opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/graph/FlowSupplier.java6
20 files changed, 871 insertions, 15 deletions
diff --git a/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/TopologyFactories.kt b/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/TopologyFactories.kt
index c1617304..fd66ca11 100644
--- a/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/TopologyFactories.kt
+++ b/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/TopologyFactories.kt
@@ -32,6 +32,7 @@ import org.opendc.compute.topology.specs.HostSpec
import org.opendc.compute.topology.specs.PowerSourceSpec
import org.opendc.compute.topology.specs.TopologySpec
import org.opendc.compute.topology.specs.toDistributionPolicy
+import org.opendc.compute.topology.specs.toVirtualizationOverheadModel
import org.opendc.simulator.compute.models.CpuModel
import org.opendc.simulator.compute.models.GpuModel
import org.opendc.simulator.compute.models.MachineModel
@@ -171,6 +172,7 @@ private fun HostJSONSpec.toHostSpec(clusterName: String): HostSpec {
val unknownMemoryUnit = MemoryUnit(memory.vendor, memory.modelName, memory.memorySpeed.toMHz(), memory.memorySize.toMiB().toLong())
val gpuUnits =
List(gpu?.count ?: 0) {
+ val virtualizationOverheadModel = gpu?.virtualizationOverHeadModel?.toVirtualizationOverheadModel()
GpuModel(
globalGpuId++,
gpu!!.coreCount,
@@ -180,6 +182,7 @@ private fun HostJSONSpec.toHostSpec(clusterName: String): HostSpec {
gpu.vendor,
gpu.modelName,
gpu.architecture,
+ virtualizationOverheadModel,
)
}
diff --git a/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/specs/TopologySpecs.kt b/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/specs/TopologySpecs.kt
index f5c8ab31..667e9cbd 100644
--- a/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/specs/TopologySpecs.kt
+++ b/opendc-compute/opendc-compute-topology/src/main/kotlin/org/opendc/compute/topology/specs/TopologySpecs.kt
@@ -35,6 +35,7 @@ import org.opendc.simulator.compute.power.batteries.policy.DoubleThresholdBatter
import org.opendc.simulator.compute.power.batteries.policy.RunningMeanBatteryPolicy
import org.opendc.simulator.compute.power.batteries.policy.RunningMeanPlusBatteryPolicy
import org.opendc.simulator.compute.power.batteries.policy.SingleThresholdBatteryPolicy
+import org.opendc.simulator.compute.virtualization.VirtualizationOverheadModelFactory.VirtualizationOverheadModelEnum
import org.opendc.simulator.engine.engine.FlowEngine
import org.opendc.simulator.engine.graph.distributionPolicies.FlowDistributorFactory.DistributionPolicy
@@ -69,8 +70,12 @@ public data class ClusterJSONSpec(
* @param name The name of the host.
* @param cpu The CPU available in this cluster
* @param memory The amount of RAM memory available in Byte
- * @param powerModel The power model used to determine the power draw of a host
* @param count The power model used to determine the power draw of a host
+ * @param gpu The GPU available in this cluster (optional)
+ * @param cpuPowerModel The power model used to determine the power draw of the CPU
+ * @param gpuPowerModel The power model used to determine the power draw of the GPU
+ * @param cpuDistributionPolicy The distribution policy used to distribute CPU resources
+ * @param gpuDistributionPolicy The distribution policy used to distribute GPU resources
*/
@Serializable
public data class HostJSONSpec(
@@ -133,6 +138,7 @@ public data class GPUJSONSpec(
val vendor: String = "unknown",
val modelName: String = "unknown",
val architecture: String = "unknown",
+ val virtualizationOverHeadModel: VirtualizationOverheadModelSpec = NoVirtualizationOverheadModelSpec(),
)
@Serializable
@@ -216,6 +222,48 @@ public data class MaxMinFairnessDistributionPolicySpec(
override val type: DistributionPolicy = DistributionPolicy.MAX_MIN_FAIRNESS,
) : DistributionPolicySpec
+@Serializable
+public sealed interface VirtualizationOverheadModelSpec {
+ public val type: VirtualizationOverheadModelEnum
+}
+
+@Serializable
+@SerialName("NONE")
+public data class NoVirtualizationOverheadModelSpec(
+ override val type: VirtualizationOverheadModelEnum =
+ VirtualizationOverheadModelEnum.NONE,
+) : VirtualizationOverheadModelSpec
+
+@Serializable
+@SerialName("CONSTANT")
+public data class ConstantVirtualizationOverheadModelSpec(
+ override val type: VirtualizationOverheadModelEnum = VirtualizationOverheadModelEnum.CONSTANT,
+ val percentageOverhead: Double? = -1.0,
+) : VirtualizationOverheadModelSpec
+
+@Serializable
+@SerialName("SHARE_BASED")
+public data class ShareBasedVirtualizationOverheadModelSpec(
+ override val type: VirtualizationOverheadModelEnum = VirtualizationOverheadModelEnum.SHARE_BASED,
+) : VirtualizationOverheadModelSpec
+
+public fun VirtualizationOverheadModelSpec.toVirtualizationOverheadModel(): VirtualizationOverheadModelEnum {
+ return when (this) {
+ is NoVirtualizationOverheadModelSpec -> VirtualizationOverheadModelEnum.NONE
+ is ConstantVirtualizationOverheadModelSpec ->
+ VirtualizationOverheadModelEnum.CONSTANT.apply {
+ if (percentageOverhead != null) {
+ // -1.0 is used to indicate that no percentage overhead is specified
+ if (percentageOverhead != -1.0 && (percentageOverhead < 0.0 || percentageOverhead > 1.0)) {
+ throw IllegalArgumentException("Percentage overhead must be between 0.0 and 1.0")
+ }
+ setProperty("percentageOverhead", percentageOverhead)
+ }
+ }
+ is ShareBasedVirtualizationOverheadModelSpec -> VirtualizationOverheadModelEnum.SHARE_BASED
+ }
+}
+
/**
* Definition of a power source used for JSON input.
*
diff --git a/opendc-experiments/opendc-experiments-base/src/test/kotlin/org/opendc/experiments/base/VirtualizationOverheadTests.kt b/opendc-experiments/opendc-experiments-base/src/test/kotlin/org/opendc/experiments/base/VirtualizationOverheadTests.kt
new file mode 100644
index 00000000..18936a15
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-base/src/test/kotlin/org/opendc/experiments/base/VirtualizationOverheadTests.kt
@@ -0,0 +1,275 @@
+/*
+ * 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.experiments.base
+
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Assertions.assertInstanceOf
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.assertAll
+import org.opendc.compute.workload.Task
+import org.opendc.simulator.compute.virtualization.OverheadModels.ConstantVirtualizationOverhead
+import org.opendc.simulator.compute.virtualization.OverheadModels.NoVirtualizationOverHead
+import org.opendc.simulator.compute.virtualization.OverheadModels.ShareBasedVirtualizationOverhead
+import org.opendc.simulator.compute.workload.trace.TraceFragment
+import java.util.ArrayList
+
+class VirtualizationOverheadTests {
+ /**
+ * Test that the different virtualization overhead models are loaded correctly from a topology file.
+ */
+ @Test
+ fun loadsVirtualizationOverheadModelCorrectly() {
+ val noModelTopology = createTopology("virtualizationOverhead/single_gpu_no_model.json")
+ val noOverHeadTopology = createTopology("virtualizationOverhead/single_gpu_no_overhead.json")
+ val constantOverHeadTopology = createTopology("virtualizationOverhead/single_gpu_constant_overhead.json")
+ val customConstantOverHeadTopology = createTopology("virtualizationOverhead/single_gpu_custom_constant_overhead.json")
+ val shareBasedOverheadTopology = createTopology("virtualizationOverhead/single_gpu_share_based_overhead.json")
+
+ assertAll(
+ {
+ assertInstanceOf(
+ NoVirtualizationOverHead::class.java,
+ noModelTopology[0].hostSpecs[0].model.gpuModels[0].virtualizationOverheadModel,
+ "Did not load default model correctly, when no model was given.",
+ )
+ },
+ // no overhead
+ {
+ assertInstanceOf(
+ NoVirtualizationOverHead::class.java,
+ noOverHeadTopology[0].hostSpecs[0].model.gpuModels[0].virtualizationOverheadModel,
+ "Did not load no overhead model correctly.",
+ )
+ },
+ // default constant overhead
+ {
+ assertInstanceOf(
+ ConstantVirtualizationOverhead::class.java,
+ constantOverHeadTopology[0].hostSpecs[0].model.gpuModels[0].virtualizationOverheadModel,
+ "Did not load constant overhead model correctly.",
+ )
+ },
+ {
+ assertEquals(
+ 0.05,
+ (
+ constantOverHeadTopology[0].hostSpecs[0].model.gpuModels[0].virtualizationOverheadModel
+ as ConstantVirtualizationOverhead
+ ).percentageOverhead,
+ "Constant overhead should have 5% overhead",
+ )
+ },
+ // custom constant overhead
+ {
+ assertInstanceOf(
+ ConstantVirtualizationOverhead::class.java,
+ customConstantOverHeadTopology[0].hostSpecs[0].model.gpuModels[0].virtualizationOverheadModel,
+ "Did not load constant overhead model correctly, when overhead factor was given.",
+ )
+ },
+ {
+ assertEquals(
+ 0.25,
+ (
+ customConstantOverHeadTopology[0].hostSpecs[0].model.gpuModels[0].virtualizationOverheadModel
+ as ConstantVirtualizationOverhead
+ ).percentageOverhead,
+ "Custom constant overhead should have 25% overhead",
+ )
+ },
+ // share-based overhead
+ {
+ assertInstanceOf(
+ ShareBasedVirtualizationOverhead::class.java,
+ shareBasedOverheadTopology[0].hostSpecs[0].model.gpuModels[0].virtualizationOverheadModel,
+ "Did not load shared based overhead model correctly",
+ )
+ },
+ )
+ }
+
+ /**
+ * Test that the NoVirtualizationOverhead model does not apply any overhead.
+ */
+ @Test
+ fun noVirtualizationOverheadModelTest() {
+ val topology = createTopology("virtualizationOverhead/single_gpu_no_overhead.json")
+ val workload: ArrayList<Task> =
+ arrayListOf(
+ createTestTask(
+ name = "0",
+ fragments =
+ arrayListOf(
+ TraceFragment(10 * 60 * 1000, 1000.0, 1, 1000.0, 1),
+ ),
+ ),
+ )
+
+ val monitor = runTest(topology, workload)
+ assertEquals(1000.0, monitor.taskGpuDemands["0"]?.get(1), "Task 0 should have gpu demand 1000.0")
+ assertEquals(1000.0, monitor.taskGpuSupplied["0"]?.get(1), "Task 0 should have gpu supplied 1000.0 ")
+ assertEquals(1000.0, monitor.hostGpuDemands["H01"]?.get(1)?.get(0), "Host H01 should have gpu demand 1000.0")
+ assertEquals(1000.0, monitor.hostGpuSupplied["H01"]?.get(1)?.get(0), "Host H01 should have gpu supply 1000.0")
+ }
+
+ /**
+ * Test that the constant overhead model does apply the correct amount of overhead.
+ */
+ @Test
+ fun constantVirtualizationOverheadModelTest() {
+ val topology = createTopology("virtualizationOverhead/single_gpu_constant_overhead.json")
+ val workload: ArrayList<Task> =
+ arrayListOf(
+ createTestTask(
+ name = "0",
+ fragments =
+ arrayListOf(
+ TraceFragment(10 * 60 * 1000, 1000.0, 1, 1000.0, 1),
+ ),
+ ),
+ )
+
+ val monitor = runTest(topology, workload)
+ assertAll(
+ { assertEquals(1000.0, monitor.taskGpuDemands["0"]?.get(1), "Task 0 should have gpu demand 1000.0") },
+ { assertEquals(0.95 * 1000.0, monitor.taskGpuSupplied["0"]?.get(1), "Task 0 should have gpu supplied 950.0 ") },
+ { assertEquals(1000.0, monitor.hostGpuDemands["H01"]?.get(1)?.get(0), "Host H01 should have gpu demand 1000.0") },
+ { assertEquals(0.95 * 1000.0, monitor.hostGpuSupplied["H01"]?.get(1)?.get(0), "Host H01 should have gpu supply 950.0") },
+ )
+ }
+
+ /**
+ * Test that the custom constant overhead model does not apply the correct amount of overhead.
+ */
+ @Test
+ fun customConstantVirtualizationOverheadModelTest() {
+ val topology = createTopology("virtualizationOverhead/single_gpu_custom_constant_overhead.json")
+ val workload: ArrayList<Task> =
+ arrayListOf(
+ createTestTask(
+ name = "0",
+ fragments =
+ arrayListOf(
+ TraceFragment(10 * 60 * 1000, 1000.0, 1, 1000.0, 1),
+ ),
+ ),
+ )
+
+ val monitor = runTest(topology, workload)
+ assertAll(
+ { assertEquals(1000.0, monitor.taskGpuDemands["0"]?.get(1), "Task 0 should have gpu demand 1000.0") },
+ { assertEquals(0.75 * 1000.0, monitor.taskGpuSupplied["0"]?.get(1), "Task 0 should have gpu supplied 750.0 ") },
+ { assertEquals(1000.0, monitor.hostGpuDemands["H01"]?.get(1)?.get(0), "Host H01 should have gpu demand 1000.0") },
+ { assertEquals(0.75 * 1000.0, monitor.hostGpuSupplied["H01"]?.get(1)?.get(0), "Host H01 should have gpu supply 750.0") },
+ )
+ }
+
+ /**
+ * Test that the share-based overhead model does not applies the correct amount of overhead, depending on the number of VMs.
+ */
+ @Test
+ fun shareBasedVirtualizationOverheadModelTest() {
+ val topology = createTopology("virtualizationOverhead/single_gpu_share_based_overhead.json")
+ val workload1: ArrayList<Task> =
+ arrayListOf(
+ createTestTask(
+ name = "0",
+ fragments =
+ arrayListOf(
+ TraceFragment(10 * 60 * 1000, 1000.0, 1, 1000.0, 1),
+ ),
+ ),
+ )
+
+ val workload2: ArrayList<Task> =
+ arrayListOf(
+ createTestTask(
+ name = "0",
+ fragments =
+ arrayListOf(
+ TraceFragment(10 * 60 * 1000, 0.0, 0, 1000.0, 1),
+ ),
+ ),
+ createTestTask(
+ name = "1",
+ fragments =
+ arrayListOf(
+ TraceFragment(10 * 60 * 1000, 0.0, 0, 1000.0, 1),
+ ),
+ ),
+ )
+
+ val workload3: ArrayList<Task> =
+ arrayListOf(
+ createTestTask(
+ name = "0",
+ fragments =
+ arrayListOf(
+ TraceFragment(10 * 60 * 1000, 0.0, 0, 1000.0, 1),
+ ),
+ ),
+ createTestTask(
+ name = "1",
+ fragments =
+ arrayListOf(
+ TraceFragment(10 * 60 * 1000, 0.0, 0, 1000.0, 1),
+ ),
+ ),
+ createTestTask(
+ name = "2",
+ fragments =
+ arrayListOf(
+ TraceFragment(10 * 60 * 1000, 0.0, 0, 1000.0, 1),
+ ),
+ ),
+ )
+
+ val monitor1 = runTest(topology, workload1)
+ val monitor2 = runTest(topology, workload2)
+ val monitor3 = runTest(topology, workload3)
+
+ assertAll(
+ // Test with one VM
+ { assertEquals(1000.0, monitor1.taskGpuDemands["0"]?.get(1), "Task 0 should have gpu demand 1000.0") },
+ { assertEquals(1000.0, monitor1.taskGpuSupplied["0"]?.get(1), "Task 0 should have gpu supplied 1000.0 ") },
+ { assertEquals(1000.0, monitor1.hostGpuDemands["H01"]?.get(1)?.get(0), "Host H01 should have gpu demand 1000.0") },
+ { assertEquals(1000.0, monitor1.hostGpuSupplied["H01"]?.get(1)?.get(0), "Host H01 should have gpu supply 1000.0") },
+ // Test with two VMs
+ { assertEquals(1000.0, monitor2.taskGpuDemands["0"]?.get(1), "Task 0 should have gpu demand 1000.0") },
+ { assertEquals(500.0, monitor2.taskGpuSupplied["0"]?.get(1), "Task 0 should have gpu supplied 500.0") },
+ { assertEquals(1000.0, monitor2.taskGpuDemands["1"]?.get(1), "Task 0 should have gpu demand 1000.0") },
+ { assertEquals(500.0, monitor2.taskGpuSupplied["1"]?.get(1), "Task 0 should have gpu supplied 500.0") },
+ { assertEquals(2000.0, monitor2.hostGpuDemands["H01"]?.get(1)?.get(0), "Host H01 should have gpu demand 2000.0") },
+ { assertEquals(1000.0, monitor2.hostGpuSupplied["H01"]?.get(1)?.get(0), "Host H01 should have gpu supply 1000.0") },
+ // Test with three VMs
+ { assertEquals(1000.0, monitor3.taskGpuDemands["0"]?.get(1), "Task 0 should have gpu demand 1000.0") },
+ { assertEquals(333.3, monitor3.taskGpuSupplied["0"]?.get(1) ?: 0.0, 0.05, "Task 0 should have gpu supplied 333.3 ") },
+ { assertEquals(1000.0, monitor3.taskGpuDemands["1"]?.get(1), "Task 0 should have gpu demand 1000.0") },
+ { assertEquals(333.3, monitor3.taskGpuSupplied["1"]?.get(1) ?: 0.0, 0.05, "Task 0 should have gpu supplied 333.3 ") },
+ { assertEquals(1000.0, monitor3.taskGpuDemands["2"]?.get(1), "Task 0 should have gpu demand 1000.0") },
+ { assertEquals(333.3, monitor3.taskGpuSupplied["2"]?.get(1) ?: 0.0, 0.05, "Task 0 should have gpu supplied 333.3 ") },
+ { assertEquals(3000.0, monitor3.hostGpuDemands["H01"]?.get(1)?.get(0), "Host H01 should have gpu demand 3000.0") },
+ { assertEquals(1000.0, monitor3.hostGpuSupplied["H01"]?.get(1)?.get(0), "Host H01 should have gpu supply 700.0") },
+ )
+ }
+}
diff --git a/opendc-experiments/opendc-experiments-base/src/test/resources/topologies/Gpus/single_gpu_full.json b/opendc-experiments/opendc-experiments-base/src/test/resources/topologies/Gpus/single_gpu_full.json
index 8e4c3546..d3e897bd 100644
--- a/opendc-experiments/opendc-experiments-base/src/test/resources/topologies/Gpus/single_gpu_full.json
+++ b/opendc-experiments/opendc-experiments-base/src/test/resources/topologies/Gpus/single_gpu_full.json
@@ -29,7 +29,10 @@
"memoryBandwidth": "900 GBps",
"vendor": "NVIDIA",
"modelName": "Tesla V100",
- "architecture": "Volta"
+ "architecture": "Volta",
+ "virtualizationOverHeadModel": {
+ "type": "CONSTANT"
+ }
},
"gpuPowerModel": {
"modelType": "linear",
diff --git a/opendc-experiments/opendc-experiments-base/src/test/resources/topologies/virtualizationOverhead/single_gpu_constant_overhead.json b/opendc-experiments/opendc-experiments-base/src/test/resources/topologies/virtualizationOverhead/single_gpu_constant_overhead.json
new file mode 100644
index 00000000..d3651cba
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-base/src/test/resources/topologies/virtualizationOverhead/single_gpu_constant_overhead.json
@@ -0,0 +1,42 @@
+{
+ "clusters":
+ [
+ {
+ "name": "C01",
+ "hosts" :
+ [
+ {
+ "name": "H01",
+ "cpu":
+ {
+ "coreCount": 1,
+ "coreSpeed": 2000
+ },
+ "memory": {
+ "memorySize": 140457600000
+ },
+ "cpuPowerModel": {
+ "modelType": "linear",
+ "power": 400.0,
+ "idlePower": 100.0,
+ "maxPower": 200.0
+ },
+ "gpu":
+ {
+ "coreCount": 3,
+ "coreSpeed": 2000,
+ "virtualizationOverHeadModel": {
+ "type": "CONSTANT"
+ }
+ },
+ "gpuPowerModel": {
+ "modelType": "linear",
+ "power": 800.0,
+ "idlePower": 300.0,
+ "maxPower": 600.0
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/opendc-experiments/opendc-experiments-base/src/test/resources/topologies/virtualizationOverhead/single_gpu_custom_constant_overhead.json b/opendc-experiments/opendc-experiments-base/src/test/resources/topologies/virtualizationOverhead/single_gpu_custom_constant_overhead.json
new file mode 100644
index 00000000..db30608e
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-base/src/test/resources/topologies/virtualizationOverhead/single_gpu_custom_constant_overhead.json
@@ -0,0 +1,43 @@
+{
+ "clusters":
+ [
+ {
+ "name": "C01",
+ "hosts" :
+ [
+ {
+ "name": "H01",
+ "cpu":
+ {
+ "coreCount": 1,
+ "coreSpeed": 2000
+ },
+ "memory": {
+ "memorySize": 140457600000
+ },
+ "cpuPowerModel": {
+ "modelType": "linear",
+ "power": 400.0,
+ "idlePower": 100.0,
+ "maxPower": 200.0
+ },
+ "gpu":
+ {
+ "coreCount": 3,
+ "coreSpeed": 2000,
+ "virtualizationOverHeadModel": {
+ "type": "CONSTANT",
+ "percentageOverhead": 0.25
+ }
+ },
+ "gpuPowerModel": {
+ "modelType": "linear",
+ "power": 800.0,
+ "idlePower": 300.0,
+ "maxPower": 600.0
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/opendc-experiments/opendc-experiments-base/src/test/resources/topologies/virtualizationOverhead/single_gpu_no_model.json b/opendc-experiments/opendc-experiments-base/src/test/resources/topologies/virtualizationOverhead/single_gpu_no_model.json
new file mode 100644
index 00000000..e272e924
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-base/src/test/resources/topologies/virtualizationOverhead/single_gpu_no_model.json
@@ -0,0 +1,39 @@
+{
+ "clusters":
+ [
+ {
+ "name": "C01",
+ "hosts" :
+ [
+ {
+ "name": "H01",
+ "cpu":
+ {
+ "coreCount": 1,
+ "coreSpeed": 2000
+ },
+ "memory": {
+ "memorySize": 140457600000
+ },
+ "cpuPowerModel": {
+ "modelType": "linear",
+ "power": 400.0,
+ "idlePower": 100.0,
+ "maxPower": 200.0
+ },
+ "gpu":
+ {
+ "coreCount": 3,
+ "coreSpeed": 2000
+ },
+ "gpuPowerModel": {
+ "modelType": "linear",
+ "power": 800.0,
+ "idlePower": 300.0,
+ "maxPower": 600.0
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/opendc-experiments/opendc-experiments-base/src/test/resources/topologies/virtualizationOverhead/single_gpu_no_overhead.json b/opendc-experiments/opendc-experiments-base/src/test/resources/topologies/virtualizationOverhead/single_gpu_no_overhead.json
new file mode 100644
index 00000000..f4cc893d
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-base/src/test/resources/topologies/virtualizationOverhead/single_gpu_no_overhead.json
@@ -0,0 +1,42 @@
+{
+ "clusters":
+ [
+ {
+ "name": "C01",
+ "hosts" :
+ [
+ {
+ "name": "H01",
+ "cpu":
+ {
+ "coreCount": 1,
+ "coreSpeed": 2000
+ },
+ "memory": {
+ "memorySize": 140457600000
+ },
+ "cpuPowerModel": {
+ "modelType": "linear",
+ "power": 400.0,
+ "idlePower": 100.0,
+ "maxPower": 200.0
+ },
+ "gpu":
+ {
+ "coreCount": 3,
+ "coreSpeed": 2000,
+ "virtualizationOverHeadModel": {
+ "type": "NONE"
+ }
+ },
+ "gpuPowerModel": {
+ "modelType": "linear",
+ "power": 800.0,
+ "idlePower": 300.0,
+ "maxPower": 600.0
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/opendc-experiments/opendc-experiments-base/src/test/resources/topologies/virtualizationOverhead/single_gpu_share_based_overhead.json b/opendc-experiments/opendc-experiments-base/src/test/resources/topologies/virtualizationOverhead/single_gpu_share_based_overhead.json
new file mode 100644
index 00000000..00fb665f
--- /dev/null
+++ b/opendc-experiments/opendc-experiments-base/src/test/resources/topologies/virtualizationOverhead/single_gpu_share_based_overhead.json
@@ -0,0 +1,42 @@
+{
+ "clusters":
+ [
+ {
+ "name": "C01",
+ "hosts" :
+ [
+ {
+ "name": "H01",
+ "cpu":
+ {
+ "coreCount": 1,
+ "coreSpeed": 2000
+ },
+ "memory": {
+ "memorySize": 140457600000
+ },
+ "cpuPowerModel": {
+ "modelType": "linear",
+ "power": 400.0,
+ "idlePower": 100.0,
+ "maxPower": 200.0
+ },
+ "gpu":
+ {
+ "coreCount": 3,
+ "coreSpeed": 1000,
+ "virtualizationOverHeadModel": {
+ "type": "SHARE_BASED"
+ }
+ },
+ "gpuPowerModel": {
+ "modelType": "linear",
+ "power": 800.0,
+ "idlePower": 300.0,
+ "maxPower": 600.0
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/gpu/SimGpu.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/gpu/SimGpu.java
index c5778dc0..99317a08 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/gpu/SimGpu.java
+++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/gpu/SimGpu.java
@@ -29,6 +29,7 @@ import org.opendc.simulator.compute.ComputeResource;
import org.opendc.simulator.compute.machine.PerformanceCounters;
import org.opendc.simulator.compute.models.GpuModel;
import org.opendc.simulator.compute.power.PowerModel;
+import org.opendc.simulator.compute.virtualization.VirtualizationOverheadModel;
import org.opendc.simulator.engine.engine.FlowEngine;
import org.opendc.simulator.engine.graph.FlowConsumer;
import org.opendc.simulator.engine.graph.FlowEdge;
@@ -47,12 +48,12 @@ public final class SimGpu extends FlowNode implements FlowSupplier, FlowConsumer
private final PowerModel gpuPowerModel;
- private double currentGpuDemand = 0.0f; // cpu capacity demanded by the mux
+ private double currentGpuDemand = 0.0f; // gpu capacity demanded by the mux
private double currentGpuUtilization = 0.0f;
- private double currentGpuSupplied = 0.0f; // cpu capacity supplied to the mux
+ private double currentGpuSupplied = 0.0f; // gpu capacity supplied to the mux
private double currentPowerDemand; // power demanded of the psu
- private double currentPowerSupplied = 0.0f; // cpu capacity supplied by the psu
+ private double currentPowerSupplied = 0.0f; // gpu capacity supplied by the psu
private double maxCapacity;
@@ -60,6 +61,9 @@ public final class SimGpu extends FlowNode implements FlowSupplier, FlowConsumer
private long lastCounterUpdate;
private final double gpuFrequencyInv;
+ private final VirtualizationOverheadModel virtualizationOverheadModel;
+ private int consumerCount = 0; // Number of consumers connected to this GPU
+
private FlowEdge distributorEdge;
private FlowEdge psuEdge;
@@ -110,7 +114,12 @@ public final class SimGpu extends FlowNode implements FlowSupplier, FlowConsumer
// Constructors
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- public SimGpu(FlowEngine engine, GpuModel gpuModel, PowerModel powerModel, int id) {
+ public SimGpu(
+ FlowEngine engine,
+ GpuModel gpuModel,
+ PowerModel powerModel,
+ int id,
+ VirtualizationOverheadModel overheadModel) {
super(engine);
this.id = id;
this.gpuModel = gpuModel;
@@ -123,6 +132,7 @@ public final class SimGpu extends FlowNode implements FlowSupplier, FlowConsumer
this.gpuFrequencyInv = 1 / this.maxCapacity;
this.currentPowerDemand = this.gpuPowerModel.computePower(this.currentGpuUtilization);
+ this.virtualizationOverheadModel = overheadModel;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -139,8 +149,8 @@ public final class SimGpu extends FlowNode implements FlowSupplier, FlowConsumer
return Long.MAX_VALUE;
}
-
- this.currentGpuSupplied = Math.min(this.currentGpuDemand, this.maxCapacity);
+ this.currentGpuSupplied = virtualizationOverheadModel.getSupply(
+ Math.min(this.currentGpuDemand, this.maxCapacity), this.consumerCount);
this.pushOutgoingSupply(this.distributorEdge, this.currentGpuSupplied);
return Long.MAX_VALUE;
@@ -213,13 +223,31 @@ public final class SimGpu extends FlowNode implements FlowSupplier, FlowConsumer
this.distributorEdge.pushSupply(newGpuSupply, true, resourceType);
}
+ @Override
+ public void handleIncomingDemand(FlowEdge consumerEdge, double newGpuDemand) {
+ updateCounters();
+ this.currentGpuDemand = newGpuDemand;
+
+ this.currentGpuUtilization = Math.min(this.currentGpuDemand / this.maxCapacity, 1.0);
+
+ // Calculate Power Demand and send to PSU
+ this.currentPowerDemand = this.gpuPowerModel.computePower(this.currentGpuUtilization);
+
+ this.invalidate();
+ }
+
/**
* Handle new demand coming in from the mux
*/
@Override
- public void handleIncomingDemand(FlowEdge consumerEdge, double newGpuDemand) {
+ public void handleIncomingDemand(
+ FlowEdge consumerEdge, double newGpuDemand, ResourceType resourceType, int consumerCount) {
+ if (resourceType != ResourceType.GPU) {
+ throw new IllegalArgumentException("Resource type must be GPU");
+ }
updateCounters();
this.currentGpuDemand = newGpuDemand;
+ this.consumerCount = consumerCount;
this.currentGpuUtilization = Math.min(this.currentGpuDemand / this.maxCapacity, 1.0);
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
index 5f4a4fcd..7158356a 100644
--- 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
@@ -242,7 +242,8 @@ public class SimMachine {
for (GpuModel gpuModel : machineModel.getGpuModels()) {
// create a new GPU
- SimGpu gpu = new SimGpu(engine, gpuModel, gpuPowerModel, gpuModel.getId());
+ SimGpu gpu = new SimGpu(
+ engine, gpuModel, gpuPowerModel, gpuModel.getId(), gpuModel.getVirtualizationOverheadModel());
gpus.add(gpu);
// Connect the GPU to the distributor
new FlowEdge(
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/models/GpuModel.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/models/GpuModel.java
index b804b061..e62c93da 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/models/GpuModel.java
+++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/models/GpuModel.java
@@ -23,6 +23,9 @@
package org.opendc.simulator.compute.models;
import java.util.Objects;
+import org.opendc.simulator.compute.virtualization.VirtualizationOverheadModel;
+import org.opendc.simulator.compute.virtualization.VirtualizationOverheadModelFactory;
+import org.opendc.simulator.compute.virtualization.VirtualizationOverheadModelFactory.VirtualizationOverheadModelEnum;
/**
* A single logical compute unit of processor node, either virtual or physical.
@@ -37,6 +40,7 @@ public final class GpuModel {
private final String vendor;
private final String modelName;
private final String arch;
+ private VirtualizationOverheadModel virtualizationOverheadModel;
/**
* Construct a {@link GpuModel} instance.
@@ -49,6 +53,7 @@ public final class GpuModel {
* @param vendor The vendor of the GPU
* @param modelName The name of the GPU
* @param arch The architecture of the GPU
+ * @param virtualizationOverheadModel The virtualization overhead model of this GPU.
*/
public GpuModel(
int id,
@@ -58,7 +63,8 @@ public final class GpuModel {
long memorySize,
String vendor,
String modelName,
- String arch) {
+ String arch,
+ VirtualizationOverheadModelEnum virtualizationOverheadModel) {
this.id = id;
this.coreCount = coreCount;
this.coreSpeed = coreSpeed;
@@ -68,6 +74,8 @@ public final class GpuModel {
this.vendor = vendor;
this.modelName = modelName;
this.arch = arch;
+ this.virtualizationOverheadModel =
+ VirtualizationOverheadModelFactory.getVirtualizationOverheadModel(virtualizationOverheadModel);
}
/**
@@ -78,11 +86,20 @@ public final class GpuModel {
* @param coreSpeed The speed of a single core
*/
public GpuModel(int id, int coreCount, double coreSpeed) {
- this(id, coreCount, coreSpeed, 0, 0, "unkown", "unkown", "unkown");
+ this(id, coreCount, coreSpeed, 0, 0, "unkown", "unkown", "unkown", VirtualizationOverheadModelEnum.NONE);
}
public GpuModel(int id, int coreCount, double coreSpeed, double memoryBandwidth, long memorySize) {
- this(id, coreCount, coreSpeed, memoryBandwidth, memorySize, "unkown", "unkown", "unkown");
+ this(
+ id,
+ coreCount,
+ coreSpeed,
+ memoryBandwidth,
+ memorySize,
+ "unkown",
+ "unkown",
+ "unkown",
+ VirtualizationOverheadModelEnum.NONE);
}
/**
@@ -148,6 +165,14 @@ public final class GpuModel {
return arch;
}
+ /**
+ * Return the virtualization overhead model of this GPU.
+ * @return The virtualization overhead model of this GPU.
+ */
+ public VirtualizationOverheadModel getVirtualizationOverheadModel() {
+ return this.virtualizationOverheadModel;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/virtualization/OverheadModels/ConstantVirtualizationOverhead.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/virtualization/OverheadModels/ConstantVirtualizationOverhead.java
new file mode 100644
index 00000000..e30133a5
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/virtualization/OverheadModels/ConstantVirtualizationOverhead.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2025 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 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.virtualization.OverheadModels;
+
+import org.opendc.simulator.compute.virtualization.VirtualizationOverheadModel;
+
+/**
+ * A VirtualizationOverheadModel that applies a constant percentage overhead to the current GPU demand.
+ * This model is useful for scenarios where a fixed overhead is expected regardless of the number of consumers.
+ */
+public class ConstantVirtualizationOverhead implements VirtualizationOverheadModel {
+
+ private double percentageOverhead = 0.0;
+
+ public double getPercentageOverhead() {
+ return percentageOverhead;
+ }
+
+ /**
+ * Creates a new instance of ConstantVirtualizationOverhead with the specified percentage overhead.
+ *
+ * @param percentageOverhead The percentage overhead to apply to the current GPU demand.
+ * If set to -1.0, a default value of 0.05 (5%) is used.
+ */
+ public ConstantVirtualizationOverhead(double percentageOverhead) {
+
+ this.percentageOverhead = (percentageOverhead == -1.0) ? 0.05 : percentageOverhead;
+ }
+
+ @Override
+ public double getSupply(double currentGpuDemand, int consumerCount) {
+ return currentGpuDemand * (1 - percentageOverhead);
+ }
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/virtualization/OverheadModels/NoVirtualizationOverHead.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/virtualization/OverheadModels/NoVirtualizationOverHead.java
new file mode 100644
index 00000000..114962ef
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/virtualization/OverheadModels/NoVirtualizationOverHead.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2025 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 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.virtualization.OverheadModels;
+
+import org.opendc.simulator.compute.virtualization.VirtualizationOverheadModel;
+
+/**
+ * A VirtualizationOverheadModel that does not introduce any overhead.
+ * It simply returns the current GPU demand as the supply.
+ */
+public class NoVirtualizationOverHead implements VirtualizationOverheadModel {
+ @Override
+ public double getSupply(double currentGpuDemand, int consumerCount) {
+ return currentGpuDemand; // No overhead, so supply is equal to demand
+ }
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/virtualization/OverheadModels/ShareBasedVirtualizationOverhead.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/virtualization/OverheadModels/ShareBasedVirtualizationOverhead.java
new file mode 100644
index 00000000..e1562c38
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/virtualization/OverheadModels/ShareBasedVirtualizationOverhead.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2025 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 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.virtualization.OverheadModels;
+
+import org.opendc.simulator.compute.virtualization.VirtualizationOverheadModel;
+
+/**
+ * A VirtualizationOverheadModel that divides the current GPU demand by the number of consumers.
+ * This model assumes that the supply is shared among all consumers, effectively reducing the
+ * supply available to each consumer based on the number of consumers.
+ */
+public class ShareBasedVirtualizationOverhead implements VirtualizationOverheadModel {
+ @Override
+ public double getSupply(double currentGpuDemand, int consumerCount) {
+ // Supply is divided by the number of consumers to account for sharing
+ return currentGpuDemand / (consumerCount == 0 ? 1 : consumerCount);
+ }
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/virtualization/VirtualizationOverheadModel.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/virtualization/VirtualizationOverheadModel.java
new file mode 100644
index 00000000..149780f8
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/virtualization/VirtualizationOverheadModel.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2025 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 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.virtualization;
+
+/**
+ * An interface for modeling the overhead introduced by virtualization in a compute environment.
+ * This model is used to determine the effective supply of resources available to consumers based
+ * on the current demand and the number of consumers.
+ */
+public interface VirtualizationOverheadModel {
+
+ public double getSupply(double currentGpuDemand, int consumerCount);
+}
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/virtualization/VirtualizationOverheadModelFactory.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/virtualization/VirtualizationOverheadModelFactory.java
new file mode 100644
index 00000000..8eee2b01
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/virtualization/VirtualizationOverheadModelFactory.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2025 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 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.virtualization;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import org.opendc.simulator.compute.virtualization.OverheadModels.ConstantVirtualizationOverhead;
+import org.opendc.simulator.compute.virtualization.OverheadModels.NoVirtualizationOverHead;
+import org.opendc.simulator.compute.virtualization.OverheadModels.ShareBasedVirtualizationOverhead;
+
+/**
+ * A factory class for creating instances of VirtualizationOverheadModel based on the specified type.
+ * This factory supports different virtualization overhead models, including no overhead, constant overhead,
+ * and share-based overhead.
+ */
+public class VirtualizationOverheadModelFactory {
+
+ public enum VirtualizationOverheadModelEnum {
+ NONE,
+ // General virtualization models -> Passthrough vs Full/Para virtualization
+ CONSTANT,
+ // Hardware assisted virtualization models
+ SHARE_BASED;
+
+ private final Map<String, Object> properties = new HashMap<>();
+
+ public void setProperty(String key, Object value) {
+ properties.put(key, value);
+ }
+
+ public Object getProperty(String key) {
+ return properties.get(key);
+ }
+
+ public <T> T getProperty(String key, Class<T> type) {
+ return type.cast(properties.get(key));
+ }
+
+ public Set<String> getPropertyNames() {
+ return properties.keySet();
+ }
+ }
+
+ /**
+ * Factory method to create a VirtualizationOverheadModel based on the specified type.
+ *
+ * @param virtualizationOverheadModelType The type of virtualization overhead model to create.
+ * @return An instance of the specified VirtualizationOverheadModel.
+ */
+ public static VirtualizationOverheadModel getVirtualizationOverheadModel(
+ VirtualizationOverheadModelEnum virtualizationOverheadModelType) {
+ return switch (virtualizationOverheadModelType) {
+ case NONE -> new NoVirtualizationOverHead();
+ case CONSTANT -> {
+ double percentageOverhead = -1.0; // Default value if not set
+ if (virtualizationOverheadModelType.getPropertyNames().contains("percentageOverhead")) {
+ percentageOverhead =
+ virtualizationOverheadModelType.getProperty("percentageOverhead", Double.class);
+ }
+ yield new ConstantVirtualizationOverhead(percentageOverhead);
+ }
+ case SHARE_BASED -> new ShareBasedVirtualizationOverhead();
+ default -> throw new IllegalArgumentException(
+ "Unknown virtualization overhead model type: " + virtualizationOverheadModelType);
+ };
+ }
+}
diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/graph/FlowDistributor.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/graph/FlowDistributor.java
index cae3e8a1..c388293b 100644
--- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/graph/FlowDistributor.java
+++ b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/graph/FlowDistributor.java
@@ -102,7 +102,6 @@ public abstract class FlowDistributor extends FlowNode implements FlowSupplier,
protected abstract void updateOutgoingDemand();
- // TODO: This should probably be moved to the distribution strategy
protected abstract void updateOutgoingSupplies();
public abstract double[] distributeSupply(
@@ -120,6 +119,7 @@ public abstract class FlowDistributor extends FlowNode implements FlowSupplier,
this.incomingDemands.add(0.0);
this.outgoingSupplies.add(0.0);
this.consumerResourceType = consumerEdge.getConsumerResourceType();
+ this.outgoingDemandUpdateNeeded = true;
}
@Override
@@ -233,7 +233,7 @@ public abstract class FlowDistributor extends FlowNode implements FlowSupplier,
@Override
public void pushOutgoingDemand(FlowEdge supplierEdge, double newDemand) {
- supplierEdge.pushDemand(newDemand, false, this.getSupplierResourceType());
+ supplierEdge.pushDemand(newDemand, false, this.getSupplierResourceType(), this.consumerEdges.size());
}
@Override
diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/graph/FlowEdge.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/graph/FlowEdge.java
index db2a2944..1e65998b 100644
--- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/graph/FlowEdge.java
+++ b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/graph/FlowEdge.java
@@ -177,6 +177,16 @@ public class FlowEdge {
this.supplierIndex = supplierIndex;
}
+ public void pushDemand(double newDemand, boolean forceThrough, ResourceType resourceType, int consumerCount) {
+ // or store last resource type in the edge
+ if ((newDemand == this.demand) && !forceThrough) {
+ return;
+ }
+
+ this.demand = newDemand;
+ this.supplier.handleIncomingDemand(this, newDemand, resourceType, consumerCount);
+ }
+
public void pushDemand(double newDemand, boolean forceThrough, ResourceType resourceType) {
// or store last resource type in the edge
if ((newDemand == this.demand) && !forceThrough) {
diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/graph/FlowSupplier.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/graph/FlowSupplier.java
index eb665b8c..2f5f80ef 100644
--- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/graph/FlowSupplier.java
+++ b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/engine/graph/FlowSupplier.java
@@ -32,6 +32,12 @@ public interface FlowSupplier {
handleIncomingDemand(consumerEdge, newDemand);
}
+ default void handleIncomingDemand(
+ FlowEdge consumerEdge, double newDemand, ResourceType resourceType, int consumerCount) {
+ handleIncomingDemand(consumerEdge, newDemand);
+ }
+ ;
+
void pushOutgoingSupply(FlowEdge consumerEdge, double newSupply);
default void pushOutgoingSupply(FlowEdge consumerEdge, double newSupply, ResourceType resourceType) {