Created
December 24, 2020 01:10
-
-
Save DougGregor/92a2e4f6e11f6d733fb5065e9d1c880f to your computer and use it in GitHub Desktop.
Swift async/await implementation of a parallel map
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extension Collection { | |
func parallelMap<T>( | |
parallelism requestedParallelism: Int? = nil, | |
_ transform: @escaping (Element) async throws -> T | |
) async throws -> [T] { | |
let defaultParallelism = 2 | |
let parallelism = requestedParallelism ?? defaultParallelism | |
let n = self.count | |
if n == 0 { | |
return [] | |
} | |
return await try Task.withGroup(resultType: (Int, T).self) { group in | |
var result = Array<T?>(repeatElement(nil, count: n)) | |
var i = self.startIndex | |
var submitted = 0 | |
func submitNext() async throws { | |
if i == self.endIndex { return } | |
await group.add { [submitted, i] in | |
let value = await try transform(self[i]) | |
return (submitted, value) | |
} | |
submitted += 1 | |
formIndex(after: &i) | |
} | |
// submit first initial tasks | |
for _ in 0..<parallelism { | |
await try submitNext() | |
} | |
// as each task completes, submit a new task until we run out of work | |
while let (index, taskResult) = await try! group.next() { | |
result[index] = taskResult | |
await try Task.checkCancellation() | |
await try submitNext() | |
} | |
assert(result.count == n) | |
return Array(result.compactMap { $0 }) | |
} | |
} | |
} | |
func getSimpleArray(n: Int) -> [Int] { | |
var array = [Int]() | |
for i in 0..<n { | |
array.append(i) | |
} | |
return array | |
} | |
runAsyncAndBlock { | |
let array = getSimpleArray(n: 100) | |
let resultArray: [String] = await try! array.parallelMap(parallelism: 4) { element in | |
print("Transforming \(element)") | |
return String(element * 10) | |
} | |
print(resultArray) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Why do you add another wrapper here
return Array(result.compactMap { $0 })
, can it be directly likereturn result.compactMap { $0 }