1+ namespace AsyncSeqBenchmarks
2+
3+ open System
4+ open BenchmarkDotNet.Attributes
5+ open BenchmarkDotNet.Configs
6+ open BenchmarkDotNet.Running
7+ open BenchmarkDotNet.Jobs
8+ open BenchmarkDotNet.Engines
9+ open BenchmarkDotNet.Toolchains .InProcess .Emit
10+ open FSharp.Control
11+
12+ /// Core AsyncSeq performance benchmarks focused on foundational operations
13+ [<MemoryDiagnoser>]
14+ [<SimpleJob( RuntimeMoniker.Net80) >]
15+ type AsyncSeqCoreBenchmarks () =
16+
17+ [<Params( 1000 , 10000 ) >]
18+ member val ElementCount = 0 with get, set
19+
20+ /// Benchmark unfoldAsync - core sequence generation
21+ [<Benchmark( Baseline = true ) >]
22+ member this.UnfoldAsync () =
23+ let generator state = async {
24+ if state < this.ElementCount then
25+ return Some ( state, state + 1 )
26+ else
27+ return None
28+ }
29+ AsyncSeq.unfoldAsync generator 0
30+ |> AsyncSeq.iterAsync ( fun _ -> async.Return())
31+ |> Async.RunSynchronously
32+
33+ /// Benchmark replicate - simple constant generation
34+ [<Benchmark>]
35+ member this.Replicate () =
36+ AsyncSeq.replicate this.ElementCount 42
37+ |> AsyncSeq.iterAsync ( fun _ -> async.Return())
38+ |> Async.RunSynchronously
39+
40+ /// Benchmark mapAsync - common transformation
41+ [<Benchmark>]
42+ member this.MapAsync () =
43+ AsyncSeq.replicate this.ElementCount 1
44+ |> AsyncSeq.mapAsync ( fun x -> async.Return ( x * 2 ))
45+ |> AsyncSeq.iterAsync ( fun _ -> async.Return())
46+ |> Async.RunSynchronously
47+
48+ /// Benchmark chooseAsync with high selectivity
49+ [<Benchmark>]
50+ member this.ChooseAsync () =
51+ AsyncSeq.replicate this.ElementCount 1
52+ |> AsyncSeq.chooseAsync ( fun x -> async.Return ( Some ( x * 2 )))
53+ |> AsyncSeq.iterAsync ( fun _ -> async.Return())
54+ |> Async.RunSynchronously
55+
56+ /// Benchmarks for append operations (previously had memory leaks)
57+ [<MemoryDiagnoser>]
58+ [<SimpleJob( RuntimeMoniker.Net80) >]
59+ type AsyncSeqAppendBenchmarks () =
60+
61+ [<Params( 10 , 50 , 100 ) >]
62+ member val ChainCount = 0 with get, set
63+
64+ /// Benchmark chained appends - tests for memory leaks and O(n²) behavior
65+ [<Benchmark>]
66+ member this.ChainedAppends () =
67+ let mutable result = AsyncSeq.singleton 1
68+ for i in 2 .. this.ChainCount do
69+ result <- AsyncSeq.append result ( AsyncSeq.singleton i)
70+ result
71+ |> AsyncSeq.iterAsync ( fun _ -> async.Return())
72+ |> Async.RunSynchronously
73+
74+ /// Benchmark multiple sequence appends
75+ [<Benchmark>]
76+ member this.MultipleAppends () =
77+ let sequences = [ 1 .. this.ChainCount] |> List.map ( fun i -> AsyncSeq.singleton i)
78+ sequences
79+ |> List.reduce AsyncSeq.append
80+ |> AsyncSeq.iterAsync ( fun _ -> async.Return())
81+ |> Async.RunSynchronously
82+
83+ /// Benchmarks for computation builder recursive patterns (previously O(n²))
84+ [<MemoryDiagnoser>]
85+ [<SimpleJob( RuntimeMoniker.Net80) >]
86+ type AsyncSeqBuilderBenchmarks () =
87+
88+ [<Params( 50 , 100 , 200 ) >]
89+ member val RecursionDepth = 0 with get, set
90+
91+ /// Benchmark recursive asyncSeq computation - tests for O(n²) regression
92+ [<Benchmark>]
93+ member this.RecursiveAsyncSeq () =
94+ let rec generate cnt = asyncSeq {
95+ if cnt = 0 then () else
96+ let! v = async.Return 1
97+ yield v
98+ yield ! generate ( cnt-1 )
99+ }
100+ generate this.RecursionDepth
101+ |> AsyncSeq.iterAsync ( fun _ -> async.Return())
102+ |> Async.RunSynchronously
103+
104+ /// Benchmark unfoldAsync equivalent for comparison
105+ [<Benchmark>]
106+ member this.UnfoldAsyncEquivalent () =
107+ AsyncSeq.unfoldAsync ( fun cnt -> async {
108+ if cnt = 0 then return None
109+ else
110+ let! v = async.Return 1
111+ return Some ( v, cnt - 1 )
112+ }) this.RecursionDepth
113+ |> AsyncSeq.iterAsync ( fun _ -> async.Return())
114+ |> Async.RunSynchronously
115+
116+ /// Entry point for running benchmarks
117+ module AsyncSeqBenchmarkRunner =
118+
119+ [<EntryPoint>]
120+ let Main args =
121+ printfn " AsyncSeq Performance Benchmarks"
122+ printfn " ================================"
123+ printfn " Running comprehensive performance benchmarks to establish baseline metrics"
124+ printfn " and verify fixes for known performance issues (memory leaks, O(n²) patterns)."
125+ printfn " "
126+
127+ let result =
128+ match args |> Array.tryHead with
129+ | Some " core" ->
130+ printfn " Running Core Operations Benchmarks..."
131+ BenchmarkRunner.Run< AsyncSeqCoreBenchmarks>() |> ignore
132+ 0
133+ | Some " append" ->
134+ printfn " Running Append Operations Benchmarks..."
135+ BenchmarkRunner.Run< AsyncSeqAppendBenchmarks>() |> ignore
136+ 0
137+ | Some " builder" ->
138+ printfn " Running Builder Pattern Benchmarks..."
139+ BenchmarkRunner.Run< AsyncSeqBuilderBenchmarks>() |> ignore
140+ 0
141+ | Some " all" | None ->
142+ printfn " Running All Benchmarks..."
143+ BenchmarkRunner.Run< AsyncSeqCoreBenchmarks>() |> ignore
144+ BenchmarkRunner.Run< AsyncSeqAppendBenchmarks>() |> ignore
145+ BenchmarkRunner.Run< AsyncSeqBuilderBenchmarks>() |> ignore
146+ 0
147+ | Some suite ->
148+ printfn " Unknown benchmark suite: %s " suite
149+ printfn " Available suites: core, append, builder, all"
150+ 1
151+
152+ printfn " "
153+ printfn " Benchmarks completed. Results provide baseline performance metrics"
154+ printfn " for future performance improvements and regression detection."
155+ result
0 commit comments