并行执行计算循环的最简单方法是concurrentPerform(以前称为dispatch_apply;请参阅并发编程指南中的Performing Loop Iterations Concurrently)。但是,不,没有map 可以为您执行此操作。你必须自己做。
例如,您可以编写一个扩展来执行并发任务:
extension Array {
public func concurrentMap<T>(_ transform: (Element) -> T) -> [T] {
var results = [Int: T](minimumCapacity: count)
let lock = NSLock()
DispatchQueue.concurrentPerform(iterations: count) { index in
let result = transform(self[index])
lock.synchronized {
results[index] = result
}
}
return (0 ..< results.count).compactMap { results[$0] }
}
}
在哪里
extension NSLocking {
func synchronized<T>(block: () throws -> T) rethrows -> T {
lock()
defer { unlock() }
return try block()
}
}
你可以使用任何你想要的同步机制(锁、串行队列、读写器),但想法是同时执行transform,然后同步集合的更新。
注意:
-
这将阻塞您从中调用它的线程(就像非并发 map 一样),因此请确保将其分派到后台队列。
-
需要确保每个线程上有足够的工作来证明管理所有这些线程的固有开销是合理的。 (例如,每个循环一个简单的异或调用是不够的,你会发现它实际上比非并发版本慢。)在这些情况下,确保你大步前进(参见Improving Loop Code,它平衡了每个并发的工作量堵塞)。例如,与其对一个极其简单的操作进行 5000 次迭代,不如在每个循环中进行 500 次操作的 10 次迭代。您可能需要尝试合适的跨步值。
虽然我怀疑您不需要这个讨论,但对于不熟悉concurrentPerform(以前称为dispatch_apply)的读者,我将在下面说明它的用法。有关该主题的更完整讨论,请参阅上面的链接。
例如,让我们考虑一些比简单的xor 复杂得多的东西(因为这样简单的东西,开销超过了所获得的任何性能),例如一个简单的斐波那契实现:
func fibonacci(_ n: Int) -> Int {
if n == 0 || n == 1 {
return n
}
return fibonacci(n - 1) + fibonacci(n - 2)
}
如果您想要计算 array 的 Int 值,而不是:
let results = array.map { fibonacci($0) }
你可以:
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
}
或者,如果你想要一个功能再现,你可以使用我上面定义的extension:
let results = array.concurrentMap { fibonacci($0) }
对于 Swift 2 版本,请参阅 previous revision of this answer。