Skip to content

Commit e9cfb4a

Browse files
authored
perf: reducing object creation (#119)
1 parent 2a524e3 commit e9cfb4a

5 files changed

Lines changed: 67 additions & 38 deletions

File tree

src/main/kotlin/at/ac/uibk/dps/cirrina/execution/object/Context.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ interface Context : AutoCloseable {
4040

4141
fun getAll(): List<ContextVariable>
4242

43+
fun clear()
44+
4345
companion object {
4446
fun from(description: Map<String, String>?): Context {
4547
val ctx = ContextInMemory()
@@ -50,5 +52,7 @@ interface Context : AutoCloseable {
5052
}
5153
return ctx
5254
}
55+
56+
fun empty() = ContextInMemory()
5357
}
5458
}

src/main/kotlin/at/ac/uibk/dps/cirrina/execution/object/Extent.kt

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
package at.ac.uibk.dps.cirrina.execution.`object`
22

3-
class Extent
4-
private constructor(
5-
private val contexts: Array<Context>,
6-
private val overlay: Map<String, Any?> = emptyMap(),
7-
) {
3+
class Extent private constructor(private val contexts: Array<Context>) {
84
val high: Context? = contexts.lastOrNull()
95

106
fun setOrCreate(name: String, value: Any?): Int =
@@ -16,16 +12,13 @@ private constructor(
1612
?: error("variable '$name' not found in any context")
1713

1814
fun resolve(name: String): Any =
19-
overlay[name]
20-
?: contexts.lastOrNull { it.has(name) }?.get(name)
15+
contexts.lastOrNull { it.has(name) }?.get(name)
2116
?: error("variable '$name' not found in any context")
2217

23-
fun has(name: String): Boolean = name in overlay || contexts.any { it.has(name) }
18+
fun has(name: String): Boolean = contexts.any { it.has(name) }
2419

2520
fun extend(high: Context): Extent = Extent(contexts + high)
2621

27-
fun with(overlay: Map<String, Any?>): Extent = Extent(contexts, overlay)
28-
2922
companion object {
3023
fun empty() = Extent(emptyArray())
3124

src/main/kotlin/at/ac/uibk/dps/cirrina/execution/object/StateMachine.kt

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,22 @@ private val logger = KotlinLogging.logger {}
2121

2222
private const val VAR_PREFIX = "$"
2323

24-
private data class ActiveTransition(val transition: Transition, val isOr: Boolean)
24+
@JvmInline
25+
value class ActiveTransition(private val wrapped: Any) {
26+
val transition: Transition
27+
get() = if (wrapped is OrMarker) wrapped.t else wrapped as Transition
28+
29+
val isOr: Boolean
30+
get() = wrapped is OrMarker
31+
32+
private class OrMarker(val t: Transition)
33+
34+
companion object {
35+
fun standard(t: Transition) = ActiveTransition(t)
36+
37+
fun or(t: Transition) = ActiveTransition(OrMarker(t))
38+
}
39+
}
2540

2641
class StateMachine
2742
@AssistedInject
@@ -80,15 +95,19 @@ internal constructor(
8095

8196
override val extent: Extent
8297

98+
private val eventExtent: Extent
99+
83100
private val eventTimer: Timer = runtime.metricRegistry.timer("event.latency")
84101

85102
init {
86-
val transientContext = Context.from(specification.transient)
87103
val instanceData = Context.from(instance.data).getAll()
104+
val transientContext = Context.from(specification.transient)
105+
val eventContext = Context.empty()
88106

89107
instanceData.forEach { transientContext.create(it.name, it.value) }
90108

91109
extent = (parent?.extent ?: runtime.extent).extend(transientContext)
110+
eventExtent = extent.extend(eventContext)
92111
}
93112

94113
fun start(): Job {
@@ -149,12 +168,18 @@ internal constructor(
149168

150169
if (candidates.isEmpty()) return null
151170

152-
val evalExtent =
153-
activeState!!.extent.with(event.data.associate { VAR_PREFIX + it.name to it.value })
171+
val eventContext = eventExtent.high!!
154172

155-
return trySelect(candidates, evalExtent)?.also {
156-
event.data.forEach { d -> extent.setOrCreate(VAR_PREFIX + d.name, d.value) }
157-
}
173+
event.data.forEach { eventContext.create(VAR_PREFIX + it.name, it.value) }
174+
175+
val selected =
176+
trySelect(candidates, eventExtent)?.also {
177+
event.data.forEach { d -> extent.setOrCreate(VAR_PREFIX + d.name, d.value) }
178+
}
179+
180+
eventContext.clear()
181+
182+
return selected
158183
}
159184

160185
private fun Event.isValid(): Boolean {
@@ -188,21 +213,13 @@ internal constructor(
188213
}
189214

190215
private fun trySelect(transitions: List<Transition>, evalExtent: Extent): ActiveTransition? {
191-
val selected =
192-
transitions.mapNotNull { transition ->
193-
val spec = transition.specification
194-
when {
195-
spec.evaluate(evalExtent) -> ActiveTransition(transition, isOr = false)
196-
spec.or != null -> ActiveTransition(transition, isOr = true)
197-
else -> null
198-
}
199-
}
200-
201-
return when (selected.size) {
202-
0 -> null
203-
1 -> selected.first()
204-
else -> error("non-determinism detected with selected transitions '$selected'")
216+
for (i in transitions.indices) {
217+
val transition = transitions[i]
218+
val spec = transition.specification
219+
if (spec.evaluate(evalExtent)) return ActiveTransition.standard(transition)
220+
if (spec.or != null) return ActiveTransition.or(transition)
205221
}
222+
return null
206223
}
207224

208225
private fun doEnter(state: State): ActiveTransition? {
@@ -234,13 +251,22 @@ internal constructor(
234251
private tailrec fun execute(actions: List<Action>, scope: Scope) {
235252
if (actions.isEmpty()) return
236253

237-
val next =
238-
actions.flatMap { action ->
239-
if (action is TimeoutResetAction) stopTimeout(action.action)
240-
actionExecutor.execute(action, scope)
254+
val nextActions = mutableListOf<Action>()
255+
256+
for (i in actions.indices) {
257+
val action = actions[i]
258+
259+
if (action is TimeoutResetAction) {
260+
stopTimeout(action.action)
241261
}
242262

243-
execute(next, scope)
263+
val result = actionExecutor.execute(action, scope)
264+
if (result.isNotEmpty()) {
265+
nextActions.addAll(result)
266+
}
267+
}
268+
269+
execute(nextActions, scope)
244270
}
245271

246272
private fun startTimeout(timeout: TimeoutAction) {

src/main/kotlin/at/ac/uibk/dps/cirrina/execution/provider/ContextEtcd.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ class ContextEtcd(endpoints: List<String>) : Context {
8383
}
8484
}
8585

86+
override fun clear() {}
87+
8688
private fun <T> CompletableFuture<T>.sync(): T = this.get()
8789

8890
private fun String.toByteSequence() = ByteSequence.from(this, StandardCharsets.UTF_8)

src/main/kotlin/at/ac/uibk/dps/cirrina/execution/provider/ContextInMemory.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import at.ac.uibk.dps.cirrina.execution.`object`.Context
44
import at.ac.uibk.dps.cirrina.execution.`object`.ContextVariable
55
import java.util.concurrent.ConcurrentHashMap
66

7-
class ContextInMemory() : Context {
7+
class ContextInMemory : Context {
88
private val values = ConcurrentHashMap<String, Any?>()
99

1010
override fun has(name: String): Boolean = values.containsKey(name)
@@ -40,7 +40,11 @@ class ContextInMemory() : Context {
4040
}
4141

4242
override fun getAll(): List<ContextVariable> =
43-
values.map { (key, value) -> ContextVariable.Companion.eager(key, value) }
43+
values.map { (key, value) -> ContextVariable.eager(key, value) }
44+
45+
override fun clear() {
46+
values.clear()
47+
}
4448

4549
override fun close() {}
4650

0 commit comments

Comments
 (0)