I finally had a reason to use DispatchQueue.concurrentPerform
to spread some chunks of work across multiple cores. There’s something very satisfying about filling up CPU cores with work!
So with a bit of help from StackOverflow I came up with this Array extension for using map and for-each with a simple API.
The functions themselves are synchronous, but DispatchQueue.concurrentPerform
does its magic and runs the passed in closures across the cores.
Here’s the code. Got a suggestion? Let me know on the Gist.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public extension Array { | |
/// Synchronous | |
func concurrentMap<T>(transform: @escaping (Element) -> T) -> [T] { | |
let result = UnsafeMutablePointer<T>.allocate(capacity: count) | |
DispatchQueue.concurrentPerform(iterations: count) { i in | |
result.advanced(by: i).initialize(to: transform(self[i])) | |
} | |
let finalResult = Array<T>(UnsafeBufferPointer(start: result, count: count)) | |
result.deallocate() | |
return finalResult | |
} | |
/// Synchronous | |
func concurrentForEach(action: @escaping (Element) -> Void) { | |
_ = concurrentMap { _ = action($0) } | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Works just like regular `map` and `forEach`. | |
// This is a simple example to show API usage; don't actually use this for such tiny amounts of work. | |
let things = [1, 2, 3, 4] | |
// 1 | |
// 2 | |
// 3 | |
// 4 | |
things.concurrentForEach { | |
print($0) | |
} | |
let multipliedByTwo = things.concurrentMap { $0 * 2 } // [2, 4, 6, 8] |
Array+Concurrency.swift
public extension Array { /// Synchronous func concurrentMap(transform: @escaping (Element) -> T) -> [T] { let result = UnsafeMutablePointer .allocate(capacity: count) DispatchQueue.concurrentPerform(iterations: count) { i in result.advanced(by: i).initialize(to: transform(self[i])) } let finalResult = Array (UnsafeBufferPointer(start: result, count: count)) result.deallocate() return finalResult } /// Synchronous func concurrentForEach(action: @escaping (Element) -> Void) { _ = concurrentMap { _ = action($0) } } }
Usage
// Works just like regular `map` and `forEach`. let things = [1, 2, 3, 4] // 1 // 2 // 3 // 4 things.concurrentForEach { print($0) } let multipliedByTwo = things.concurrentMap { $0 * 2 } // [2, 4, 6, 8]