Skip to content

SIMD vectorization of Array.sum<int>, etc #18509

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

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
24 changes: 24 additions & 0 deletions src/FSharp.Core/array.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1588,6 +1588,22 @@ module Array =

acc

[<CompiledName("Sum")>]
let inline sumFloat (array: float array) : float =
System.Linq.Enumerable.Sum array

[<CompiledName("Sum")>]
let inline sumFloat32 (array: float32 array) : float32 =
System.Linq.Enumerable.Sum array

[<CompiledName("Sum")>]
let inline sumInt (array: int array) : int =
System.Linq.Enumerable.Sum array

[<CompiledName("Sum")>]
let inline sumInt64 (array: int64 array) : int64 =
System.Linq.Enumerable.Sum array
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would think that this would be a reasonable place to use static optimization syntax to specify which types should delegate to the LINQ method and which to the existing code, e.g.,

let inline sum (array: ^T array) : ^T =
    existingSumCode array
    when ^T : int   = System.Linq.Enumerable.Sum array
    when ^T : int64 = System.Linq.Enumerable.Sum array
    …

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I expect "static optimization conditionals" are a compile-time thing and not runtime? Because I can't check easily with sharplab.io, it says "error FS0817: Static optimization conditionals are only for use within the F# library"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the appropriate implementation would be chosen at compile-time once the type parameter was resolved.

You could add some IL tests under https://github.com/dotnet/fsharp/tree/main/tests/FSharp.Compiler.ComponentTests/EmittedIL if you wanted.


[<CompiledName("SumBy")>]
let inline sumBy ([<InlineIfLambda>] projection: 'T -> ^U) (array: 'T array) : ^U =
checkNonNull "array" array
Expand Down Expand Up @@ -1686,6 +1702,14 @@ module Array =

LanguagePrimitives.DivideByInt< ^T> acc array.Length

[<CompiledName("Average")>]
let inline averageFloat (array: float array) : float =
System.Linq.Enumerable.Average array

[<CompiledName("Average")>]
let inline averageFloat32 (array: float32 array) : float32 =
System.Linq.Enumerable.Average array

[<CompiledName("AverageBy")>]
let inline averageBy ([<InlineIfLambda>] projection: 'T -> ^U) (array: 'T array) : ^U =
checkNonNull "array" array
Expand Down
126 changes: 126 additions & 0 deletions src/FSharp.Core/array.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,56 @@ module Array =
and ^T: (static member DivideByInt: ^T * int -> ^T)
and ^T: (static member Zero: ^T)

/// <summary>Returns the average of the elements in the array using vectorization.</summary>
///
/// <param name="array">The input array.</param>
///
/// <exception cref="T:System.ArgumentException">Thrown when <c>array</c> is empty.</exception>
/// <exception cref="T:System.ArgumentNullException">Thrown when the input array is null.</exception>
///
/// <returns>The average of the elements in the array.</returns>
///
/// <example id="average-1">
/// <code lang="fsharp">
/// [| 1.0; 2.0; 6.0 |] |> Array.average
/// </code>
/// Evaluates to <c>3.0</c>
/// </example>
///
/// <example id="average-2">
/// <code lang="fsharp">
/// [| |] |> Array.average
/// </code>
/// Throws <c>ArgumentException</c>
/// </example>
[<CompiledName("Average")>]
val inline averageFloat: array: float32 array -> float32

/// <summary>Returns the average of the elements in the array using vectorization.</summary>
///
/// <param name="array">The input array.</param>
///
/// <exception cref="T:System.ArgumentException">Thrown when <c>array</c> is empty.</exception>
/// <exception cref="T:System.ArgumentNullException">Thrown when the input array is null.</exception>
///
/// <returns>The average of the elements in the array.</returns>
///
/// <example id="average-1">
/// <code lang="fsharp">
/// [| 1f; 2f; 6f |] |> Array.average
/// </code>
/// Evaluates to <c>3f</c>
/// </example>
///
/// <example id="average-2">
/// <code lang="fsharp">
/// [| |] |> Array.average
/// </code>
/// Throws <c>ArgumentException</c>
/// </example>
[<CompiledName("Average")>]
val inline averageFloat32: array: float32 array -> float32

/// <summary>Returns the average of the elements generated by applying the function to each element of the array.</summary>
///
/// <param name="projection">The function to transform the array elements before averaging.</param>
Expand Down Expand Up @@ -2484,6 +2534,82 @@ module Array =
[<CompiledName("Sum")>]
val inline sum: array: ^T array -> ^T when ^T: (static member (+): ^T * ^T -> ^T) and ^T: (static member Zero: ^T)

/// <summary>Returns the sum of the elements in the array using vectorization.</summary>
///
/// <param name="array">The input array.</param>
///
/// <returns>The resulting sum.</returns>
///
/// <exception cref="T:System.ArgumentNullException">Thrown when the input array is null.</exception>
///
/// <example id="sum-1">
/// <code lang="fsharp">
/// let input = [| 1.; 5.; 3.; 2. |]
///
/// input |> Array.sum
/// </code>
/// Evaluates to <c>11.</c>.
/// </example>
[<CompiledName("Sum")>]
val inline sumFloat: array: float array -> float

/// <summary>Returns the sum of the elements in the array using vectorization.</summary>
///
/// <param name="array">The input array.</param>
///
/// <returns>The resulting sum.</returns>
///
/// <exception cref="T:System.ArgumentNullException">Thrown when the input array is null.</exception>
///
/// <example id="sum-1">
/// <code lang="fsharp">
/// let input = [| 1f; 5f; 3f; 2f |]
///
/// input |> Array.sum
/// </code>
/// Evaluates to <c>11f</c>.
/// </example>
[<CompiledName("Sum")>]
val inline sumFloat32: array: float32 array -> float32

/// <summary>Returns the sum of the elements in the array using vectorization.</summary>
///
/// <param name="array">The input array.</param>
///
/// <returns>The resulting sum.</returns>
///
/// <exception cref="T:System.ArgumentNullException">Thrown when the input array is null.</exception>
///
/// <example id="sum-1">
/// <code lang="fsharp">
/// let input = [| 1; 5; 3; 2 |]
///
/// input |> Array.sum
/// </code>
/// Evaluates to <c>11</c>.
/// </example>
[<CompiledName("Sum")>]
val inline sumInt: array: int array -> int

/// <summary>Returns the sum of the elements in the array using vectorization.</summary>
///
/// <param name="array">The input array.</param>
///
/// <returns>The resulting sum.</returns>
///
/// <exception cref="T:System.ArgumentNullException">Thrown when the input array is null.</exception>
///
/// <example id="sum-1">
/// <code lang="fsharp">
/// let input = [| 1L; 5L; 3L; 2L |]
///
/// input |> Array.sum
/// </code>
/// Evaluates to <c>11L</c>.
/// </example>
[<CompiledName("Sum")>]
val inline sumInt64: array: int64 array -> int64

/// <summary>Returns the sum of the results generated by applying the function to each element of the array.</summary>
///
/// <param name="projection">The function to transform the array elements into the type to be summed.</param>
Expand Down
24 changes: 24 additions & 0 deletions src/FSharp.Core/seq.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1473,6 +1473,22 @@

acc

[<CompiledName("Sum")>]
let inline sumFloat (array: float array) : float =

Check failure on line 1477 in src/FSharp.Core/seq.fs

View check run for this annotation

Azure Pipelines / fsharp-ci (Build Linux)

src/FSharp.Core/seq.fs#L1477

src/FSharp.Core/seq.fs(1477,16): error FS0034: (NETCORE_ENGINEERING_TELEMETRY=Build) Module 'Microsoft.FSharp.Collections.Seq' contains� val sumFloat: array: float array -> float �but its signature specifies� val sumFloat: source: seq<float> -> float �The types differ

Check failure on line 1477 in src/FSharp.Core/seq.fs

View check run for this annotation

Azure Pipelines / fsharp-ci (Build MacOS)

src/FSharp.Core/seq.fs#L1477

src/FSharp.Core/seq.fs(1477,16): error FS0034: (NETCORE_ENGINEERING_TELEMETRY=Build) Module 'Microsoft.FSharp.Collections.Seq' contains� val sumFloat: array: float array -> float �but its signature specifies� val sumFloat: source: seq<float> -> float �The types differ

Check failure on line 1477 in src/FSharp.Core/seq.fs

View check run for this annotation

Azure Pipelines / fsharp-ci

