diff --git a/src/FSharpPlus/Extensions/Extensions.fs b/src/FSharpPlus/Extensions/Extensions.fs index 7f6ab1ba7..cef08f747 100644 --- a/src/FSharpPlus/Extensions/Extensions.fs +++ b/src/FSharpPlus/Extensions/Extensions.fs @@ -127,39 +127,47 @@ module Extensions = /// Returns None if it contains a None element, otherwise a list of all elements static member Sequence (t: seq>) = - let mutable ok = true - let res = Seq.toArray (seq { - use e = t.GetEnumerator () - while e.MoveNext () && ok do - match e.Current with - | Some v -> yield v - | None -> ok <- false }) - if ok then Some (Array.toSeq res) else None - + let accumulator = ArrayCollector<'T> () + let mutable noneFound = false + use e = t.GetEnumerator () + while e.MoveNext () && noneFound do + match e.Current with + | Some v -> accumulator.Add v + | None -> noneFound <- true + + if noneFound + then None + else + Some (accumulator.Close () |> Array.toSeq) + type ValueOption<'t> with /// Returns None if it contains a None element, otherwise a list of all elements static member Sequence (t: seq>) = - let mutable ok = true - let res = Seq.toArray (seq { - use e = t.GetEnumerator () - while e.MoveNext () && ok do - match e.Current with - | ValueSome v -> yield v - | ValueNone -> ok <- false }) - if ok then ValueSome (Array.toSeq res) else ValueNone - + let accumulator = ArrayCollector<'T> () + let mutable noneFound = false + use e = t.GetEnumerator () + while e.MoveNext () && noneFound do + match e.Current with + | ValueSome v -> accumulator.Add v + | ValueNone -> noneFound <- true + + if noneFound + then ValueNone + else + ValueSome (accumulator.Close () |> Array.toSeq) + type Result<'t, 'error> with /// Returns the first Error if it contains an Error element, otherwise a list of all elements static member Sequence (t: seq>) = - let mutable bad = None - let res = Seq.toArray (seq { - use e = t.GetEnumerator () - while e.MoveNext () && bad.IsNone do - match e.Current with - | Ok v -> yield v - | Error x -> bad <- Some x }) - match bad with - | None-> Ok (Array.toSeq res) + let accumulator = ArrayCollector<'T> () + let mutable error = None + use e = t.GetEnumerator () + while e.MoveNext () && error.IsNone do + match e.Current with + | Ok v -> accumulator.Add v + | Error x -> error <- Some x + match error with + | None-> Ok (accumulator.Close () |> Array.toSeq) | Some x -> Error x diff --git a/tests/benchmarks/Benchmarks.fsproj b/tests/benchmarks/Benchmarks.fsproj index 2c1ffc1e6..584d25237 100644 --- a/tests/benchmarks/Benchmarks.fsproj +++ b/tests/benchmarks/Benchmarks.fsproj @@ -16,6 +16,10 @@ + + + + diff --git a/tests/benchmarks/ExtensionsBenchmarks.fs b/tests/benchmarks/ExtensionsBenchmarks.fs new file mode 100644 index 000000000..4dcb22c54 --- /dev/null +++ b/tests/benchmarks/ExtensionsBenchmarks.fs @@ -0,0 +1,170 @@ +module ExtensionsBenchmarks + +open FSharpPlus +open BenchmarkDotNet.Attributes +open Microsoft.FSharp.Core.CompilerServices + +[] +module Common = + let mkArray s n invalidAtEnd arraySize = + if invalidAtEnd then + seq { + yield! Array.init arraySize (fun _ -> s(1)) + yield n + } + |> Seq.toArray + else + seq { + yield! Array.init (arraySize / 2) (fun _ -> s(1)) + yield n + yield! Array.init (arraySize / 2) (fun _ -> s(1)) + } + |> Seq.toArray + +module OptionOldVersion = + let sequence (t: seq>) = + let mutable ok = true + let res = Seq.toArray (seq { + use e = t.GetEnumerator () + while e.MoveNext () && ok do + match e.Current with + | Some v -> yield v + | None -> ok <- false }) + if ok then Some (Array.toSeq res) else None + +module VOptionOldVersion = + let sequence (t: seq>) = + let mutable ok = true + let res = Seq.toArray (seq { + use e = t.GetEnumerator () + while e.MoveNext () && ok do + match e.Current with + | ValueSome v -> yield v + | ValueNone -> ok <- false }) + if ok then ValueSome (Array.toSeq res) else ValueNone + +module ResultOldVersion = + let sequence (t: seq>) = + let mutable error = None + let res = Seq.toArray (seq { + use e = t.GetEnumerator () + while e.MoveNext () && error.IsNone do + match e.Current with + | Ok v -> yield v + | Error e -> error <- Some e }) + + match error with + | None -> Ok (Array.toSeq res) + | Some e -> Error e + +module OptionNewVersion = + + let sequence (t: seq>) = + let accumulator = ArrayCollector<'T> () + let mutable noneFound = false + use e = t.GetEnumerator () + while e.MoveNext () && noneFound do + match e.Current with + | Some v -> accumulator.Add v + | None -> noneFound <- true + + if noneFound + then None + else + Some (accumulator.Close () |> Array.toSeq) + +module VOptionNewVersion = + + let sequence (t: seq>) = + let accumulator = ArrayCollector<'T> () + let mutable noneFound = false + use e = t.GetEnumerator () + while e.MoveNext () && noneFound do + match e.Current with + | ValueSome v -> accumulator.Add v + | ValueNone -> noneFound <- true + + if noneFound + then ValueNone + else + ValueSome (accumulator.Close () |> Array.toSeq) + +module ResultNewVersion = + + let sequence (t: seq>) : Result = + let accumulator = ArrayCollector<'T> () + let mutable error = None + use e = t.GetEnumerator () + while e.MoveNext () && error.IsNone do + match e.Current with + | Ok v -> accumulator.Add v + | Error e -> error <- Some e + + match error with + | None -> Ok (accumulator.Close () |> Array.toSeq) + | Some e -> Error e + +type Values<'t> = { Title: string; Values: 't } + +[] +type OptionBenchmarks() = + + member this.runArray = + seq { + yield { Title = "1000_M"; Values = mkArray Some None false 1000 } + yield { Title = "10000_M"; Values = mkArray Some None false 10000 } + yield { Title = "100000_M"; Values = mkArray Some None false 100000 } + yield { Title = "1000_E"; Values = mkArray Some None true 1000 } + yield { Title = "10000_E"; Values = mkArray Some None true 10000 } + yield { Title = "100000_E"; Values = mkArray Some None true 100000 } + } + + [] + [.runArray))>] + member this.Base (v: Values) = OptionOldVersion.sequence v.Values + + [] + [.runArray))>] + member this.NewVersion (v: Values) = OptionNewVersion.sequence v.Values + +[] +type VOptionBenchmarks() = + + member this.runArray = + seq { + yield { Title = "1000_M"; Values = mkArray ValueSome ValueNone false 1000 } + yield { Title = "10000_M"; Values = mkArray ValueSome ValueNone false 10000 } + yield { Title = "100000_M"; Values = mkArray ValueSome ValueNone false 100000 } + yield { Title = "1000_E"; Values = mkArray ValueSome ValueNone true 1000 } + yield { Title = "10000_E"; Values = mkArray ValueSome ValueNone true 10000 } + yield { Title = "100000_E"; Values = mkArray ValueSome ValueNone true 100000 } + } + + [] + [.runArray))>] + member this.VOptionBase (v: Values) = VOptionOldVersion.sequence v.Values + + [] + [.runArray))>] + member this.NewVersion (v: Values) = VOptionNewVersion.sequence v.Values + +[] +type ResultBenchmarks() = + + member this.runArray = + seq { + yield { Title = "1000_M"; Values = mkArray Ok (Error "error") false 1000 } + yield { Title = "10000_M"; Values = mkArray Ok (Error "error") false 10000 } + yield { Title = "100000_M"; Values = mkArray Ok (Error "error") false 100000 } + yield { Title = "1000_E"; Values = mkArray Ok (Error "error") true 1000 } + yield { Title = "10000_E"; Values = mkArray Ok (Error "error") true 10000 } + yield { Title = "100000_E"; Values = mkArray Ok (Error "error") true 100000 } + } + + [] + [.runArray))>] + member this.ResultBase (v: Values array>) = ResultOldVersion.sequence v.Values + + [] + [.runArray))>] + member this.NewVersion (v: Values array>) = ResultNewVersion.sequence v.Values diff --git a/tests/benchmarks/Program.fs b/tests/benchmarks/Program.fs index 10fccc611..2b603f16f 100644 --- a/tests/benchmarks/Program.fs +++ b/tests/benchmarks/Program.fs @@ -1,6 +1,6 @@ -open AsyncSequences - [] let main _ = - do BenchmarkDotNet.Running.BenchmarkRunner.Run() |> ignore - 0 \ No newline at end of file + // do BenchmarkDotNet.Running.BenchmarkRunner.Run() |> ignore + // do BenchmarkDotNet.Running.BenchmarkRunner.Run() |> ignore + do BenchmarkDotNet.Running.BenchmarkRunner.Run() |> ignore + 0