summaryrefslogtreecommitdiff
path: root/opendc-compute/opendc-compute-simulator/src/main
diff options
context:
space:
mode:
authorFabian Mastenbroek <mail.fabianm@gmail.com>2021-10-02 13:08:06 +0200
committerFabian Mastenbroek <mail.fabianm@gmail.com>2021-10-03 17:17:41 +0200
commit012fe8fa9be1676b8eef0cce795738a00c4260c0 (patch)
tree34238f56af20f0eb697f25ad5a700bab7fa4d6fb /opendc-compute/opendc-compute-simulator/src/main
parent081221684fb826ab5a00c1d8cc5a9886b9e2203c (diff)
perf(compute): Optimize telemetry collection
This change optimizes the telemetry collection in the SimHost class. Previously, there was significant overhead in collecting the metrics of this and associated classes due large `Attributes` object that did not cache accesses to `hashCode()`. We now wrap this object and manually cache the hash code.
Diffstat (limited to 'opendc-compute/opendc-compute-simulator/src/main')
-rw-r--r--opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt45
-rw-r--r--opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/Guest.kt97
2 files changed, 102 insertions, 40 deletions
diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt
index 4b96872b..b9d02185 100644
--- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt
+++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt
@@ -109,6 +109,7 @@ public class SimHost(
* The virtual machines running on the hypervisor.
*/
private val guests = HashMap<Server, Guest>()
+ private val _guests = mutableListOf<Guest>()
override val state: HostState
get() = _state
@@ -199,7 +200,7 @@ public class SimHost(
require(canFit(key)) { "Server does not fit" }
val machine = hypervisor.createMachine(key.flavor.toMachineModel(), key.name)
- Guest(
+ val newGuest = Guest(
scope.coroutineContext,
clock,
this,
@@ -208,6 +209,9 @@ public class SimHost(
server,
machine
)
+
+ _guests.add(newGuest)
+ newGuest
}
if (start) {
@@ -231,7 +235,7 @@ public class SimHost(
override suspend fun delete(server: Server) {
val guest = guests[server] ?: return
- guest.terminate()
+ guest.delete()
}
override fun addListener(listener: HostListener) {
@@ -253,7 +257,7 @@ public class SimHost(
public suspend fun fail() {
reset()
- for (guest in guests.values) {
+ for (guest in _guests) {
guest.fail()
}
}
@@ -266,7 +270,7 @@ public class SimHost(
// Wait for the hypervisor to launch before recovering the guests
yield()
- for (guest in guests.values) {
+ for (guest in _guests) {
guest.recover()
}
}
@@ -357,11 +361,17 @@ public class SimHost(
var error = 0L
var invalid = 0L
- for ((_, guest) in guests) {
+ val guests = _guests.listIterator()
+ for (guest in guests) {
when (guest.state) {
ServerState.TERMINATED -> terminated++
ServerState.RUNNING -> running++
ServerState.ERROR -> error++
+ ServerState.DELETED -> {
+ // Remove guests that have been deleted
+ this.guests.remove(guest.server)
+ guests.remove()
+ }
else -> invalid++
}
}
@@ -380,8 +390,9 @@ public class SimHost(
private fun collectCpuLimit(result: ObservableDoubleMeasurement) {
result.observe(_cpuLimit)
- for (guest in guests.values) {
- guest.collectCpuLimit(result)
+ val guests = _guests
+ for (i in guests.indices) {
+ guests[i].collectCpuLimit(result)
}
}
@@ -401,8 +412,9 @@ public class SimHost(
result.observe(counters.cpuStealTime / 1000L, _stealState)
result.observe(counters.cpuLostTime / 1000L, _lostState)
- for (guest in guests.values) {
- guest.collectCpuTime(result)
+ val guests = _guests
+ for (i in guests.indices) {
+ guests[i].collectCpuTime(result)
}
}
@@ -423,8 +435,9 @@ public class SimHost(
_downtime += duration
}
- for (guest in guests.values) {
- guest.updateUptime(duration)
+ val guests = _guests
+ for (i in guests.indices) {
+ guests[i].updateUptime(duration)
}
}
@@ -442,8 +455,9 @@ public class SimHost(
result.observe(_uptime, _upState)
result.observe(_downtime, _downState)
- for (guest in guests.values) {
- guest.collectUptime(result)
+ val guests = _guests
+ for (i in guests.indices) {
+ guests[i].collectUptime(result)
}
}
@@ -457,8 +471,9 @@ public class SimHost(
result.observe(_bootTime)
}
- for (guest in guests.values) {
- guest.collectBootTime(result)
+ val guests = _guests
+ for (i in guests.indices) {
+ guests[i].collectBootTime(result)
}
}
}
diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/Guest.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/Guest.kt
index 3ac165c8..5ea1860d 100644
--- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/Guest.kt
+++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/Guest.kt
@@ -24,6 +24,7 @@ package org.opendc.compute.simulator.internal
import io.opentelemetry.api.common.AttributeKey
import io.opentelemetry.api.common.Attributes
+import io.opentelemetry.api.common.AttributesBuilder
import io.opentelemetry.api.metrics.ObservableDoubleMeasurement
import io.opentelemetry.api.metrics.ObservableLongMeasurement
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes
@@ -71,17 +72,7 @@ internal class Guest(
/**
* The attributes of the guest.
*/
- val attributes: Attributes = Attributes.builder()
- .put(ResourceAttributes.HOST_NAME, server.name)
- .put(ResourceAttributes.HOST_ID, server.uid.toString())
- .put(ResourceAttributes.HOST_TYPE, server.flavor.name)
- .put(AttributeKey.longKey("host.num_cpus"), server.flavor.cpuCount.toLong())
- .put(AttributeKey.longKey("host.mem_capacity"), server.flavor.memorySize)
- .put(AttributeKey.stringArrayKey("host.labels"), server.labels.map { (k, v) -> "$k:$v" })
- .put(ResourceAttributes.HOST_ARCH, ResourceAttributes.HostArchValues.AMD64)
- .put(ResourceAttributes.HOST_IMAGE_NAME, server.image.name)
- .put(ResourceAttributes.HOST_IMAGE_ID, server.image.uid.toString())
- .build()
+ val attributes: Attributes = GuestAttributes(this)
/**
* Start the guest.
@@ -114,12 +105,12 @@ internal class Guest(
}
/**
- * Terminate the guest.
+ * Delete the guest.
*
* This operation will stop the guest if it is running on the host and remove all resources associated with the
* guest.
*/
- suspend fun terminate() {
+ suspend fun delete() {
stop()
state = ServerState.DELETED
@@ -224,12 +215,10 @@ internal class Guest(
private var _uptime = 0L
private var _downtime = 0L
- private val _upState = Attributes.builder()
- .putAll(attributes)
+ private val _upState = attributes.toBuilder()
.put(STATE_KEY, "up")
.build()
- private val _downState = Attributes.builder()
- .putAll(attributes)
+ private val _downState = attributes.toBuilder()
.put(STATE_KEY, "down")
.build()
@@ -263,20 +252,16 @@ internal class Guest(
}
}
- private val _activeState = Attributes.builder()
- .putAll(attributes)
+ private val _activeState = attributes.toBuilder()
.put(STATE_KEY, "active")
.build()
- private val _stealState = Attributes.builder()
- .putAll(attributes)
+ private val _stealState = attributes.toBuilder()
.put(STATE_KEY, "steal")
.build()
- private val _lostState = Attributes.builder()
- .putAll(attributes)
+ private val _lostState = attributes.toBuilder()
.put(STATE_KEY, "lost")
.build()
- private val _idleState = Attributes.builder()
- .putAll(attributes)
+ private val _idleState = attributes.toBuilder()
.put(STATE_KEY, "idle")
.build()
@@ -300,4 +285,66 @@ internal class Guest(
fun collectCpuLimit(result: ObservableDoubleMeasurement) {
result.observe(_cpuLimit, attributes)
}
+
+ /**
+ * An optimized [Attributes] implementation.
+ */
+ private class GuestAttributes(private val uid: String, private val attributes: Attributes) : Attributes by attributes {
+ /**
+ * Construct a [GuestAttributes] instance from a [Guest].
+ */
+ constructor(guest: Guest) : this(
+ guest.server.uid.toString(),
+ Attributes.builder()
+ .put(ResourceAttributes.HOST_NAME, guest.server.name)
+ .put(ResourceAttributes.HOST_ID, guest.server.uid.toString())
+ .put(ResourceAttributes.HOST_TYPE, guest.server.flavor.name)
+ .put(AttributeKey.longKey("host.num_cpus"), guest.server.flavor.cpuCount.toLong())
+ .put(AttributeKey.longKey("host.mem_capacity"), guest.server.flavor.memorySize)
+ .put(AttributeKey.stringArrayKey("host.labels"), guest.server.labels.map { (k, v) -> "$k:$v" })
+ .put(ResourceAttributes.HOST_ARCH, ResourceAttributes.HostArchValues.AMD64)
+ .put(ResourceAttributes.HOST_IMAGE_NAME, guest.server.image.name)
+ .put(ResourceAttributes.HOST_IMAGE_ID, guest.server.image.uid.toString())
+ .build()
+ )
+
+ override fun <T : Any?> get(key: AttributeKey<T>): T? {
+ // Optimize access to the HOST_ID key which is accessed quite often
+ if (key == ResourceAttributes.HOST_ID) {
+ @Suppress("UNCHECKED_CAST")
+ return uid as T?
+ }
+ return attributes.get(key)
+ }
+
+ override fun toBuilder(): AttributesBuilder {
+ val delegate = attributes.toBuilder()
+ return object : AttributesBuilder {
+
+ override fun putAll(attributes: Attributes): AttributesBuilder {
+ delegate.putAll(attributes)
+ return this
+ }
+
+ override fun <T : Any?> put(key: AttributeKey<Long>, value: Int): AttributesBuilder {
+ delegate.put<T>(key, value)
+ return this
+ }
+
+ override fun <T : Any?> put(key: AttributeKey<T>, value: T): AttributesBuilder {
+ delegate.put(key, value)
+ return this
+ }
+
+ override fun build(): Attributes = GuestAttributes(uid, delegate.build())
+ }
+ }
+
+ override fun equals(other: Any?): Boolean = attributes == other
+
+ // Cache hash code
+ private val _hash = attributes.hashCode()
+
+ override fun hashCode(): Int = _hash
+ }
}