Il modo più semplice per eseguire un ciclo di calcoli in parallelo è concurrentPerform
(precedentemente chiamato dispatch_apply
, vedi Performing Loop Iterations Concurrently nella Guida di programmazione Concurrency). Ma no, non esiste una resa map
che faccia questo per te. Devi farlo da solo.
Ad esempio, si potrebbe scrivere una proroga per eseguire le attività concomitanti:
extension Array {
public func concurrentMap<T>(_ transform: (Element) -> T) -> [T] {
var results = [Int: T]()
let queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".sync", attributes: .concurrent)
DispatchQueue.concurrentPerform(iterations: count) { index in
let result = transform(self[index])
queue.async { results[index] = result }
}
return queue.sync(flags: .barrier) {
(0 ..< results.count).map { results[$0]! }
}
}
}
Nota:
Questo bloccherà il thread si chiama da (proprio come il non-concorrente map
will), quindi assicurati di inviarlo a una coda in background.
Uno ha bisogno di garantire che ci sia abbastanza lavoro su ogni thread per giustificare il sovraccarico inerente della gestione di tutti questi thread. (Ad esempio, una semplice chiamata xor per ciclo non è sufficiente, e scoprirai che è in realtà più lenta della resa non simultanea.) In questi casi, assicurati di avanzare (vedi Improving Loop Code che bilancia la quantità di lavoro per blocco simultaneo) . Ad esempio, anziché eseguire iterazioni di 5000 un'operazione estremamente semplice, eseguire 10 iterazioni di 500 operazioni per ciclo. Potrebbe essere necessario sperimentare con adeguati valori di passo.
Mentre ho il sospetto che non è necessario questa discussione, per i lettori non hanno familiarità con concurrentPerform
(precedentemente noto come dispatch_apply
), ti illustrano il suo utilizzo qui di seguito. Per una discussione più completa sull'argomento, fare riferimento ai collegamenti sopra.
Per esempio, prendiamo in considerazione qualcosa di molto più complicato di una semplice xor
(perché con qualcosa di così semplice, il sovraccarico supera qualsiasi prestazione maturata), come ad esempio un'implementazione di Fibonacci ingenua:
func fibonacci(_ n: Int) -> Int {
if n == 0 || n == 1 {
return n
}
return fibonacci(n - 1) + fibonacci(n - 2)
}
Se si ha avuto un array
di Int
valori per i quali si voleva calcolare, piuttosto che:
let results = array.map { fibonacci($0) }
si potrebbe:
012.351.641.061.
var results = [Int](count: array.count, repeatedValue: 0)
DispatchQueue.concurrentPerform(iterations: array.count) { index in
let result = self.fibonacci(array[index])
synchronize.update { results[index] = result } // use whatever synchronization mechanism you want
}
Oppure, se si vuole una resa funzionale, è possibile utilizzare tale extension
ho definito sopra:
let results = array.concurrentMap { fibonacci($0) }
Per Swift 2 rendition, vedere previous revision of this answer.
Come ho già detto nella risposta ora cancellata, ho fatto un po 'di benchmarking. È leggermente più veloce se dividi l'array in x slice dove x = 'NSProcessInfo(). ActiveProcessorCount * 4'. Ogni slice ha il proprio 'dispatch_apply' –