Repository navigation
feat: add advanced thread metrics (peak UPS, state transitions, queue depth, memory, jitter) #63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
851bece
ae2ac24
8197824
7dcdf43
a09c321
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,11 +1,14 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package com.bs.threadsimulator.common | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import android.os.Debug | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import kotlinx.coroutines.flow.MutableStateFlow | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import kotlinx.coroutines.flow.StateFlow | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import kotlinx.coroutines.flow.asStateFlow | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.concurrent.ConcurrentHashMap | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.concurrent.atomic.AtomicInteger | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.concurrent.atomic.AtomicLong | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import javax.inject.Inject | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import kotlin.math.sqrt | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Data class representing thread execution metrics. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -17,13 +20,23 @@ import javax.inject.Inject | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @property updateType The type of update being tracked (e.g., "PE", "CurrentPrice", "HighLow") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @property updateCount Total number of updates performed by this thread | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @property avgUpdateTimeMs Average time in milliseconds per update operation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @property peakUpdatesPerSec Highest updates-per-second rate observed for this thread+type | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @property stateTransitions Count of thread state changes (e.g., RUNNABLE↔WAITING) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @property queueDepth Current channel buffer occupancy (shared across threads) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @property threadAllocatedBytes Cumulative bytes allocated by this thread (-1 if unavailable) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @property jitterMs Standard deviation of update interval times in milliseconds | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data class ThreadMetrics( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val threadId: Long, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val threadName: String, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val updateType: String, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val updateCount: Long, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val avgUpdateTimeMs: Long, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val peakUpdatesPerSec: Double = 0.0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val stateTransitions: Int = 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val queueDepth: Int = 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val threadAllocatedBytes: Long = -1L, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val jitterMs: Double = 0.0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -32,6 +45,9 @@ data class ThreadMetrics( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * [ThreadMonitor] records update counts and timings for each thread and update type, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * providing real-time visibility into multi-threaded performance. Useful for debugging | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * threading issues and monitoring performance bottlenecks in concurrent operations. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Advanced metrics include peak updates/sec, thread state transitions, queue depth, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * per-thread memory allocation, and jitter (std-dev of update intervals). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class ThreadMonitor | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Inject | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -46,65 +62,190 @@ class ThreadMonitor | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val metrics: StateFlow<List<ThreadMetrics>> = _metrics.asStateFlow() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // --- Basic tracking --- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private val updateCounts = ConcurrentHashMap<String, AtomicLong>() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private val updateTimes = ConcurrentHashMap<String, AtomicLong>() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private val threadNames = ConcurrentHashMap<Long, String>() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // --- Advanced tracking --- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** Timestamps of each update for peak-UPS and jitter computation. */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private val updateTimestamps = ConcurrentHashMap<String, MutableList<Long>>() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
buddhasaikia marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** Per-thread state transition counter. */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private val stateTransitionCounts = ConcurrentHashMap<Long, AtomicInteger>() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** Previous thread state for detecting transitions. */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private val lastThreadStates = ConcurrentHashMap<Long, Thread.State>() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** Shared channel queue depth counter. */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private val queueDepthCounter = AtomicInteger(0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Increments the queue depth counter. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Should be called after successfully sending an element to the channel. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fun incrementQueueDepth() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| queueDepthCounter.incrementAndGet() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Decrements the queue depth counter. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Should be called when an element is received from the channel. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fun decrementQueueDepth() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| queueDepthCounter.decrementAndGet() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Records an update operation with its execution time. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Thread-safe. Should be called by worker threads to log their update operations. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Synchronized to prevent race conditions with [clearMetrics] operations. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Also tracks thread state transitions, timestamps for peak-UPS/jitter, and | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * per-thread memory allocation. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param updateType The type of update (e.g., "PE", "CurrentPrice", "HighLow") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param updateTimeMs The time taken for this update operation in milliseconds | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Synchronized | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fun recordUpdate( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updateType: String, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updateTimeMs: Long, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val thread = Thread.currentThread() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val key = "${thread.id}_$updateType" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| threadNames.putIfAbsent(thread.id, thread.name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updateCounts.getOrPut(key) { AtomicLong(0) }.incrementAndGet() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updateTimes.getOrPut(key) { AtomicLong(0) }.addAndGet(updateTimeMs) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| synchronized(this) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| threadNames.putIfAbsent(thread.id, thread.name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updateCounts.getOrPut(key) { AtomicLong(0) }.incrementAndGet() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updateTimes.getOrPut(key) { AtomicLong(0) }.addAndGet(updateTimeMs) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Record timestamp for peak-UPS and jitter with bounded 10-second retention window | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val now = System.currentTimeMillis() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val timestamps = updateTimestamps.getOrPut(key) { mutableListOf() } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| timestamps.add(now) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val retentionWindowMs = 10_000L | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val cutoff = now - retentionWindowMs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while (timestamps.isNotEmpty() && timestamps[0] < cutoff) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| timestamps.removeAt(0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Track state transitions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val currentState = thread.state | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val previousState = lastThreadStates.put(thread.id, currentState) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (previousState != null && previousState != currentState) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| stateTransitionCounts.getOrPut(thread.id) { AtomicInteger(0) }.incrementAndGet() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updateMetrics() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private fun updateMetrics() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val currentQueueDepth = queueDepthCounter.get() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val currentMetrics = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updateCounts.keys.map { key -> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val (threadId, updateType) = key.split("_", limit = 2) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val threadIdLong = threadId.toLong() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val count = updateCounts[key]?.get() ?: 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val totalTime = updateTimes[key]?.get() ?: 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val timestamps = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| synchronized(this) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updateTimestamps[key]?.toList() ?: emptyList() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ThreadMetrics( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| threadId = threadIdLong, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| threadName = threadNames[threadIdLong] ?: "Unknown", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updateType = updateType, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updateCount = count, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| avgUpdateTimeMs = if (count > 0) totalTime / count else 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| peakUpdatesPerSec = computePeakUpdatesPerSec(timestamps), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| stateTransitions = stateTransitionCounts[threadIdLong]?.get() ?: 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| queueDepth = currentQueueDepth, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| threadAllocatedBytes = getThreadAllocatedBytes(threadIdLong), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| jitterMs = computeJitter(timestamps), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _metrics.value = currentMetrics | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Computes peak updates per second. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Runs in O(n) time using a sliding window over the monotonically increasing timestamps. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Suppress("ReturnCount") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private fun computePeakUpdatesPerSec(timestamps: List<Long>): Double { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (timestamps.isEmpty()) return 0.0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (timestamps.size == 1) return 1.0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val windowMillis = 1000L | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var peak = 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var start = 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (end in timestamps.indices) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val windowStart = timestamps[end] - windowMillis | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while (start < end && timestamps[start] <= windowStart) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| start++ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val count = end - start + 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (count > peak) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| peak = count | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+179
to
+200
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | |
| private fun computePeakUpdatesPerSec(timestamps: List<Long>): Double { | |
| if (timestamps.size < 2) return timestamps.size.toDouble() | |
| var peak = 0 | |
| for (i in timestamps.indices) { | |
| val windowEnd = timestamps[i] + 1000L | |
| var count = 0 | |
| for (j in i until timestamps.size) { | |
| if (timestamps[j] <= windowEnd) count++ else break | |
| } | |
| if (count > peak) peak = count | |
| } | |
| * | |
| * This implementation runs in O(n) time by using a two-pointer sliding window | |
| * over the (assumed) monotonically increasing list of timestamps. | |
| */ | |
| private fun computePeakUpdatesPerSec(timestamps: List<Long>): Double { | |
| if (timestamps.isEmpty()) return 0.0 | |
| if (timestamps.size == 1) return 1.0 | |
| val windowMillis = 1000L | |
| var peak = 0 | |
| var start = 0 | |
| for (end in timestamps.indices) { | |
| val windowStart = timestamps[end] - windowMillis | |
| // Advance start index while the timestamp is older than the 1-second window | |
| while (start < end && timestamps[start] < windowStart) { | |
| start++ | |
| } | |
| val count = end - start + 1 | |
| if (count > peak) { | |
| peak = count | |
| } | |
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -331,7 +331,7 @@ private fun ThreadMetricsDisplay(threadMetrics: List<ThreadMetrics>) { | |
| modifier = | ||
| Modifier | ||
| .padding(4.dp) | ||
| .width(200.dp), | ||
| .width(220.dp), | ||
| ) { | ||
| Column( | ||
| modifier = Modifier.padding(8.dp), | ||
|
|
@@ -352,6 +352,28 @@ private fun ThreadMetricsDisplay(threadMetrics: List<ThreadMetrics>) { | |
| text = "Avg Time: ${metric.avgUpdateTimeMs}ms", | ||
| style = MaterialTheme.typography.bodySmall, | ||
| ) | ||
| Text( | ||
| text = "Peak UPS: ${"%.1f".format(metric.peakUpdatesPerSec)}", | ||
| style = MaterialTheme.typography.bodySmall, | ||
| ) | ||
| Text( | ||
| text = "State Transitions: ${metric.stateTransitions}", | ||
| style = MaterialTheme.typography.bodySmall, | ||
| ) | ||
| Text( | ||
| text = "Queue Depth: ${metric.queueDepth}", | ||
| style = MaterialTheme.typography.bodySmall, | ||
| ) | ||
| if (metric.threadAllocatedBytes >= 0) { | ||
| Text( | ||
| text = "Memory: ${"%.2f".format(metric.threadAllocatedBytes / 1024.0)} KB", | ||
| style = MaterialTheme.typography.bodySmall, | ||
|
Comment on lines
+367
to
+370
|
||
| ) | ||
| } | ||
| Text( | ||
| text = "Jitter: ${"%.2f".format(metric.jitterMs)}ms", | ||
| style = MaterialTheme.typography.bodySmall, | ||
| ) | ||
| } | ||
| } | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.