Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@
## 2024-05-25 - Efficient Dictionary Initialization
**Learning:** Building a dictionary via a standard `for` loop by inserting elements one by one can cause unnecessary overhead due to repeated mutations. `reduce(into: [:])` is highly optimized in Swift to build collections without creating intermediate copies.
**Action:** Use `reduce(into: [:])` when constructing a dictionary from an array, particularly when uniqueness checks or transformations are required.
## 2024-05-20 - Parallelizing bulk I/O operations with TaskGroup
**Learning:** To optimize bulk I/O operations (e.g., FileManager.removeItem loops) in Swift, parallelizing them using Task.detached is not enough as it does NOT escape the cooperative thread pool.
**Action:** Use withThrowingTaskGroup combined with a sliding window iterator and to prevent thread pool exhaustion/deadlock, wrap synchronous blocking calls in withCheckedThrowingContinuation and explicitly dispatch them to a background GCD queue (e.g., DispatchQueue.global(qos: .userInitiated).async).
49 changes: 44 additions & 5 deletions Sources/Cacheout/Cleaner/CacheCleaner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ actor CacheCleaner {
if moveToTrash {
try await trashDirectory(url)
} else {
try removeContents(of: url)
try await removeContents(of: url)
}
categoryFreed += result.sizeBytes
} catch {
Expand Down Expand Up @@ -133,12 +133,51 @@ actor CacheCleaner {
}
}

private func removeContents(of url: URL) throws {
let contents = try fileManager.contentsOfDirectory(
private func removeContents(of url: URL) async throws {
let currentFileManager = self.fileManager
let contents = try currentFileManager.contentsOfDirectory(
at: url, includingPropertiesForKeys: nil
)
for item in contents {
try fileManager.removeItem(at: item)
// ⚑ Bolt Optimization: Parallelize bulk I/O operations (FileManager.removeItem).
// Uses a sliding window TaskGroup to delete up to 8 files concurrently.
// Synchronous blocking calls are explicitly dispatched to a background GCD queue
// to prevent thread pool exhaustion/deadlock.
// Expected Impact: Significantly faster deletion of large cache directories with many files.
try await withThrowingTaskGroup(of: Void.self) { group in
var iterator = contents.makeIterator()
let maxConcurrency = 8
for _ in 0..<maxConcurrency {
if let item = iterator.next() {
group.addTask {
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
DispatchQueue.global(qos: .userInitiated).async {
do {
try currentFileManager.removeItem(at: item)
continuation.resume()
} catch {
continuation.resume(throwing: error)
}
}
}
}
}
}
for try await _ in group {
if let item = iterator.next() {
group.addTask {
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
DispatchQueue.global(qos: .userInitiated).async {
do {
try currentFileManager.removeItem(at: item)
continuation.resume()
} catch {
continuation.resume(throwing: error)
}
}
}
}
}
}
}
}

Expand Down
Loading