I wish to perform the following function on large arrays of UInt32s in Swift:
for i in 0..<array.count {
array[i] = array[i] % UInt32(i + 1)
As each element is changed independently, this suggests that this function could be parallelised. So I started looking into GCD methods.
My first attempt (in a Playground) used dispatch_async:
import Cocoa
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
let myQueue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)
func moduloFunc(inout thisArray: [UInt32]) {
let len = thisArray.count
for i in 0..<len {
dispatch_async(myQueue) {
thisArray[i] = thisArray[i] % UInt32(i + 1)
}
}
}
// test1 is a [UInt32] array with 100,000+ elements
let testLen = test1.count
moduloFunc(&test1)
print((test1.filter({$0 > UInt32(testLen)})).count)
// this prints how many elements remain unchanged when the result of the function is passed on
When run, moduloFunc works through the array twice as fast as the regular for-loop — but, being within an async queue, it passes the array on to the print function before it's finished; consequently, the print shows most of the array unmodified. Most of what I've read about GCD says I should include a 'completion handler' function within moduloFunc, but I'm unclear as to how this would be implemented in this case (and many of the examples online are based on Objective-C).
If dispatch_async is replaced with dispatch_sync, all elements are successfully modified, but the function runs only about 20% faster than the regular for-loop.
Following on from this post and this link about concurrent loops, I tried using dispatch_apply instead:
let myQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT)
func moduloFunc(inout thisArray: [UInt32]) {
let len = thisArray.count
dispatch_apply(len, myQueue) {i in
thisArray[i] = thisArray[i] % UInt32(i + 1)
}
}
This worked better: the function went through the loop as fast as the dispatch_async example and most of the elements were modified — but still not all of them. (A few hundred remained unchanged, generally.) Changing DISPATCH_QUEUE_CONCURRENT to DISPATCH_QUEUE_SERIAL produced the same result as the dispatch_sync example.
I sense I'm missing something obvious here. Or am I going about this completely the wrong way?
Running on a 2008 8-core Mac Pro, OS X 10.11.6, XCode 7.3.1, Swift 2.2