Categories
Dev

Multi-core array operations in Swift

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.

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) }
}
}
// 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]