Skip to content

Commit 9bb2f82

Browse files
xperiandriclaude
andcommitted
fixup! feat: FlatList.Parallel module with the vocabulary of the FSharp.Core Array.Parallel module
The comments of ParallelArrays are the same kind of documentation as the ones of Arrays: the summary of the FSharp.Core function word for word, the parameters, the result, and remarks that give the complexity and say why the algorithm exists (the options or the reference tuples that FSharp.Core takes or returns, the loop that calls no delegate per element, the comparer of the keys). The callers of each algorithm are seealso. The functions without a counterpart in FSharp.Core say so. The code does not change. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
1 parent 648a861 commit 9bb2f82

1 file changed

Lines changed: 137 additions & 24 deletions

File tree

‎src/FSharp.Collections.Immutable/Helper/ParallelArrays.fs‎

Lines changed: 137 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,29 @@
11
/// <summary>
2-
/// Parallel algorithms over arrays for the functions of <see cref="T:Microsoft.FSharp.Collections.ArrayModule.Parallel"/>
3-
/// that take or return options or reference tuples (ADR 0001, D3 and D11).
2+
/// Parallel algorithms over arrays for the functions of
3+
/// <see cref="T:Microsoft.FSharp.Collections.ArrayModule.Parallel"/> that take or return options or reference tuples
4+
/// (ADR 0001, D3 and D11).
45
/// </summary>
56
/// <remarks>
6-
/// The algorithms take value options and tuple constructors instead, so that the default modules and the <c>Tuple</c> modules
7-
/// share them and nothing is allocated per element. The callers check their arguments first, with the exceptions of
8-
/// <see cref="T:Microsoft.FSharp.Collections.ArrayModule.Parallel"/>.
7+
/// <para>
8+
/// The default functions of <see cref="T:FSharp.Collections.Immutable.FlatList"/> take and return value options and struct
9+
/// tuples, and the functions of the <see cref="T:FSharp.Collections.Immutable.Tuple.FlatList"/> module return reference
10+
/// tuples, where the FSharp.Core functions take and return options and return reference tuples. Calling the FSharp.Core
11+
/// function and converting would allocate for every element. The algorithms here take value options and tuple constructors
12+
/// instead, so that the default modules and the modules of the <see cref="N:FSharp.Collections.Immutable.Tuple"/> namespace
13+
/// share them and nothing is allocated per element.
14+
/// </para>
15+
/// <para>
16+
/// The callers check their arguments first, with the exceptions of
17+
/// <see cref="T:Microsoft.FSharp.Collections.ArrayModule.Parallel"/>, so the algorithms check nothing and document no
18+
/// exceptions.
19+
/// </para>
20+
/// <para>
21+
/// The summary of each function that mirrors a function of the FSharp.Core
22+
/// <see cref="T:Microsoft.FSharp.Collections.ArrayModule.Parallel"/> module is the summary of that function word for word
23+
/// (FSharp.Core 10.1), with <c>ValueSome</c> and <c>ValueNone</c> where it says <c>Some</c> and <c>None</c>, and so are the
24+
/// descriptions of the parameters and of the result where the signatures agree. The remarks say what differs and why the
25+
/// function exists.
26+
/// </para>
927
/// </remarks>
1028
module internal FSharp.Collections.Immutable.ParallelArrays
1129

