Skip to content

Instantly share code, notes, and snippets.

@robertmryan
Last active August 31, 2024 18:16
Show Gist options
  • Save robertmryan/80572be8d62bf2ccb41733cf141fd0a0 to your computer and use it in GitHub Desktop.
Save robertmryan/80572be8d62bf2ccb41733cf141fd0a0 to your computer and use it in GitHub Desktop.
Chunking of GCD’s concurrentPerform
import Foundation
extension DispatchQueue {
/// Chunked concurrentPerform
///
/// - Parameters:
///
/// - iterations: How many total iterations.
///
/// - chunks: How many chunks into which these iterations will be divided. This is optional and will default to
/// `activeProcessorCount`. If the work is largely uniform, you can safely omit this parameter and the
/// work will evenly distributed amongst the CPU cores.
///
/// If different chunks are likely to take significantly different amounts of time,
/// you may want to increase this value above the processor count to avoid blocking the whole process
/// for slowest chunk and afford the opportunity for threads processing faster chunks to handle more than one.
///
/// But, be careful to not increase this value too high, as each dispatched chunk entails a modest amount of overhead.
/// You may want to empirically test different chunk sizes (vs the default value) for your particular use-case.
///
/// - chunk: Closure to be called for each chunk.
static func chunkedConcurrentPerform(iterations: Int, chunks: Int? = nil, chunk: @Sendable (Range<Int>) -> Void) {
let chunks = min(iterations, chunks ?? ProcessInfo.processInfo.activeProcessorCount)
let (baseChunkSize, remainder) = iterations.quotientAndRemainder(dividingBy: chunks)
concurrentPerform(iterations: chunks) { chunkIndex in
let start = chunkIndex * baseChunkSize + min(chunkIndex, remainder)
let end = start + baseChunkSize + (chunkIndex < remainder ? 1 : 0)
chunk(start ..< end)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment