diff --git a/.jules/bolt.md b/.jules/bolt.md index 8f342ab..851d647 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -1,5 +1,3 @@ -## 2024-05-24 - Started - -## 2024-05-24 - O(N) Iteration in Cache Size Calculation -**Learning:** Calculating dynamic capacity dynamically via `store.values.sumOf { it.sizeBytes }` effectively incurs an O(N) cost on every `put` because it runs when `trimToCapacity()` calls `dynamicCapacity`. When cache sizes get large, this causes O(N²) overall insertion performance and UI stutters. -**Action:** Use an `AtomicLong` (O(1)) to track the running sum of cache size instead of dynamically iterating through the entire collection. +## 2024-06-25 - Avoid Eager Collection Operations in Tight Loops +**Learning:** Using `store.filter { ... }.minByOrNull { ... }` inside a `while` loop for cache eviction creates temporary `LinkedHashMap` instances on every iteration, leading to excessive GC pressure and O(N) memory allocations per loop cycle. +**Action:** Replace functional pipelines that allocate intermediate collections with explicit, allocation-free `for` loops when iterating over Maps or Collections within performance-critical tight loops (like cache eviction). diff --git a/app/src/main/java/com/example/reddittube/ui/main/components/MinimalButton.kt b/app/src/main/java/com/example/reddittube/ui/main/components/MinimalButton.kt index 056265a..8879150 100644 --- a/app/src/main/java/com/example/reddittube/ui/main/components/MinimalButton.kt +++ b/app/src/main/java/com/example/reddittube/ui/main/components/MinimalButton.kt @@ -16,7 +16,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.semantics.Role import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.semantics.Role import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp diff --git a/app/src/main/java/com/example/reddittube/util/AdaptiveCacheEngine.kt b/app/src/main/java/com/example/reddittube/util/AdaptiveCacheEngine.kt index 177860c..c52aff0 100644 --- a/app/src/main/java/com/example/reddittube/util/AdaptiveCacheEngine.kt +++ b/app/src/main/java/com/example/reddittube/util/AdaptiveCacheEngine.kt @@ -97,9 +97,24 @@ class AdaptiveCacheEngine( val target = dynamicCapacity val now = SystemClock.elapsedRealtime() while (store.size > target) { - val candidate = store.filter { (k, n) -> - !isProtectedKey(k) || (now - n.lastAccessedMs > protectedTtlMs) - }.minByOrNull { computeScore(it.value, now) } ?: break + // ⚡ Bolt: Removed eager store.filter Map creation to prevent heavy GC pressure. + // Using an explicit loop avoids allocating temporary collections on every eviction iteration. + var minEntry: Map.Entry>? = null + var minScore = Double.MAX_VALUE + + for (entry in store.entries) { + val k = entry.key + val n = entry.value + if (!isProtectedKey(k) || (now - n.lastAccessedMs > protectedTtlMs)) { + val score = computeScore(n, now) + if (score < minScore) { + minScore = score + minEntry = entry + } + } + } + + val candidate = minEntry ?: break val removedNode = store.remove(candidate.key) if (removedNode != null) { currentSizeBytes.addAndGet(-removedNode.sizeBytes)