diff options
5 files changed, 42 insertions, 33 deletions
diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/execution/ServerContext.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/execution/ServerContext.kt index 3a804f51..485fdcdf 100644 --- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/execution/ServerContext.kt +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/execution/ServerContext.kt @@ -54,10 +54,15 @@ public interface ServerContext { * Request the specified burst time from the processor cores and suspend execution until a processor core finishes * processing a **non-zero** burst or until the deadline is reached. * + * After the method returns, [burst] will contain the remaining burst length for each of the cores (which may be + * zero). + * + * Both [burst] and [maxUsage] must be of the same size and in any other case the method will throw an + * [IllegalArgumentException]. + * * @param burst The burst time to request from each of the processor cores. * @param maxUsage The maximum usage in terms of MHz that the processing core may use while running the burst. * @param deadline The instant at which this request needs to be fulfilled. - * @return The remaining burst time of the processor cores. */ - public suspend fun run(burst: LongArray, maxUsage: DoubleArray, deadline: Long): LongArray + public suspend fun run(burst: LongArray, maxUsage: DoubleArray, deadline: Long) } diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/image/FlopsApplicationImage.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/image/FlopsApplicationImage.kt index d2d35db9..27d8091a 100644 --- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/image/FlopsApplicationImage.kt +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/image/FlopsApplicationImage.kt @@ -42,7 +42,7 @@ import kotlin.math.min * @property cores The number of cores that the image is able to utilize. * @property utilization A model of the CPU utilization of the application. */ -class FlopsApplicationImage( +data class FlopsApplicationImage( public override val uid: UUID, public override val name: String, public override val tags: TagContainer, @@ -61,7 +61,7 @@ class FlopsApplicationImage( */ override suspend fun invoke(ctx: ServerContext) { val cores = min(this.cores, ctx.server.flavor.cpuCount) - var burst = LongArray(cores) { flops / cores } + val burst = LongArray(cores) { flops / cores } val maxUsage = DoubleArray(cores) { i -> ctx.cpus[i].frequency * utilization } while (coroutineContext.isActive) { @@ -69,7 +69,7 @@ class FlopsApplicationImage( break } - burst = ctx.run(burst, maxUsage, Long.MAX_VALUE) + ctx.run(burst, maxUsage, Long.MAX_VALUE) } } } diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/image/VmImage.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/image/VmImage.kt index 7c4fe839..52f9068d 100644 --- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/image/VmImage.kt +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/core/image/VmImage.kt @@ -33,4 +33,6 @@ class VmImage( } } } + + override fun toString(): String = "VmImage(uid=$uid, name=$name, cores=$cores, requiredMemory=$requiredMemory)" } diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriver.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriver.kt index d86e04ff..92338ca1 100644 --- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriver.kt +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/metal/driver/SimpleBareMetalDriver.kt @@ -167,15 +167,14 @@ public class SimpleBareMetalDriver( domain.launch { monitor.onUpdate(server, previousState) } } - override suspend fun run(burst: LongArray, maxUsage: DoubleArray, deadline: Long): LongArray { + override suspend fun run(burst: LongArray, maxUsage: DoubleArray, deadline: Long) { + require(burst.size == maxUsage.size) { "Array dimensions do not match" } + val start = simulationContext.clock.millis() var duration = max(0, deadline - start) - for (i in 0..cpus.size) { - if (i >= burst.size || i >= maxUsage.size) { - continue - } - + // Determine the duration of the first CPU to finish + for (i in 0 until min(cpus.size, burst.size)) { val cpu = cpus[i] val usage = min(maxUsage[i], cpu.frequency) * 1_000_000 // Usage from MHz to Hz val cpuDuration = ceil(burst[i] / usage * 1000).toLong() // Convert from seconds to milliseconds @@ -192,14 +191,12 @@ public class SimpleBareMetalDriver( } val end = simulationContext.clock.millis() - return LongArray(cpus.size) { i -> - if (i < burst.size && i < maxUsage.size) { - val usage = min(maxUsage[i], cpus[i].frequency) * 1_000_000 - val granted = ceil((end - start) / 1000.0 * usage).toLong() - max(0, burst[i] - granted) - } else { - 0 - } + + // Write back the remaining burst time + for (i in 0 until min(cpus.size, burst.size)) { + val usage = min(maxUsage[i], cpus[i].frequency) * 1_000_000 + val granted = ceil((end - start) / 1000.0 * usage).toLong() + burst[i] = max(0, burst[i] - granted) } } } diff --git a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorVirtDriver.kt b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorVirtDriver.kt index 87c4f073..2f1328ab 100644 --- a/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorVirtDriver.kt +++ b/opendc/opendc-compute/src/main/kotlin/com/atlarge/opendc/compute/virt/driver/hypervisor/HypervisorVirtDriver.kt @@ -105,6 +105,12 @@ class HypervisorVirtDriver( */ private suspend fun reschedule() { flush() + + // Do not schedule a call if there is no work to schedule + if (activeVms.isEmpty()) { + return + } + val call = simulationContext.domain.launch { val start = simulationContext.clock.millis() val vms = activeVms.toSet() @@ -114,7 +120,7 @@ class HypervisorVirtDriver( val usage = DoubleArray(hostContext.cpus.size) for (vm in vms) { - for (i in vm.cpus.indices) { + for (i in 0 until min(vm.cpus.size, vm.requestedBurst.size)) { val cpu = vm.cpus[i] // Limit each vCPU to at most an equal share of the host CPU @@ -130,7 +136,7 @@ class HypervisorVirtDriver( val burst = LongArray(hostContext.cpus.size) for (vm in vms) { - for (i in vm.cpus.indices) { + for (i in 0 until min(vm.cpus.size, vm.requestedBurst.size)) { val cpu = vm.cpus[i] // Limit each vCPU to at most an equal share of the host CPU @@ -143,7 +149,7 @@ class HypervisorVirtDriver( // We run the total burst on the host processor. Note that this call may be cancelled at any moment in // time, so not all of the burst may be executed. - val remainder = hostContext.run(burst, usage, deadline) + hostContext.run(burst, usage, deadline) val end = simulationContext.clock.millis() // No work was performed @@ -152,17 +158,18 @@ class HypervisorVirtDriver( } for (vm in vms) { - for (i in vm.cpus.indices) { + for (i in 0 until min(vm.cpus.size, vm.requestedBurst.size)) { val cpu = vm.cpus[i] // Limit each vCPU to at most an equal share of the host CPU val actualUsage = min(vm.requestedUsage[i], cpu.frequency / vms.size) + val actualBurst = (duration * actualUsage * 1_000_000L).toLong() // Compute the fraction of compute time allocated to the VM val fraction = actualUsage / usage[i] // Compute the burst time that the VM was actually granted - val grantedBurst = max(0, burst[i] - ceil(remainder[i] * fraction).toLong()) + val grantedBurst = max(0, actualBurst - ceil(burst[i] * fraction).toLong()) // Compute remaining burst time to be executed for the request vm.requestedBurst[i] = max(0, vm.requestedBurst[i] - grantedBurst) @@ -174,7 +181,6 @@ class HypervisorVirtDriver( } } } - this.call = call call.invokeOnCompletion { this.call = null } } @@ -194,8 +200,8 @@ class HypervisorVirtDriver( val monitor: ServerMonitor, ctx: SimulationContext ) : ServerManagementContext { - val requestedBurst: LongArray = LongArray(server.flavor.cpuCount) - val requestedUsage: DoubleArray = DoubleArray(server.flavor.cpuCount) + lateinit var requestedBurst: LongArray + lateinit var requestedUsage: DoubleArray var requestedDeadline: Long = 0L var chan = Channel<Unit>(Channel.RENDEZVOUS) private var initialized: Boolean = false @@ -234,11 +240,11 @@ class HypervisorVirtDriver( monitors.forEach { it.onUpdate(vms.size, availableMemory) } } - override suspend fun run(burst: LongArray, maxUsage: DoubleArray, deadline: Long): LongArray { - for (i in cpus.indices) { - requestedBurst[i] = if (i < burst.size) burst[i] else 0 - requestedUsage[i] = if (i < maxUsage.size) maxUsage[i] else 0.0 - } + override suspend fun run(burst: LongArray, maxUsage: DoubleArray, deadline: Long) { + require(burst.size == maxUsage.size) { "Array dimensions do not match" } + + requestedBurst = burst + requestedUsage = maxUsage requestedDeadline = deadline // Wait until the burst has been run or the coroutine is cancelled @@ -251,7 +257,6 @@ class HypervisorVirtDriver( } activeVms -= this reschedule() - return requestedBurst } } } |
