From 966715d7df139a431293f5c2fc67916fbcc1ecfb Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Mon, 21 Jun 2021 11:36:25 +0200 Subject: simulator: Reduce allocations in interpreter hot path This change updates the resources module to reduce the number of object allocations in the interpreter's hot path. This in turn should reduce the GC pressure. --- .../resources/SimResourceAggregatorMaxMin.kt | 2 +- .../resources/SimResourceDistributorMaxMin.kt | 77 ++++++++++---------- .../resources/impl/SimResourceContextImpl.kt | 81 +++++++++++----------- 3 files changed, 79 insertions(+), 81 deletions(-) (limited to 'opendc-simulator') diff --git a/opendc-simulator/opendc-simulator-resources/src/main/kotlin/org/opendc/simulator/resources/SimResourceAggregatorMaxMin.kt b/opendc-simulator/opendc-simulator-resources/src/main/kotlin/org/opendc/simulator/resources/SimResourceAggregatorMaxMin.kt index c39c1aca..991cda7a 100644 --- a/opendc-simulator/opendc-simulator-resources/src/main/kotlin/org/opendc/simulator/resources/SimResourceAggregatorMaxMin.kt +++ b/opendc-simulator/opendc-simulator-resources/src/main/kotlin/org/opendc/simulator/resources/SimResourceAggregatorMaxMin.kt @@ -45,7 +45,7 @@ public class SimResourceAggregatorMaxMin( val command = if (grantedWork > 0.0 && grantedSpeed > 0.0) SimResourceCommand.Consume(grantedWork, grantedSpeed, deadline) else - SimResourceCommand.Idle(deadline) + SimResourceCommand.Idle() input.push(command) } } diff --git a/opendc-simulator/opendc-simulator-resources/src/main/kotlin/org/opendc/simulator/resources/SimResourceDistributorMaxMin.kt b/opendc-simulator/opendc-simulator-resources/src/main/kotlin/org/opendc/simulator/resources/SimResourceDistributorMaxMin.kt index f7c5c5d7..d8fc8cb6 100644 --- a/opendc-simulator/opendc-simulator-resources/src/main/kotlin/org/opendc/simulator/resources/SimResourceDistributorMaxMin.kt +++ b/opendc-simulator/opendc-simulator-resources/src/main/kotlin/org/opendc/simulator/resources/SimResourceDistributorMaxMin.kt @@ -65,7 +65,7 @@ public class SimResourceDistributorMaxMin( /* SimResourceConsumer */ override fun onNext(ctx: SimResourceContext): SimResourceCommand { - return doNext(ctx.capacity) + return doNext(ctx) } override fun onEvent(ctx: SimResourceContext, event: SimResourceEvent) { @@ -94,12 +94,13 @@ public class SimResourceDistributorMaxMin( /** * Schedule the work of the outputs. */ - private fun doNext(capacity: Double): SimResourceCommand { + private fun doNext(ctx: SimResourceContext): SimResourceCommand { // If there is no work yet, mark the input as idle. if (activeOutputs.isEmpty()) { return SimResourceCommand.Idle() } + val capacity = ctx.capacity var duration: Double = Double.MAX_VALUE var deadline: Long = Long.MAX_VALUE var availableSpeed = capacity @@ -112,7 +113,7 @@ public class SimResourceDistributorMaxMin( output.pull() // Remove outputs that have finished - if (output.isFinished) { + if (!output.isActive) { outputIterator.remove() } } @@ -125,33 +126,23 @@ public class SimResourceDistributorMaxMin( var remaining = activeOutputs.size for (output in activeOutputs) { val availableShare = availableSpeed / remaining-- + val grantedSpeed = min(output.allowedSpeed, availableShare) + deadline = min(deadline, output.deadline) - when (val command = output.activeCommand) { - is SimResourceCommand.Idle -> { - deadline = min(deadline, command.deadline) - output.actualSpeed = 0.0 - } - is SimResourceCommand.Consume -> { - val grantedSpeed = min(output.allowedSpeed, availableShare) - deadline = min(deadline, command.deadline) - - // Ignore idle computation - if (grantedSpeed <= 0.0 || command.work <= 0.0) { - output.actualSpeed = 0.0 - continue - } + // Ignore idle computation + if (grantedSpeed <= 0.0 || output.work <= 0.0) { + output.actualSpeed = 0.0 + continue + } - totalRequestedSpeed += command.limit - totalRequestedWork += command.work + totalRequestedSpeed += output.limit + totalRequestedWork += output.work - output.actualSpeed = grantedSpeed - availableSpeed -= grantedSpeed + output.actualSpeed = grantedSpeed + availableSpeed -= grantedSpeed - // The duration that we want to run is that of the shortest request of an output - duration = min(duration, command.work / grantedSpeed) - } - SimResourceCommand.Exit -> assert(false) { "Did not expect output to be stopped" } - } + // The duration that we want to run is that of the shortest request of an output + duration = min(duration, output.work / grantedSpeed) } assert(deadline >= interpreter.clock.millis()) { "Deadline already passed" } @@ -189,9 +180,19 @@ public class SimResourceDistributorMaxMin( private var isClosed: Boolean = false /** - * The current command that is processed by the resource. + * The current requested work. + */ + var work: Double = 0.0 + + /** + * The requested limit. */ - var activeCommand: SimResourceCommand = SimResourceCommand.Idle() + var limit: Double = 0.0 + + /** + * The current deadline. + */ + var deadline: Long = Long.MAX_VALUE /** * The processing speed that is allowed by the model constraints. @@ -203,12 +204,6 @@ public class SimResourceDistributorMaxMin( */ var actualSpeed: Double = 0.0 - /** - * A flag to indicate that the output is finished. - */ - val isFinished - get() = activeCommand is SimResourceCommand.Exit - /** * The timestamp at which we received the last command. */ @@ -238,15 +233,19 @@ public class SimResourceDistributorMaxMin( /* SimResourceProviderLogic */ override fun onIdle(ctx: SimResourceControllableContext, deadline: Long): Long { allowedSpeed = 0.0 - activeCommand = SimResourceCommand.Idle(deadline) + this.deadline = deadline + work = 0.0 + limit = 0.0 lastCommandTimestamp = ctx.clock.millis() return Long.MAX_VALUE } override fun onConsume(ctx: SimResourceControllableContext, work: Double, limit: Double, deadline: Long): Long { - allowedSpeed = ctx.speed - activeCommand = SimResourceCommand.Consume(work, limit, deadline) + allowedSpeed = min(ctx.capacity, limit) + this.work = work + this.limit = limit + this.deadline = deadline lastCommandTimestamp = ctx.clock.millis() return Long.MAX_VALUE @@ -257,7 +256,9 @@ public class SimResourceDistributorMaxMin( } override fun onFinish(ctx: SimResourceControllableContext) { - activeCommand = SimResourceCommand.Exit + work = 0.0 + limit = 0.0 + deadline = Long.MAX_VALUE lastCommandTimestamp = ctx.clock.millis() } diff --git a/opendc-simulator/opendc-simulator-resources/src/main/kotlin/org/opendc/simulator/resources/impl/SimResourceContextImpl.kt b/opendc-simulator/opendc-simulator-resources/src/main/kotlin/org/opendc/simulator/resources/impl/SimResourceContextImpl.kt index 5c3f95e8..90c7bc75 100644 --- a/opendc-simulator/opendc-simulator-resources/src/main/kotlin/org/opendc/simulator/resources/impl/SimResourceContextImpl.kt +++ b/opendc-simulator/opendc-simulator-resources/src/main/kotlin/org/opendc/simulator/resources/impl/SimResourceContextImpl.kt @@ -212,11 +212,15 @@ internal class SimResourceContextImpl( // 2. The resource capacity cannot satisfy the demand. // 3. The resource consumer should be interrupted (e.g., someone called .interrupt()) if ((isConsume && remainingWork == 0.0) || _deadline <= timestamp || isInterrupted) { - next(timestamp) + when (val command = consumer.onNext(this)) { + is SimResourceCommand.Idle -> interpretIdle(timestamp, command.deadline) + is SimResourceCommand.Consume -> interpretConsume(timestamp, command.work, command.limit, command.deadline) + is SimResourceCommand.Exit -> interpretExit() + } } else if (isConsume) { - interpret(SimResourceCommand.Consume(remainingWork, _limit, _deadline), timestamp) + interpretConsume(timestamp, remainingWork, _limit, _deadline) } else { - interpret(SimResourceCommand.Idle(_deadline), timestamp) + interpretIdle(timestamp, _deadline) } } } @@ -249,57 +253,50 @@ internal class SimResourceContextImpl( } /** - * Interpret the specified [SimResourceCommand] that was submitted by the resource consumer. + * Interpret the [SimResourceCommand.Consume] command. */ - private fun interpret(command: SimResourceCommand, now: Long): SimResourceState { - return when (command) { - is SimResourceCommand.Idle -> { - val deadline = command.deadline - - require(deadline >= now) { "Deadline already passed" } - - _speed = 0.0 - _work = 0.0 - _limit = 0.0 - _deadline = deadline + private fun interpretConsume(now: Long, work: Double, limit: Double, deadline: Long): SimResourceState { + require(deadline >= now) { "Deadline already passed" } - val timestamp = logic.onIdle(this, deadline) - scheduleUpdate(timestamp) + _speed = min(capacity, limit) + _work = work + _limit = limit + _deadline = deadline - SimResourceState.Active - } - is SimResourceCommand.Consume -> { - val work = command.work - val limit = command.limit - val deadline = command.deadline + val timestamp = logic.onConsume(this, work, limit, deadline) + scheduleUpdate(timestamp) - require(deadline >= now) { "Deadline already passed" } + return SimResourceState.Active + } - _speed = min(capacity, limit) - _work = work - _limit = limit - _deadline = deadline + /** + * Interpret the [SimResourceCommand.Idle] command. + */ + private fun interpretIdle(now: Long, deadline: Long): SimResourceState { + require(deadline >= now) { "Deadline already passed" } - val timestamp = logic.onConsume(this, work, limit, deadline) - scheduleUpdate(timestamp) + _speed = 0.0 + _work = 0.0 + _limit = 0.0 + _deadline = deadline - SimResourceState.Active - } - is SimResourceCommand.Exit -> { - _speed = 0.0 - _work = 0.0 - _limit = 0.0 - _deadline = Long.MAX_VALUE + val timestamp = logic.onIdle(this, deadline) + scheduleUpdate(timestamp) - SimResourceState.Stopped - } - } + return SimResourceState.Active } /** - * Request the workload for more work. + * Interpret the [SimResourceCommand.Exit] command. */ - private fun next(now: Long): SimResourceState = interpret(consumer.onNext(this), now) + private fun interpretExit(): SimResourceState { + _speed = 0.0 + _work = 0.0 + _limit = 0.0 + _deadline = Long.MAX_VALUE + + return SimResourceState.Stopped + } private var _remainingWork: Double = 0.0 private var _remainingWorkFlush: Long = Long.MIN_VALUE -- cgit v1.2.3