src/FSharp.Core/seq.fs#L1477

src/FSharp.Core/seq.fs(1477,16): error FS0034: (NETCORE_ENGINEERING_TELEMETRY=Build) Module 'Microsoft.FSharp.Collections.Seq' contains� val sumFloat: array: float array -> float �but its signature specifies� val sumFloat: source: seq<float> -> float �The types differ
System.Linq.Enumerable.Sum array

[<CompiledName("Sum")>]
let inline sumFloat32 (array: float32 array) : float32 =

Check failure on line 1481 in src/FSharp.Core/seq.fs

View check run for this annotation

Azure Pipelines / fsharp-ci (Build Linux)

src/FSharp.Core/seq.fs#L1481

src/FSharp.Core/seq.fs(1481,16): error FS0034: (NETCORE_ENGINEERING_TELEMETRY=Build) Module 'Microsoft.FSharp.Collections.Seq' contains� val sumFloat32: array: float32 array -> float32 �but its signature specifies� val sumFloat32: source: seq<float32> -> float32 �The types differ

Check failure on line 1481 in src/FSharp.Core/seq.fs

View check run for this annotation

Azure Pipelines / fsharp-ci (Build MacOS)

src/FSharp.Core/seq.fs#L1481

src/FSharp.Core/seq.fs(1481,16): error FS0034: (NETCORE_ENGINEERING_TELEMETRY=Build) Module 'Microsoft.FSharp.Collections.Seq' contains� val sumFloat32: array: float32 array -> float32 �but its signature specifies� val sumFloat32: source: seq<float32> -> float32 �The types differ

Check failure on line 1481 in src/FSharp.Core/seq.fs

View check run for this annotation

Azure Pipelines / fsharp-ci

src/FSharp.Core/seq.fs#L1481

src/FSharp.Core/seq.fs(1481,16): error FS0034: (NETCORE_ENGINEERING_TELEMETRY=Build) Module 'Microsoft.FSharp.Collections.Seq' contains� val sumFloat32: array: float32 array -> float32 �but its signature specifies� val sumFloat32: source: seq<float32> -> float32 �The types differ
System.Linq.Enumerable.Sum array

[<CompiledName("Sum")>]
let inline sumInt (array: int array) : int =

Check failure on line 1485 in src/FSharp.Core/seq.fs

View check run for this annotation

Azure Pipelines / fsharp-ci (Build Linux)

src/FSharp.Core/seq.fs#L1485

src/FSharp.Core/seq.fs(1485,16): error FS0034: (NETCORE_ENGINEERING_TELEMETRY=Build) Module 'Microsoft.FSharp.Collections.Seq' contains� val sumInt: array: int array -> int �but its signature specifies� val sumInt: source: seq<int> -> int �The types differ

Check failure on line 1485 in src/FSharp.Core/seq.fs

View check run for this annotation

Azure Pipelines / fsharp-ci (Build MacOS)

src/FSharp.Core/seq.fs#L1485

src/FSharp.Core/seq.fs(1485,16): error FS0034: (NETCORE_ENGINEERING_TELEMETRY=Build) Module 'Microsoft.FSharp.Collections.Seq' contains� val sumInt: array: int array -> int �but its signature specifies� val sumInt: source: seq<int> -> int �The types differ

Check failure on line 1485 in src/FSharp.Core/seq.fs

View check run for this annotation

Azure Pipelines / fsharp-ci

src/FSharp.Core/seq.fs#L1485

src/FSharp.Core/seq.fs(1485,16): error FS0034: (NETCORE_ENGINEERING_TELEMETRY=Build) Module 'Microsoft.FSharp.Collections.Seq' contains� val sumInt: array: int array -> int �but its signature specifies� val sumInt: source: seq<int> -> int �The types differ
System.Linq.Enumerable.Sum array

[<CompiledName("Sum")>]
let inline sumInt64 (array: int64 array) : int64 =

Check failure on line 1489 in src/FSharp.Core/seq.fs

View check run for this annotation

Azure Pipelines / fsharp-ci (Build Linux)

src/FSharp.Core/seq.fs#L1489

