Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Array.Parallel extensions - filter, zip #14916

Merged
merged 8 commits into from
Mar 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 45 additions & 3 deletions src/FSharp.Core/array.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2098,6 +2098,31 @@ module Array =
yield new ArraySegment<'T>(array, offset, maxIdxExclusive - offset)
|]

[<CompiledName("Zip")>]
let zip (array1: _[]) (array2: _[]) =
checkNonNull "array1" array1
checkNonNull "array2" array2
let len1 = array1.Length

if len1 <> array2.Length then
invalidArgDifferentArrayLength "array1" len1 "array2" array2.Length

let res = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked len1
let inputChunks = createPartitionsUpTo array1.Length array1

Parallel.For(
0,
inputChunks.Length,
fun chunkIdx ->
let chunk = inputChunks[chunkIdx]

for elemIdx = chunk.Offset to (chunk.Offset + chunk.Count - 1) do
res[elemIdx] <- (array1[elemIdx], array2[elemIdx])
)
|> ignore

res

let inline groupByImplParallel
(comparer: IEqualityComparer<'SafeKey>)
([<InlineIfLambda>] keyf: 'T -> 'SafeKey)
Expand Down Expand Up @@ -2213,8 +2238,7 @@ module Array =
Parallel.For(0, count, (fun i -> result.[i] <- initializer i)) |> ignore
result

[<CompiledName("Partition")>]
let partition predicate (array: 'T[]) =
let countAndCollectTrueItems predicate (array: 'T[]) =
checkNonNull "array" array
let inputLength = array.Length

Expand All @@ -2237,10 +2261,28 @@ module Array =
)
|> ignore

trueLength, isTrue

[<CompiledName("Filter")>]
let filter predicate (array: 'T[]) =
let trueLength, isTrue = countAndCollectTrueItems predicate array
T-Gro marked this conversation as resolved.
Show resolved Hide resolved
let res = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked trueLength
let mutable resIdx = 0

for i = 0 to isTrue.Length - 1 do
T-Gro marked this conversation as resolved.
Show resolved Hide resolved
T-Gro marked this conversation as resolved.
Show resolved Hide resolved
if isTrue.[i] then
res.[resIdx] <- array.[i]
resIdx <- resIdx + 1

res

[<CompiledName("Partition")>]
let partition predicate (array: 'T[]) =
let trueLength, isTrue = countAndCollectTrueItems predicate array
let res1 = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked trueLength

let res2 =
Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked (inputLength - trueLength)
Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked (array.Length - trueLength)

let mutable iTrue = 0
let mutable iFalse = 0
Expand Down
46 changes: 46 additions & 0 deletions src/FSharp.Core/array.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -3491,3 +3491,49 @@ module Array =
/// </example>
[<CompiledName("Partition")>]
val partition: predicate:('T -> bool) -> array:'T[] -> 'T[] * 'T[]

/// <summary>Combines the two arrays into an array of pairs. The two arrays must have equal lengths, otherwise an <c>ArgumentException</c> is
/// raised.</summary>
///
/// <param name="array1">The first input array.</param>
/// <param name="array2">The second input array.</param>
///
/// <exception cref="T:System.ArgumentNullException">Thrown when either of the input arrays is null.</exception>
/// <exception cref="T:System.ArgumentException">Thrown when the input arrays differ in length.</exception>
///
/// <returns>The array of tupled elements.</returns>
///
/// <example id="para-zip-1">
/// <code lang="fsharp">
/// let numbers = [|1; 2|]
/// let names = [|"one"; "two"|]
///
/// Array.Parallel.zip numbers names
/// </code>
/// Evaluates to <c>[| (1, "one"); (2, "two") |]</c>.
/// </example>
[<CompiledName("Zip")>]
T-Gro marked this conversation as resolved.
Show resolved Hide resolved
[<Experimental("Experimental library feature, requires '--langversion:preview'")>]
val zip: array1:'T1[] -> array2:'T2[] -> ('T1 * 'T2)[]

/// <summary>Returns a new collection containing only the elements of the collection
/// for which the given predicate returns <c>true</c>.</summary>
///
/// <param name="predicate">The function to test the input elements.</param>
/// <param name="array">The input array.</param>
///
/// <returns>An array containing the elements for which the given predicate returns true.</returns>
///
/// <exception cref="T:System.ArgumentNullException">Thrown when the input array is <c>null</c>.</exception>
///
/// <example id="para-filter-1">
/// <code lang="fsharp">
/// let inputs = [| 1; 2; 3; 4 |]
///
/// inputs |> Array.Parallel.filter (fun elm -> elm % 2 = 0)
/// </code>
/// Evaluates to <c>[| 2; 4 |]</c>
/// </example>
[<CompiledName("Filter")>]
T-Gro marked this conversation as resolved.
Show resolved Hide resolved
[<Experimental("Experimental library feature, requires '--langversion:preview'")>]
val filter: predicate:('T -> bool) -> array:'T[] -> 'T[]
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,14 @@ Microsoft.FSharp.Collections.ArrayModule+Parallel: System.Tuple`2[TKey,T[]][] Gr
Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[System.Int32] TryFindIndex[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[TResult] TryPick[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpOption`1[TResult]], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[T] TryFind[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: System.Tuple`2[T1,T2][] Zip[T1,T2](T1[], T2[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: System.Tuple`2[TKey,T[]][] GroupBy[T,TKey](Microsoft.FSharp.Core.FSharpFunc`2[T,TKey], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: System.Tuple`2[T[],T[]] Partition[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] Choose[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpOption`1[TResult]], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] Collect[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult[]], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] MapIndexed[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[T,TResult]], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] Map[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: T[] Filter[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: T[] Initialize[T](Int32, Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,T])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Void IterateIndexed[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.Unit]], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Void Iterate[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.Unit], T[])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,19 @@ Microsoft.FSharp.Collections.Array4DModule: T[,,,] Create[T](Int32, Int32, Int32
Microsoft.FSharp.Collections.Array4DModule: T[,,,] Initialize[T](Int32, Int32, Int32, Int32, Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,T]]]])
Microsoft.FSharp.Collections.Array4DModule: T[,,,] ZeroCreate[T](Int32, Int32, Int32, Int32)
Microsoft.FSharp.Collections.Array4DModule: Void Set[T](T[,,,], Int32, Int32, Int32, Int32, T)
Microsoft.FSharp.Collections.ArrayModule+Parallel: Boolean Exists[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Boolean ForAll[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: System.Tuple`2[TKey,T[]][] GroupBy[T,TKey](Microsoft.FSharp.Core.FSharpFunc`2[T,TKey], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[System.Int32] TryFindIndex[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[TResult] TryPick[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpOption`1[TResult]], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[T] TryFind[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: System.Tuple`2[T1,T2][] Zip[T1,T2](T1[], T2[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Boolean Exists[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Boolean ForAll[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: System.Tuple`2[TKey,T[]][] GroupBy[T,TKey](Microsoft.FSharp.Core.FSharpFunc`2[T,TKey], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: System.Tuple`2[T[],T[]] Partition[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] Choose[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpOption`1[TResult]], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] Collect[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult[]], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] MapIndexed[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[T,TResult]], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] Map[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: T[] Filter[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: T[] Initialize[T](Int32, Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,T])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Void IterateIndexed[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.Unit]], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Void Iterate[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.Unit], T[])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,17 @@ Microsoft.FSharp.Collections.Array4DModule: T[,,,] ZeroCreate[T](Int32, Int32, I
Microsoft.FSharp.Collections.Array4DModule: Void Set[T](T[,,,], Int32, Int32, Int32, Int32, T)
Microsoft.FSharp.Collections.ArrayModule+Parallel: Boolean Exists[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Boolean ForAll[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: System.Tuple`2[TKey,T[]][] GroupBy[T,TKey](Microsoft.FSharp.Core.FSharpFunc`2[T,TKey], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[System.Int32] TryFindIndex[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[TResult] TryPick[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpOption`1[TResult]], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[T] TryFind[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: System.Tuple`2[T1,T2][] Zip[T1,T2](T1[], T2[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: System.Tuple`2[TKey,T[]][] GroupBy[T,TKey](Microsoft.FSharp.Core.FSharpFunc`2[T,TKey], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: System.Tuple`2[T[],T[]] Partition[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] Choose[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpOption`1[TResult]], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] Collect[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult[]], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] MapIndexed[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[T,TResult]], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] Map[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: T[] Filter[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: T[] Initialize[T](Int32, Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,T])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Void IterateIndexed[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.Unit]], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Void Iterate[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.Unit], T[])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,13 @@ Microsoft.FSharp.Collections.ArrayModule+Parallel: System.Tuple`2[TKey,T[]][] Gr
Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[System.Int32] TryFindIndex[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[TResult] TryPick[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpOption`1[TResult]], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Microsoft.FSharp.Core.FSharpOption`1[T] TryFind[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: System.Tuple`2[T1,T2][] Zip[T1,T2](T1[], T2[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: System.Tuple`2[T[],T[]] Partition[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] Choose[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpOption`1[TResult]], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] Collect[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult[]], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] MapIndexed[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[T,TResult]], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: TResult[] Map[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: T[] Filter[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: T[] Initialize[T](Int32, Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,T])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Void IterateIndexed[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.Unit]], T[])
Microsoft.FSharp.Collections.ArrayModule+Parallel: Void Iterate[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.Unit], T[])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,20 @@ type ArrayModule() =
CheckThrowsArgumentNullException (fun () -> Array.filter funcStr nullArr |> ignore)

()

[<Fact>]
member this.ParallelFilter () =
let assertSameBehavior predicate arr =
let sequentialZip = Array.filter predicate arr
let paraZip = Array.Parallel.filter predicate arr
Assert.AreEqual(sequentialZip, paraZip)

[| 1..20 |] |> assertSameBehavior (fun x -> x%5 = 0)
[|"Lists"; "are"; "a"; "commonly"; "data";"structor" |] |> assertSameBehavior (fun x -> x.Length > 4)
[| |] |> assertSameBehavior (fun x -> x%5 = 0)
let nullArr = null:int[]
CheckThrowsArgumentNullException (fun () -> Array.Parallel.filter (fun x -> x%5 = 0) nullArr |> ignore)


[<Fact>]
member this.Filter2 () =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1259,7 +1259,22 @@ type ArrayModule2() =
// len1 <> len2
CheckThrowsArgumentException(fun () -> Array.zip [|1..10|] [|2..20|] |> ignore)

()
[<Fact>]
member this.ParallelZip() =
let assertSameBehaviour arr1 arr2 =
let sequentialZip = Array.zip arr1 arr2
let paraZip = Array.Parallel.zip arr1 arr2
Assert.AreEqual(sequentialZip, paraZip)

assertSameBehaviour [|1..3|] [|2..2..6|]
assertSameBehaviour[|"A"; "B"; "C" ; "D" |] [|"a";"b";"c";"d"|]
assertSameBehaviour[||] [||]

// null array
let nullArr = null:string[]
CheckThrowsArgumentNullException (fun () -> Array.Parallel.zip nullArr nullArr |> ignore)
// len1 <> len2
CheckThrowsArgumentException(fun () -> Array.Parallel.zip [|1..10|] [|2..20|] |> ignore)

[<Fact>]
member this.Zip3() =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,8 @@ let filter<'a when 'a : equality> (xs : 'a []) predicate =
let s = xs |> Seq.filter predicate
let l = xs |> List.ofArray |> List.filter predicate
let a = xs |> Array.filter predicate
Seq.toArray s = a && List.toArray l = a
let pa = xs |> Array.Parallel.filter predicate
pa = a && Seq.toArray s = a && List.toArray l = a

[<Fact>]
let ``filter is consistent`` () =
Expand Down Expand Up @@ -1288,9 +1289,10 @@ let zip<'a when 'a : equality> (xs':('a*'a) []) =
let xs = Array.map fst xs'
let xs2 = Array.map snd xs'
let s = runAndCheckErrorType (fun () -> Seq.zip xs xs2 |> Seq.toArray)
let l = runAndCheckErrorType (fun () -> List.zip (List.ofSeq xs) (List.ofSeq xs2) |> List.toArray)
let a = runAndCheckErrorType (fun () -> Array.zip (Array.ofSeq xs) (Array.ofSeq xs2))
consistency "zip" s l a
let l = runAndCheckErrorType (fun () -> List.zip (List.ofArray xs) (List.ofArray xs2) |> List.toArray)
let a = runAndCheckErrorType (fun () -> Array.zip xs xs2)
let pa = runAndCheckErrorType (fun () -> Array.Parallel.zip xs xs2)
consistencyIncludingParallel "zip" s l a pa

[<Fact>]
let ``zip is consistent for collections with equal length`` () =
Expand Down