-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add BatchedForkedArray and other changes (#17)
- Loading branch information
Showing
9 changed files
with
395 additions
and
27 deletions.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/// Using a single array and a single async function, batch the parallelized work for each value of the array | ||
public struct BatchedForkedArray<Value, Output> { | ||
private let batchedArray: [[Value]] | ||
private let filter: (Value) async throws -> Bool | ||
private let map: (Value) async throws -> Output | ||
|
||
/// Create a ``BatchedForkedArray`` using a single `Array` | ||
/// - Parameters: | ||
/// - array: The `Array` to be used in creating the output | ||
/// - batch: The number of elements to batch together. (The minimum value is 1) | ||
/// - filter: An `async` closure that determines if the value should be used or not | ||
/// - map: An `async` closure that uses the `Array.Element` as its input | ||
public init( | ||
_ array: [Value], | ||
batch: UInt, | ||
filter: @escaping (Value) async throws -> Bool, | ||
map: @escaping (Value) async throws -> Output | ||
) { | ||
var index: Int = 0 | ||
let batchLimit: UInt = max(batch, 1) | ||
let batchedArray: [[Value]] | ||
|
||
batchedArray = array.reduce(into: []) { partialResult, value in | ||
guard partialResult.isEmpty == false else { | ||
partialResult.append([value]) | ||
return | ||
} | ||
|
||
guard partialResult[index].count < batchLimit else { | ||
partialResult.append([value]) | ||
return index += 1 | ||
} | ||
|
||
partialResult[index].append(value) | ||
} | ||
|
||
self.batchedArray = batchedArray | ||
self.filter = filter | ||
self.map = map | ||
} | ||
|
||
/// Asynchronously resolve the forked array | ||
/// | ||
/// - Returns: The resolved array after performing the batched operations | ||
public func output() async throws -> [Output] { | ||
var batchedOutput: [[Output]] = [] | ||
|
||
for batch in batchedArray { | ||
let batchedValues = try await batch.asyncFilter(filter).asyncMap(map) | ||
|
||
batchedOutput.append(batchedValues) | ||
} | ||
|
||
return batchedOutput.flatMap(identity) | ||
} | ||
|
||
/// Stream the forked array asynchronously | ||
/// | ||
/// - Returns: An AsyncThrowingStream object that yields batches of the resolved array | ||
public func stream() -> AsyncThrowingStream<[Output], Error> { | ||
AsyncThrowingStream { continuation in | ||
Task { | ||
for batch in batchedArray { | ||
let batchedValues = try await batch.asyncFilter(filter).asyncMap(map) | ||
|
||
continuation.yield(batchedValues) | ||
} | ||
|
||
continuation.finish() | ||
} | ||
} | ||
} | ||
} | ||
|
||
extension BatchedForkedArray { | ||
/// Create a ``BatchedForkedArray`` using a single `Array` | ||
/// - Parameters: | ||
/// - array: The `Array` to be used in creating the output | ||
/// - batch: The number of elements to batch together. (The minimum value is 1) | ||
/// - map: An `async` closure that uses the `Array.Element` as its input | ||
public init( | ||
_ array: [Value], | ||
batch: UInt, | ||
map: @escaping (Value) async throws -> Output | ||
) { | ||
self.init(array, batch: batch, filter: { _ in true }, map: 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
extension Sequence { | ||
/// Create a ``BatchedForkedArray`` from the current `Sequence` | ||
public func batchedFork<Output>( | ||
batch: UInt, | ||
filter: @escaping (Element) async throws -> Bool, | ||
map: @escaping (Element) async throws -> Output | ||
) -> BatchedForkedArray<Element, Output> { | ||
BatchedForkedArray( | ||
Array(self), | ||
batch: batch, | ||
filter: filter, | ||
map: map | ||
) | ||
} | ||
|
||
/// Create a ``BatchedForkedArray`` from the current `Sequence` | ||
public func batchedFork<Output>( | ||
batch: UInt, | ||
map: @escaping (Element) async throws -> Output | ||
) -> BatchedForkedArray<Element, Output> { | ||
batchedFork(batch: batch, filter: { _ in true }, map: map) | ||
} | ||
|
||
/// Create a ``BatchedForkedArray`` from the current `Sequence` and get the Output Array | ||
public func batchedForked<Output>( | ||
batch: UInt, | ||
filter: @escaping (Element) async throws -> Bool, | ||
map: @escaping (Element) async throws -> Output | ||
) async throws -> [Output] { | ||
try await fork(filter: filter, map: map).output() | ||
} | ||
|
||
/// Create a ``BatchedForkedArray`` from the current `Sequence` and get the Output Array | ||
public func batchedForked<Output>( | ||
batch: UInt, | ||
map: @escaping (Element) async throws -> Output | ||
) async throws -> [Output] { | ||
try await batchedForked(batch: batch, filter: { _ in true }, map: map) | ||
} | ||
|
||
/// Returns an array containing the results of mapping the given closure over the sequence’s elements. | ||
public func asyncBatchedMap<Output>( | ||
batch: UInt, | ||
_ transform: @escaping (Element) async throws -> Output | ||
) async throws -> [Output] { | ||
try await batchedFork(batch: batch, map: transform).output() | ||
} | ||
|
||
/// Returns an array containing the results, that aren't nil, of mapping the given closure over the sequence’s elements. | ||
public func asyncBatchedCompactMap<Output>( | ||
batch: UInt, | ||
_ transform: @escaping (Element) async throws -> Output? | ||
) async throws -> [Output] { | ||
try await batchedFork(batch: batch, map: transform).output().compactMap { $0 } | ||
} | ||
|
||
/// Returns an array containing only the true results from the given closure over the sequence’s elements. | ||
public func asyncBatchedFilter( | ||
batch: UInt, | ||
_ isIncluded: @escaping (Element) async throws -> Bool | ||
) async throws -> [Element] { | ||
try await batchedFork(batch: batch, filter: isIncluded, map: identity).output() | ||
} | ||
|
||
/// Calls the given closure for each of the elements in the Sequence. This function uses ``BatchedForkedArray`` and will be parallelized when possible. | ||
public func asyncBatchedForEach( | ||
batch: UInt, | ||
_ transform: @escaping (Element) async throws -> Void | ||
) async throws { | ||
_ = try await asyncBatchedMap(batch: batch, transform) | ||
} | ||
} |
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,5 +59,4 @@ extension ForkedActor { | |
rightOutput: rightOutput | ||
) | ||
} | ||
|
||
} |
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
Oops, something went wrong.