src/FSharp.Core/seq.fs(1489,16): error FS0034: (NETCORE_ENGINEERING_TELEMETRY=Build) Module 'Microsoft.FSharp.Collections.Seq' contains� val sumInt64: array: int64 array -> int64 �but its signature specifies� val sumInt64: source: seq<int64> -> int64 �The types differ

Check failure on line 1489 in src/FSharp.Core/seq.fs

View check run for this annotation

Azure Pipelines / fsharp-ci (Build Linux)

src/FSharp.Core/seq.fs#L1489

src/FSharp.Core/seq.fs(1489,16): error FS0034: (NETCORE_ENGINEERING_TELEMETRY=Build) Module 'Microsoft.FSharp.Collections.Seq' contains� val sumInt64: array: int64 array -> int64 �but its signature specifies� val sumInt64: source: seq<int64> -> int64 �The types differ

Check failure on line 1489 in src/FSharp.Core/seq.fs

View check run for this annotation

Azure Pipelines / fsharp-ci (Build MacOS)

src/FSharp.Core/seq.fs#L1489

src/FSharp.Core/seq.fs(1489,16): error FS0034: (NETCORE_ENGINEERING_TELEMETRY=Build) Module 'Microsoft.FSharp.Collections.Seq' contains� val sumInt64: array: int64 array -> int64 �but its signature specifies� val sumInt64: source: seq<int64> -> int64 �The types differ

Check failure on line 1489 in src/FSharp.Core/seq.fs

View check run for this annotation

Azure Pipelines / fsharp-ci (Build MacOS)

src/FSharp.Core/seq.fs#L1489

src/FSharp.Core/seq.fs(1489,16): error FS0034: (NETCORE_ENGINEERING_TELEMETRY=Build) Module 'Microsoft.FSharp.Collections.Seq' contains� val sumInt64: array: int64 array -> int64 �but its signature specifies� val sumInt64: source: seq<int64> -> int64 �The types differ

Check failure on line 1489 in src/FSharp.Core/seq.fs

View check run for this annotation

Azure Pipelines / fsharp-ci

src/FSharp.Core/seq.fs#L1489

src/FSharp.Core/seq.fs(1489,16): error FS0034: (NETCORE_ENGINEERING_TELEMETRY=Build) Module 'Microsoft.FSharp.Collections.Seq' contains� val sumInt64: array: int64 array -> int64 �but its signature specifies� val sumInt64: source: seq<int64> -> int64 �The types differ
System.Linq.Enumerable.Sum array

[<CompiledName("SumBy")>]
let inline sumBy ([<InlineIfLambda>] projection: 'T -> ^U) (source: seq<'T>) : ^U =
use e = source.GetEnumerator()
Expand All @@ -1499,6 +1515,14 @@

LanguagePrimitives.DivideByInt< ^a> acc count

[<CompiledName("Average")>]
let inline averageFloat (source: seq<float>) : float =
System.Linq.Enumerable.Average source

[<CompiledName("Average")>]
let inline averageFloat32 (source: seq<float32>) : float32 =
System.Linq.Enumerable.Average source

[<CompiledName("AverageBy")>]
let inline averageBy ([<InlineIfLambda>] projection: 'T -> ^U) (source: seq<'T>) : ^U =
checkNonNull "source" source
Expand Down
132 changes: 132 additions & 0 deletions src/FSharp.Core/seq.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,62 @@ module Seq =
and ^T: (static member DivideByInt: ^T * int -> ^T)
and ^T: (static member Zero: ^T)

/// <summary>Returns the average of the elements in the sequence using vectorization.</summary>
///
/// <remarks>The elements are averaged using the <c>+</c> operator, <c>DivideByInt</c> method and <c>Zero</c> property
/// associated with the element type.</remarks>
///
/// <param name="source">The input sequence.</param>
///
/// <returns>The average.</returns>
///
/// <exception cref="T:System.ArgumentNullException">Thrown when the input sequence is null.</exception>
/// <exception cref="T:System.ArgumentException">Thrown when the input sequence has zero elements.</exception>
///
/// <example id="average-1">
/// <code lang="fsharp">
/// [1.0; 2.0; 3.0] |> Seq.average
/// </code>
/// Evaluates to <c>2.0</c>
/// </example>
///
/// <example id="average-2">
/// <code lang="fsharp">
/// [] |> Seq.average
/// </code>
/// Throws <c>ArgumentException</c>
/// </example>
[<CompiledName("Average")>]
val inline averageFloat: source: seq<float> -> float