@@ -18,6 +36,9 @@ open System.Threading.Tasks
1836
/// The chunks of a chunked parallel loop over <paramref name="length"/> elements on <paramref name="processors"/>
1937
/// processors, each as its first index and the index after its last one.
2038
/// </summary>
39+
/// <param name="processors">The number of processors.</param>
40+
/// <param name="length">The number of elements.</param>
41+
/// <returns>The chunks, each as a struct tuple of its first index and the index after its last one.</returns>
2142
/// <remarks>
2243
/// <para>
2344
/// The chunks are those of the chunked loops of <see cref="T:Microsoft.FSharp.Collections.ArrayModule.Parallel"/>: one chunk
@@ -29,6 +50,11 @@ open System.Threading.Tasks
2950
/// of a chunk adds only the elements that remain to its first index, because the first index plus the full chunk length
3051
/// can overflow for arrays near the maximum length on machines with more than 56 processors.
3152
/// </para>
53+
/// <para>
54+
/// FSharp.Core has no such function in its public API. It is a function of its own, so that
55+
/// <see cref="M:FSharp.Collections.Immutable.ParallelArrays.forEachChunk(System.Int32,Microsoft.FSharp.Core.FSharpFunc{System.Int32,Microsoft.FSharp.Core.FSharpFunc{System.Int32,Microsoft.FSharp.Core.Unit}})"/>
56+
/// and the tests share it, and the tests can check the arithmetic for lengths that no array can have.
57+
/// </para>
3258
/// </remarks>
3359
let chunks (processors : int) (length : int) : struct (int * int) array =
3460
let chunk =
@@ -48,9 +74,17 @@ let chunks (processors : int) (length : int) : struct (int * int) array =
4874
/// <summary>
4975
/// Runs an action on the chunks of the indices from 0 to <paramref name="length"/> in parallel.
5076
/// </summary>
77+
/// <param name="length">The number of elements.</param>
78+
/// <param name="action">The action, which takes the first index of its chunk and the index after its last one.</param>
5179
/// <remarks>
52-
/// The action takes the first index of its chunk and the index after its last one. A loop per chunk calls no delegate per
53-
/// element.
80+
/// <para>
81+
/// A loop per chunk calls no delegate per element.
82+
/// </para>
83+
/// <para>
84+
/// FSharp.Core has no such function in its public API. It exists because a
85+
/// <see cref="M:System.Threading.Tasks.Parallel.For(System.Int32,System.Int32,System.Action{System.Int32})"/> over the
86+
/// indices calls a delegate for every element, where the action of a chunk is inlined and loops over its indices.
87+
/// </para>
5488
/// </remarks>
5589
let inline forEachChunk (length : int) ([<InlineIfLambda>] action : int -> int -> unit) =
5690
let bounds = chunks Environment.ProcessorCount length
@@ -65,13 +99,26 @@ let inline forEachChunk (length : int) ([<InlineIfLambda>] action : int -> int -
6599
|> ignore
66100

67101
/// <summary>
68-
/// The values that the chooser returns for the elements, in the order of the elements.
102+
/// Apply the given function to each element of the array. Return the array comprised of the results <c>x</c> for each
103+
/// element where the function returns <c>ValueSome x</c>.
69104
/// </summary>
105+
/// <param name="chooser">The function to generate value options from the elements.</param>
106+
/// <param name="array">The input array.</param>
107+
/// <returns>The array of results.</returns>
70108
/// <remarks>
71-
/// The chooser runs in parallel into an array of value options, which a sequential pass compacts. Calling
109+
/// <para>
110+
/// Performs the operation in parallel using <see cref="M:System.Threading.Tasks.Parallel.For"/>. The order in which the
111+
/// given function is applied to elements of the input array is not specified. This is an O(n) operation, where n is the
112+
/// length of the array.
113+
/// </para>
114+
/// <para>
115+
/// It exists because
72116
/// <see cref="M:Microsoft.FSharp.Collections.ArrayModule.Parallel.Choose``2(Microsoft.FSharp.Core.FSharpFunc{``0,Microsoft.FSharp.Core.FSharpOption{``1}},``0[])"/>
73-
/// instead would convert every value option to an option.
117+
/// takes a chooser that returns options, and would convert every value option to an option. The chooser runs in parallel
118+
/// into an array of value options, which a sequential pass compacts.
119+
/// </para>
74120
/// </remarks>
121+
/// <seealso cref="M:FSharp.Collections.Immutable.FlatList.Parallel.Choose``2(Microsoft.FSharp.Core.FSharpFunc{``0,Microsoft.FSharp.Core.FSharpValueOption{``1}},System.Collections.Immutable.ImmutableArray{``0})"/>
75122
let choose (chooser : 'T -> 'U voption) (array : 'T array) : 'U array =
76123
let chosen = Array.Parallel.map chooser array
77124
let mutable count = 0
@@ -95,6 +142,10 @@ let choose (chooser : 'T -> 'U voption) (array : 'T array) : 'U array =
95142
/// <summary>
96143
/// The comparer of the keys of a parallel grouping.
97144
/// </summary>
145+
/// <returns>
146+
/// The comparer of F# structural equality, except that the keys of the types <see cref="T:System.Double"/> and
147+
/// <see cref="T:System.Single"/> compare with <see cref="M:Microsoft.FSharp.Core.LanguagePrimitives.GenericEqualityER``1(``0,``0)"/>.
148+
/// </returns>
98149
/// <remarks>
99150
/// <para>
100151
/// Keys of type <see cref="T:System.Double"/> and <see cref="T:System.Single"/> compare as in
@@ -108,6 +159,11 @@ let choose (chooser : 'T -> 'U voption) (array : 'T array) : 'U array =
108159
/// The function of FSharp.Core throws <see cref="T:System.AggregateException"/> for such a key: its second pass cannot find
109160
/// the key that its first pass added. That failure is not mirrored.
110161
/// </para>
162+
/// <para>
163+
/// FSharp.Core has no such function in its public API. It exists because the grouping here runs over the keys with
164+
/// <see cref="M:System.Linq.Enumerable.ToLookup``3(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1},System.Func{``0,``2},System.Collections.Generic.IEqualityComparer{``1})"/>,
165+
/// which takes the comparer of the keys, and the comparer has to be that of FSharp.Core.
166+
/// </para>
111167
/// </remarks>
112168
let groupingComparer<'Key when 'Key : equality> () : IEqualityComparer<'Key> =
113169
if
@@ -119,18 +175,35 @@ let groupingComparer<'Key when 'Key : equality> () : IEqualityComparer<'Key> =
119175
HashIdentity.Structural<'Key>
120176

121177
/// <summary>
122-
/// The keys of the projection, each with its elements; the order of both is not specified.
178+
/// Applies a key-generating function to each element of an array in parallel and yields an array of unique keys. Each
179+
/// unique key contains an array of all elements that match to this key.
123180
/// </summary>
181+
/// <param name="tuple">The function that builds an element of the result from a key and the array of its elements.</param>
182+
/// <param name="projection">A function that transforms an element of the array into a comparable key.</param>
183+
/// <param name="array">The input array.</param>
184+
/// <returns>The result array.</returns>
124185
/// <remarks>
125-
/// The projection, the work of the caller, runs in parallel. The grouping then runs over the computed keys with
186+
/// <para>
187+
/// Performs the operation in parallel using <see cref="M:System.Threading.Tasks.Parallel.For"/>. The order in which the
188+
/// given function is applied to elements of the input array is not specified. The order of the keys and values in the
189+
/// result is also not specified. This is an O(n) operation, where n is the length of the array.
190+
/// </para>
191+
/// <para>
192+
/// It exists because
193+
/// <see cref="M:Microsoft.FSharp.Collections.ArrayModule.Parallel.GroupBy``2(Microsoft.FSharp.Core.FSharpFunc{``0,``1},``0[])"/>
194+
/// returns reference tuples. The projection, the work of the caller, runs in parallel. The grouping then runs over the
195+
/// computed keys with
126196
/// <see cref="M:System.Linq.Enumerable.ToLookup``3(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1},System.Func{``0,``2},System.Collections.Generic.IEqualityComparer{``1})"/>
127197
/// and the comparer of <see cref="M:FSharp.Collections.Immutable.ParallelArrays.groupingComparer``1"/>, so that a
128198
/// <see langword="null"/> key is a key like any other. The result has the order of
129199
/// <see cref="M:Microsoft.FSharp.Collections.ArrayModule.GroupBy``2(Microsoft.FSharp.Core.FSharpFunc{``0,``1},``0[])"/>,
130200
/// one of the orders that
131201
/// <see cref="M:Microsoft.FSharp.Collections.ArrayModule.Parallel.GroupBy``2(Microsoft.FSharp.Core.FSharpFunc{``0,``1},``0[])"/>
132202
/// allows.
203+
/// </para>
133204
/// </remarks>
205+
/// <seealso cref="M:FSharp.Collections.Immutable.FlatList.Parallel.GroupBy``2(Microsoft.FSharp.Core.FSharpFunc{``0,``1},System.Collections.Immutable.ImmutableArray{``0})"/>
206+
/// <seealso cref="M:FSharp.Collections.Immutable.Tuple.FlatList.Parallel.GroupBy``2(Microsoft.FSharp.Core.FSharpFunc{``0,``1},System.Collections.Immutable.ImmutableArray{``0})"/>
134207
let inline groupBy
135208
([<InlineIfLambda>] tuple : 'Key -> 'T array -> 'R)
136209
(projection : 'T -> 'Key)
@@ -153,13 +226,26 @@ let inline groupBy
153226
result
154227

155228
/// <summary>
156-
/// The index of the first element that satisfies the predicate, or <c>ValueNone</c>.
229+
/// Returns the index of the first element in the array that satisfies the given predicate. Returns <c>ValueNone</c> if no
230+
/// such element exists.
157231
/// </summary>
232+
/// <param name="predicate">The function to test the input elements.</param>
233+
/// <param name="array">The input array.</param>
234+
/// <returns>The index of the first element that satisfies the predicate, or <c>ValueNone</c>.</returns>
158235
/// <remarks>
159-
/// The predicate runs in parallel. A match breaks the loop, which then starts only the elements before it, and the lowest
160-
/// index that broke the loop wins, as in
161-
/// <see cref="M:Microsoft.FSharp.Collections.ArrayModule.Parallel.TryFindIndex``1(Microsoft.FSharp.Core.FSharpFunc{``0,System.Boolean},``0[])"/>.
236+
/// <para>
237+
/// This is an O(n) operation in the worst case, where n is the length of the array.
238+
/// </para>
239+
/// <para>
240+
/// It exists because
241+
/// <see cref="M:Microsoft.FSharp.Collections.ArrayModule.Parallel.TryFindIndex``1(Microsoft.FSharp.Core.FSharpFunc{``0,System.Boolean},``0[])"/>
242+
/// returns an option, where the default functions return a value option. The predicate runs in parallel. A match breaks the
243+
/// loop, which then starts only the elements before it, and the lowest index that broke the loop wins, as in the function
244+
/// of FSharp.Core.
245+
/// </para>
162246
/// </remarks>
247+
/// <seealso cref="M:FSharp.Collections.Immutable.FlatList.Parallel.TryFindIndex``1(Microsoft.FSharp.Core.FSharpFunc{``0,System.Boolean},System.Collections.Immutable.ImmutableArray{``0})"/>
248+
/// <seealso cref="M:FSharp.Collections.Immutable.FlatList.Parallel.TryFind``1(Microsoft.FSharp.Core.FSharpFunc{``0,System.Boolean},System.Collections.Immutable.ImmutableArray{``0})"/>
163249
let tryFindIndex (predicate : 'T -> bool) (array : 'T array) : int voption =
164250
let result =
165251
Parallel.For (
@@ -178,15 +264,27 @@ let tryFindIndex (predicate : 'T -> bool) (array : 'T array) : int voption =
178264
ValueNone
179265

180266
/// <summary>
181-
/// The value that the chooser returns for the first element with a value, or <c>ValueNone</c>.
267+
/// Applies the given function to successive elements, returning the first result where the function returns
268+
/// <c>ValueSome x</c> for some <c>x</c>. If the function never returns <c>ValueSome x</c> then <c>ValueNone</c> is
269+
/// returned.
182270
/// </summary>
271+
/// <param name="chooser">The function to transform the array elements into value options.</param>
272+
/// <param name="array">The input array.</param>
273+
/// <returns>The first transformed element that is <c>ValueSome x</c>.</returns>
183274
/// <remarks>
184-
/// The chooser runs in parallel. A value breaks the loop, which then starts only the elements before it, and the value of
185-
/// the lowest index wins. The function of FSharp.Core,
186-
/// <see cref="M:Microsoft.FSharp.Collections.ArrayModule.Parallel.TryPick``2(Microsoft.FSharp.Core.FSharpFunc{``0,Microsoft.FSharp.Core.FSharpOption{``1}},``0[])"/>,
187-
/// keeps every value in a concurrent dictionary by index; here a lock guards the value of the lowest index so far, and
188-
/// only the elements that have a value take it.
275+
/// <para>
276+
/// This is an O(n) operation in the worst case, where n is the length of the array.
277+
/// </para>
278+
/// <para>
279+
/// It exists because
280+
/// <see cref="M:Microsoft.FSharp.Collections.ArrayModule.Parallel.TryPick``2(Microsoft.FSharp.Core.FSharpFunc{``0,Microsoft.FSharp.Core.FSharpOption{``1}},``0[])"/>
281+
/// takes a chooser that returns options, where the default functions use value options. The chooser runs in parallel. A
282+
/// value breaks the loop, which then starts only the elements before it, and the value of the lowest index wins. The
283+
/// function of FSharp.Core keeps every value in a concurrent dictionary by index; here a lock guards the value of the
284+
/// lowest index so far, and only the elements that have a value take it.
285+
/// </para>
189286
/// </remarks>
287+
/// <seealso cref="M:FSharp.Collections.Immutable.FlatList.Parallel.TryPick``2(Microsoft.FSharp.Core.FSharpFunc{``0,Microsoft.FSharp.Core.FSharpValueOption{``1}},System.Collections.Immutable.ImmutableArray{``0})"/>
190288
let tryPick (chooser : 'T -> 'U voption) (array : 'T array) : 'U voption =
191289
let gate = obj ()
192290
let lowest = ref Int32.MaxValue
@@ -214,11 +312,26 @@ let tryPick (chooser : 'T -> 'U voption) (array : 'T array) : 'U voption =
214312
chosen.Value
215313

216314
/// <summary>
217-
/// Pairs the elements of two arrays of the same length in parallel.
315+
/// Combines the two arrays into an array of pairs. The two arrays must have equal lengths, otherwise an
316+
/// <see cref="T:System.ArgumentException"/> is raised.
218317
/// </summary>
318+
/// <param name="tuple">The function that builds an element of the result from the elements at the same index.</param>
319+
/// <param name="array1">The first input array.</param>
320+
/// <param name="array2">The second input array.</param>
321+
/// <returns>The array of tupled elements.</returns>
219322
/// <remarks>
220-
/// The chunks are those of <see cref="M:Microsoft.FSharp.Collections.ArrayModule.Parallel.Zip``2(``0[],``1[])"/>.
323+
/// <para>
324+
/// This is an O(n) operation, where n is the length of the arrays. The chunks are those of
325+
/// <see cref="M:Microsoft.FSharp.Collections.ArrayModule.Parallel.Zip``2(``0[],``1[])"/>. The callers check the lengths first
326+
/// and raise its exception.
327+
/// </para>
328+
/// <para>
329+
/// It exists because that function returns reference tuples, which allocate for every element, where the default function
330+
/// returns struct tuples.
331+
/// </para>
221332
/// </remarks>
333+
/// <seealso cref="M:FSharp.Collections.Immutable.FlatList.Parallel.Zip``2(System.Collections.Immutable.ImmutableArray{``0},System.Collections.Immutable.ImmutableArray{``1})"/>
334+
/// <seealso cref="M:FSharp.Collections.Immutable.Tuple.FlatList.Parallel.Zip``2(System.Collections.Immutable.ImmutableArray{``0},System.Collections.Immutable.ImmutableArray{``1})"/>
222335
let inline zip ([<InlineIfLambda>] tuple : 'T1 -> 'T2 -> 'R) (array1 : 'T1 array) (array2 : 'T2 array) : 'R array =
223336
let result = Array.zeroCreate array1.Length
224337

0 commit comments

Comments
 (0)