执行并行计算的最简单方法是使用
concurrentPerform
(以前称为
dispatch_apply
;请参阅
Concurrency Programming Guide中的
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
一样),因此请确保将其调度到后台队列。
- 需要确保每个线程上有足够的工作来证明管理所有这些线程的固有开销是合理的。(例如,每次循环一个简单的异或调用是不够的,您会发现它实际上比非并发版本更慢。)在这些情况下,请确保跨越适当的步幅(请参见
改善循环代码,平衡每个并发块的工作量)。例如,而不是执行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)
}
如果你有一个 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 }
}
或者,如果你想要一个功能性的版本,你可以使用我上面定义的那个 扩展
:
let results = array.concurrentMap { fibonacci($0) }
对于Swift 2版本,请参见此回答的先前版本。
NSProcessInfo().activeProcessorCount * 4
,则速度会稍微快一些。每个切片都有自己的dispatch_apply
。 - R MenkeconcurrentPerform
的每次迭代来说不够大,那么您需要使用stride。请参考https://stackoverflow.com/a/39949292/1271826以获取stride的示例。 - Rob