Thursday, April 15, 2021

Collecting concurrentPerform(iterations:) Results in a Swift Array

David Smith (archive):

Useful pattern for aggregating the results of parallel work in Swift:

let result = Array(unsafeUninitializedCapacity: count) { (buffer) in
  DispatchQueue.concurrentPerform(iterations: count) { (idx) in
    buffer[idx] = processItem(idx)
  }
}

Avoids making an extra buffer copy

If you make the Array up front and try to operate directly on it instead of the UnsafeMutableBufferPointer in that initializer, each thread will get its own copy due to copy-on-write, which generally is not what you wanted.

David Smith (archive):

I would not trust it [with small array elements]. Aligned word-sized non-float things are your friends when dealing with concurrency. If you’re not sure, try TSAN, and consider just using a lock.

Previously:

Update (2026-02-20): For objects and other types that use ARC, directly assigning into the buffer as shown above will crash in swift_unknownObjectRelease() as Swift tries to release the garbage memory that was in that position in the buffer. You should instead use:

buffer.initializeElement(at: idx, to: processItem(idx))

The original tweets have been deleted, so perhaps I’m missing something from the context, but the part about each thread getting its own copy if you make an Array up front does not sound correct to me.

Comments RSS · Twitter

Leave a Comment