diff --git a/.jules/bolt.md b/.jules/bolt.md index ed3edb1..18c5dde 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -130,3 +130,6 @@ If your candidate site already uses any of these, the issue is fixed. ## 2025-05-15 - Array Materialization via sorted() on Lazy Sequences **Learning:** `LazyFilterSequence` does not have a custom `sorted()` implementation. Calling `sorted()` on a `LazyFilterSequence` relies on the default `Sequence` extension, which completely consumes the sequence and materializes it into an array immediately. While chaining `.lazy.filter` before `.sorted()` avoids allocating a separate intermediate array just for the filtered results, `sorted()` itself forces array materialization regardless. Adding `.lazy` specifically *just* before `.sorted()` creates confusion because it suggests the sort operation itself is lazy (which is impossible, sorting requires the whole collection). The codebase considers `.lazy.filter.sorted` an anti-pattern. **Action:** Do not propose adding `.lazy` before `.sorted()`. Ensure `.lazy` is only proposed when preceding operations that natively support lazy consumption like `.reduce`, `.count`, `.first`, `.contains`, or lazy `prefix`. +## 2025-05-16 - Refactoring Filter Methods for Iteration Optimization +**Learning:** Functions that encapsulate `.filter` logic and return a filtered array force intermediate array allocation when called within a loop, especially for potentially large datasets like process lists (~400-600 items). The `for ... where` idiom cannot be applied when the condition logic is hidden inside a method that returns the already filtered array. +**Action:** When a method exists purely to filter an array for subsequent iteration, refactor the method into a `static func` (or regular function) that returns a `Bool` evaluating a single element. This allows the caller to use an efficient `for element in collection where isCondition(element)` pattern, avoiding intermediate memory allocations. diff --git a/Sources/Cacheout/Memory/PredictiveEngine.swift b/Sources/Cacheout/Memory/PredictiveEngine.swift index 725b3cd..e1ba422 100644 --- a/Sources/Cacheout/Memory/PredictiveEngine.swift +++ b/Sources/Cacheout/Memory/PredictiveEngine.swift @@ -15,7 +15,7 @@ /// engine.recordAvailableMB(mb, at: timestamp) /// // Query predictions: /// let tte = engine.predictTimeToExhaustion() -/// let growers = await engine.detectHighGrowthProcesses() +/// // Use PredictiveEngine.isHighGrowthProcess(entry) to filter growers /// ``` import CacheoutShared @@ -188,23 +188,21 @@ actor PredictiveEngine { // MARK: - High-Growth Process Detection - /// Detect processes that have grown to and remain at their lifetime peak. + /// Check if a process has grown to and remains at its lifetime peak. /// - /// Filters for processes where: + /// Matches processes where: /// - `leakIndicator < 1.05` (within 5% of lifetime peak) /// - `physFootprint > 500MB` (significant memory consumer) /// /// This is NOT leak detection -- it identifies processes with sustained growth /// that are currently at or near their maximum footprint. /// - /// - Parameter processes: Process entries to evaluate. - /// - Returns: Processes matching high-growth criteria. - func detectHighGrowthProcesses(from processes: [ProcessEntryDTO]) -> [ProcessEntryDTO] { - processes.filter { entry in - entry.leakIndicator < Self.highGrowthMaxLeakIndicator - && entry.leakIndicator > 0 // Exclude zero (no footprint data) - && entry.physFootprint > Self.highGrowthMinFootprint - } + /// - Parameter entry: The process entry to evaluate. + /// - Returns: True if the process matches high-growth criteria. + static func isHighGrowthProcess(_ entry: ProcessEntryDTO) -> Bool { + entry.leakIndicator < Self.highGrowthMaxLeakIndicator + && entry.leakIndicator > 0 // Exclude zero (no footprint data) + && entry.physFootprint > Self.highGrowthMinFootprint } // MARK: - Process Scan Cache diff --git a/Sources/Cacheout/Memory/RecommendationEngine.swift b/Sources/Cacheout/Memory/RecommendationEngine.swift index f5d5300..2612c2f 100644 --- a/Sources/Cacheout/Memory/RecommendationEngine.swift +++ b/Sources/Cacheout/Memory/RecommendationEngine.swift @@ -189,8 +189,8 @@ struct RecommendationEngine { } // high_growth_process - let highGrowth = await predictiveEngine.detectHighGrowthProcesses(from: scanResult.processes) - for proc in highGrowth { + // ⚡ Bolt: Use for-where clause to prevent intermediate array allocation + for proc in scanResult.processes where PredictiveEngine.isHighGrowthProcess(proc) { let footprintGB = Double(proc.physFootprint) / (1024 * 1024 * 1024) recommendations.append(Recommendation( type: .highGrowthProcess,