我最近一直在Swift 2.1中操作字节数组,我经常发现自己编写这样的代码:
// code to add functions to a [UInt8] object
extension CollectionType where Generator.Element == UInt8 {
func xor(with byte: UInt8) -> [UInt8] {
return map { $0 ^ byte }
}
}
// example usage: [67, 108].xor(with: 0) == [67, 108]
是否有一种简单的方法可以并行化此map
调用,以便多个线程可以同时在阵列的非重叠区域上运行?
我可以编写代码来手动将数组划分为子数组,并map
在不同的线程中调用每个子数组.但我想知道Swift中是否存在一些自动进行除法的框架,因为它map
是一个可以在没有副作用的线程安全环境中工作的函数调用.
澄清笔记:
代码只需要处理一个[UInt8]
对象,不一定每个都有CollectionType
.
我正在编写这段代码只是为了学习Swift,而不是为了任何现实世界的输入.毕竟,xor运行速度非常快,无需任何类型的并行化.
如果绝对必要的,上面的功能,可以在上下文中可以看到这里,它被用来解决集1,面临的挑战3 Matasano加密挑战.
Rob.. 8
并行执行计算循环的最简单方法是concurrentPerform
(以前称为dispatch_apply
;请参阅" 并发编程指南"中的" 同时执行循环迭代").但是,不,没有任何map
表现可以为你做到这一点.你必须自己做.
例如,您可以编写扩展来执行并发任务:
extension Array { public func concurrentMap(_ 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]! } } } }
注意:
这将阻止你调用它的线程(就像非并发的一样map
),所以一定要把它调到后台队列.
需要确保每个线程上有足够的工作来证明管理所有这些线程的固有开销.(例如,每个循环的简单xor调用是不够的,你会发现它实际上比非并发再现慢.)在这些情况下,请确保你迈步(参见改进循环代码,以平衡每个并发的工作量)块).例如,不是对一个非常简单的操作进行5000次迭代,而是每次循环执行10次500次操作的迭代.您可能需要尝试合适的跨越值.
虽然我怀疑你不需要这个讨论,对于不熟悉concurrentPerform
(以前称为dispatch_apply
)的读者,我将在下面说明它的用法.有关该主题的更完整讨论,请参阅上面的链接.
例如,让我们考虑比简单更复杂的东西xor
(因为有些东西很简单,开销超过任何获得的性能),例如天真的Fibonacci实现:
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的演绎,请参阅此答案的上一版本.
并行执行计算循环的最简单方法是concurrentPerform
(以前称为dispatch_apply
;请参阅" 并发编程指南"中的" 同时执行循环迭代").但是,不,没有任何map
表现可以为你做到这一点.你必须自己做.
例如,您可以编写扩展来执行并发任务:
extension Array { public func concurrentMap(_ 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]! } } } }
注意:
这将阻止你调用它的线程(就像非并发的一样map
),所以一定要把它调到后台队列.
需要确保每个线程上有足够的工作来证明管理所有这些线程的固有开销.(例如,每个循环的简单xor调用是不够的,你会发现它实际上比非并发再现慢.)在这些情况下,请确保你迈步(参见改进循环代码,以平衡每个并发的工作量)块).例如,不是对一个非常简单的操作进行5000次迭代,而是每次循环执行10次500次操作的迭代.您可能需要尝试合适的跨越值.
虽然我怀疑你不需要这个讨论,对于不熟悉concurrentPerform
(以前称为dispatch_apply
)的读者,我将在下面说明它的用法.有关该主题的更完整讨论,请参阅上面的链接.
例如,让我们考虑比简单更复杂的东西xor
(因为有些东西很简单,开销超过任何获得的性能),例如天真的Fibonacci实现:
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的演绎,请参阅此答案的上一版本.