diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..d44fbbd --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2025-04-12 - TaskGroup Sliding Window Concurrency Optimization +**Learning:** When using Swift's `withTaskGroup` for high-volume concurrent processing (e.g., scanning hundreds of PIDs), grouping tasks into static chunks and waiting for the chunk to finish causes tail latency. The entire chunk waits for the slowest task to complete, temporarily dropping concurrency to 1. +**Action:** Always use a sliding window approach with an iterator (e.g., `makeIterator()`). Seed the group up to the `maxConcurrency` limit, and within the `for await result in group` loop, immediately add a new task using `iterator.next()`. This ensures the worker pool stays consistently saturated at max concurrency. \ No newline at end of file diff --git a/Sources/Cacheout/Memory/ProcessMemoryScanner.swift b/Sources/Cacheout/Memory/ProcessMemoryScanner.swift index 3f8e728..0732586 100644 --- a/Sources/Cacheout/Memory/ProcessMemoryScanner.swift +++ b/Sources/Cacheout/Memory/ProcessMemoryScanner.swift @@ -97,29 +97,37 @@ actor ProcessMemoryScanner { /// /// Returns the collected entries and the count of EPERM failures. private func scanPIDs(_ pids: [pid_t]) async -> (entries: [ProcessEntryDTO], epermCount: Int) { - // Chunk PIDs to cap concurrency at maxConcurrency. - let chunks = stride(from: 0, to: pids.count, by: maxConcurrency).map { - Array(pids[$0..