diff --git a/docs/release-notes/.FSharp.Core/10.0.100.md b/docs/release-notes/.FSharp.Core/10.0.100.md index 10ff967c125..643930ee30f 100644 --- a/docs/release-notes/.FSharp.Core/10.0.100.md +++ b/docs/release-notes/.FSharp.Core/10.0.100.md @@ -7,5 +7,6 @@ ### Changed * Random functions support for zero element chosen/sampled ([PR #18568](https://github.com/dotnet/fsharp/pull/18568)) +* Array.sum and Seq.sum to call System.Linq.Enumerable methods on base-types (float/float32/int/int64) to utilize vectorization. [PR #18509](https://github.com/dotnet/fsharp/pull/18509) ### Breaking Changes diff --git a/src/FSharp.Core/array.fs b/src/FSharp.Core/array.fs index 358836ddf1b..b81828663a7 100644 --- a/src/FSharp.Core/array.fs +++ b/src/FSharp.Core/array.fs @@ -1578,8 +1578,7 @@ module Array = checkNonNull "array" array Microsoft.FSharp.Primitives.Basics.Array.permute indexMap array - [] - let inline sum (array: ^T array) : ^T = + let inline private fsharpSumImpl (array: ^T array) : ^T = checkNonNull "array" array let mutable acc = LanguagePrimitives.GenericZero< ^T> @@ -1588,6 +1587,32 @@ module Array = acc + let isNetFramework = System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription.StartsWith ".NET Framework" + + [] + let inline sum (array: ^T array) : ^T = + fsharpSumImpl array + when ^T : float = + if isNetFramework then fsharpSumImpl array + else + let r = (System.Linq.Enumerable.Sum : IEnumerable -> float) (# "" array : IEnumerable #) + (# "" r : 'T #) + when ^T : float32 = + if isNetFramework then fsharpSumImpl array + else + let r = (System.Linq.Enumerable.Sum : IEnumerable -> float32) (# "" array : IEnumerable #) + (# "" r : 'T #) + when ^T : int = + if isNetFramework then fsharpSumImpl array + else + let r = (System.Linq.Enumerable.Sum : IEnumerable -> int) (# "" array : IEnumerable #) + (# "" r : 'T #) + when ^T : int64 = + if isNetFramework then fsharpSumImpl array + else + let r = (System.Linq.Enumerable.Sum : IEnumerable -> int64) (# "" array : IEnumerable #) + (# "" r : 'T #) + [] let inline sumBy ([] projection: 'T -> ^U) (array: 'T array) : ^U = checkNonNull "array" array diff --git a/src/FSharp.Core/array.fsi b/src/FSharp.Core/array.fsi index 18a4463277e..c08fac6b8a0 100644 --- a/src/FSharp.Core/array.fsi +++ b/src/FSharp.Core/array.fsi @@ -2466,6 +2466,11 @@ module Array = [] val inline sortByDescending: projection: ('T -> 'Key) -> array: 'T array -> 'T array when 'Key: comparison + /// Internal use of Array.sum to detect if vectorization can be used. + /// Due to sum "inline" this can't be private. + [] + val isNetFramework : bool + /// Returns the sum of the elements in the array. /// /// The input array. diff --git a/src/FSharp.Core/seq.fs b/src/FSharp.Core/seq.fs index ce1bfe6d4ab..bf4b3c3449d 100644 --- a/src/FSharp.Core/seq.fs +++ b/src/FSharp.Core/seq.fs @@ -478,7 +478,8 @@ module Internal = static member Bind(g: Generator<'T>, cont) = match g with | :? GenerateThen<'T> as g -> - GenerateThen<_>.Bind(g.Generator, (fun () -> GenerateThen<_>.Bind(g.Cont(), cont))) + GenerateThen<_> + .Bind(g.Generator, (fun () -> GenerateThen<_>.Bind(g.Cont(), cont))) | g -> (new GenerateThen<'T>(g, cont) :> Generator<'T>) let bindG g cont = @@ -1463,15 +1464,40 @@ module Seq = else mkDelayedSeq (fun () -> countByRefType projection source) - [] - let inline sum (source: seq< ^a >) : ^a = + let inline private fsharpSumImpl (source: seq< ^a >) : ^a = use e = source.GetEnumerator() let mutable acc = LanguagePrimitives.GenericZero< ^a> while e.MoveNext() do acc <- Checked.(+) acc e.Current - acc + acc + + let isNetFramework = System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription.StartsWith ".NET Framework" + + [] + let inline sum (source: seq< ^a >) : ^a = + fsharpSumImpl source + when ^a: int64 = + if isNetFramework then fsharpSumImpl source + else + let r = (System.Linq.Enumerable.Sum: IEnumerable -> int64) (# "" source : IEnumerable #) + (# "" r : 'a #) + when ^a: int = + if isNetFramework then fsharpSumImpl source + else + let r = (System.Linq.Enumerable.Sum: IEnumerable -> int) (# "" source : IEnumerable #) + (# "" r : 'a #) + when ^a: float32 = + if isNetFramework then fsharpSumImpl source + else + let r = (System.Linq.Enumerable.Sum: IEnumerable -> float32) (# "" source : IEnumerable #) + (# "" r : 'a #) + when ^a: float = + if isNetFramework then fsharpSumImpl source + else + let r = (System.Linq.Enumerable.Sum: IEnumerable -> float) (# "" source : IEnumerable #) + (# "" r : 'a #) [] let inline sumBy ([] projection: 'T -> ^U) (source: seq<'T>) : ^U = diff --git a/src/FSharp.Core/seq.fsi b/src/FSharp.Core/seq.fsi index 24189d3730c..2a418d69f51 100644 --- a/src/FSharp.Core/seq.fsi +++ b/src/FSharp.Core/seq.fsi @@ -2329,6 +2329,11 @@ module Seq = [] val inline sortByDescending: projection: ('T -> 'Key) -> source: seq<'T> -> seq<'T> when 'Key: comparison + /// Internal use of Seq.sum to detect if vectorization can be used. + /// Due to sum "inline" this can't be private. + [] + val isNetFramework : bool + /// Returns the sum of the elements in the sequence. /// /// The elements are summed using the + operator and Zero property associated with the generated type. diff --git a/tests/benchmarks/CompiledCodeBenchmarks/MicroPerf/Collections.fs b/tests/benchmarks/CompiledCodeBenchmarks/MicroPerf/Collections.fs index 50f1719c5b5..9e1f84548b9 100644 --- a/tests/benchmarks/CompiledCodeBenchmarks/MicroPerf/Collections.fs +++ b/tests/benchmarks/CompiledCodeBenchmarks/MicroPerf/Collections.fs @@ -141,6 +141,11 @@ type CollectionsBenchmark() = |> Array.updateAt (x.Length - 1) 1 |> ignore + [] + member x.ArraySum() = + array + |> Array.sum + |> ignore /// Seq [] member x.SeqBaseline() =