Skip to content
This repository has been archived by the owner on Jun 27, 2023. It is now read-only.

Commit

Permalink
Significantly improve the performance of `Collection<FutureType>.flat…
Browse files Browse the repository at this point in the history
…ten`.

This further halves the runtime in vapor/template-kit#50 from 0.1s to 50ms.
  • Loading branch information
MrMage committed Apr 1, 2019
1 parent 727ae29 commit 369755e
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 17 deletions.
47 changes: 30 additions & 17 deletions Sources/Async/Future+Flatten.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,27 +60,40 @@ extension Collection where Element: FutureType {
return eventLoop.newSucceededFuture(result: [])
}

var promises = [EventLoopPromise<Element.Expectation>]()
for future in self {
let promise = eventLoop.newPromise(of: Element.Expectation.self)
promises.append(promise)
let resultPromise: EventLoopPromise<[Element.Expectation]> = eventLoop.newPromise()
var promiseFulfilled = false

let expectedCount = self.count
var fulfilledCount = 0
var results = Array<Element.Expectation?>(repeating: nil, count: expectedCount)
for (index, future) in self.enumerated() {
future.addAwaiter { result in
switch result {
case .success(let value):
promise.succeed(result: value)
case .error(let error):
promise.fail(error: error)
let work: () -> Void = {
guard !promiseFulfilled else { return }
switch result {
case .success(let result):
results[index] = result
fulfilledCount += 1

if fulfilledCount == expectedCount {
promiseFulfilled = true
// Forcibly unwrapping is okay here, because we know that each result has been filled.
resultPromise.succeed(result: results.map { $0! })
}
case .error(let error):
promiseFulfilled = true
resultPromise.fail(error: error)
}
}

if future.eventLoop === eventLoop {
work()
} else {
eventLoop.execute(work)
}
}
}
let futures = promises.map { $0.futureResult }
return Future<[Element.Expectation]>.reduce(
into: [],
futures,
eventLoop: eventLoop
) { partialResult, nextElement in
return partialResult.append(nextElement)
}
return resultPromise.futureResult
}
}

Expand Down
3 changes: 3 additions & 0 deletions Sources/Async/FutureType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ public protocol FutureType {
/// This future's result type.
typealias Result = FutureResult<Expectation>

/// The event loop this future is fulfilled on.
var eventLoop: EventLoop { get }

/// Adds a new awaiter to this `Future` that will be called when the result is ready.
func addAwaiter(callback: @escaping FutureResultCallback<Expectation>)
}
Expand Down

0 comments on commit 369755e

Please sign in to comment.