/// <summary>Returns the average of the elements in the sequence using vectorization.</summary>
///
/// <remarks>The elements are averaged using the <c>+</c> operator, <c>DivideByInt</c> method and <c>Zero</c> property
/// associated with the element type.</remarks>
///
/// <param name="source">The input sequence.</param>
///
/// <returns>The average.</returns>
///
/// <exception cref="T:System.ArgumentNullException">Thrown when the input sequence is null.</exception>
/// <exception cref="T:System.ArgumentException">Thrown when the input sequence has zero elements.</exception>
///
/// <example id="average-1">
/// <code lang="fsharp">
/// [1f; 2f; 3f] |> Seq.average
/// </code>
/// Evaluates to <c>2f</c>
/// </example>
///
/// <example id="average-2">
/// <code lang="fsharp">
/// [] |> Seq.average
/// </code>
/// Throws <c>ArgumentException</c>
/// </example>
[<CompiledName("Average")>]
val inline averageFloat32: source: seq<float32> -> float32

/// <summary>Returns the average of the results generated by applying the function to each element
/// of the sequence.</summary>
///
Expand Down Expand Up @@ -2347,6 +2403,82 @@ module Seq =
[<CompiledName("Sum")>]
val inline sum: source: seq<(^T)> -> ^T when ^T: (static member (+): ^T * ^T -> ^T) and ^T: (static member Zero: ^T)

/// <summary>Returns the sum of the elements in the sequence using vectorization.</summary>
///
/// <remarks>The elements are summed using the <c>+</c> operator and <c>Zero</c> property associated with the generated type.</remarks>
///
/// <param name="source">The input sequence.</param>
///
/// <returns>The computed sum.</returns>
///
/// <example id="sum-1">
/// <code lang="fsharp">
/// let input = [ 1.; 5.; 3.; 2. ]
///
/// input |> Seq.sum
/// </code>
/// Evaluates to <c>11.</c>.
/// </example>
[<CompiledName("Sum")>]
val inline sumFloat: source: seq<float> -> float

/// <summary>Returns the sum of the elements in the sequence using vectorization.</summary>
///
/// <remarks>The elements are summed using the <c>+</c> operator and <c>Zero</c> property associated with the generated type.</remarks>
///
/// <param name="source">The input sequence.</param>
///
/// <returns>The computed sum.</returns>
///
/// <example id="sum-1">
/// <code lang="fsharp">
/// let input = [ 1f; 5f; 3f; 2f ]
///
/// input |> Seq.sum
/// </code>
/// Evaluates to <c>11f</c>.
/// </example>
[<CompiledName("Sum")>]
val inline sumFloat32: source: seq<float32> -> float32

/// <summary>Returns the sum of the elements in the sequence using vectorization.</summary>
///
/// <remarks>The elements are summed using the <c>+</c> operator and <c>Zero</c> property associated with the generated type.</remarks>
///
/// <param name="source">The input sequence.</param>
///
/// <returns>The computed sum.</returns>
///
/// <example id="sum-1">
/// <code lang="fsharp">
/// let input = [ 1; 5; 3; 2 ]
///
/// input |> Seq.sum
/// </code>
/// Evaluates to <c>11</c>.
/// </example>
[<CompiledName("Sum")>]
val inline sumInt: source: seq<int> -> int

/// <summary>Returns the sum of the elements in the sequence using vectorization.</summary>
///
/// <remarks>The elements are summed using the <c>+</c> operator and <c>Zero</c> property associated with the generated type.</remarks>
///
/// <param name="source">The input sequence.</param>
///
/// <returns>The computed sum.</returns>
///
/// <example id="sum-1">
/// <code lang="fsharp">
/// let input = [ 1; 5; 3; 2 ]
///
/// input |> Seq.sum
/// </code>
/// Evaluates to <c>11</c>.
/// </example>
[<CompiledName("Sum")>]
val inline sumInt64: source: seq<int64> -> int64

/// <summary>Returns the sum of the results generated by applying the function to each element of the sequence.</summary>
///
/// <remarks>The generated elements are summed using the <c>+</c> operator and <c>Zero</c> property associated with the generated type.</remarks>
Expand Down
Loading