Skip to content

Commit 5f8b745

Browse files
authored
Merge pull request #200 from fsprojects/daily-perf-improver/optimize-iterasync-performance
Daily Perf Improver: Optimize iterAsync and iteriAsync for better performance
2 parents 7978074 + ce2d23b commit 5f8b745

File tree

1 file changed

+51
-10
lines changed

1 file changed

+51
-10
lines changed

src/FSharp.Control.AsyncSeq/AsyncSeq.fs

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -734,23 +734,64 @@ module AsyncSeq =
734734
dispose e
735735
| _ -> () } }
736736

737+
// Optimized iterAsync implementation to reduce allocations
738+
type internal OptimizedIterAsyncEnumerator<'T>(enumerator: IAsyncEnumerator<'T>, f: 'T -> Async<unit>) =
739+
let mutable disposed = false
740+
741+
member _.IterateAsync() =
742+
let rec loop() = async {
743+
let! next = enumerator.MoveNext()
744+
match next with
745+
| Some value ->
746+
do! f value
747+
return! loop()
748+
| None -> return ()
749+
}
750+
loop()
751+
752+
interface IDisposable with
753+
member _.Dispose() =
754+
if not disposed then
755+
disposed <- true
756+
enumerator.Dispose()
757+
758+
// Optimized iteriAsync implementation with direct tail recursion
759+
type internal OptimizedIteriAsyncEnumerator<'T>(enumerator: IAsyncEnumerator<'T>, f: int -> 'T -> Async<unit>) =
760+
let mutable disposed = false
761+
762+
member _.IterateAsync() =
763+
let rec loop count = async {
764+
let! next = enumerator.MoveNext()
765+
match next with
766+
| Some value ->
767+
do! f count value
768+
return! loop (count + 1)
769+
| None -> return ()
770+
}
771+
loop 0
772+
773+
interface IDisposable with
774+
member _.Dispose() =
775+
if not disposed then
776+
disposed <- true
777+
enumerator.Dispose()
778+
737779
let iteriAsync f (source : AsyncSeq<_>) =
738780
async {
739-
use ie = source.GetEnumerator()
740-
let count = ref 0
741-
let! move = ie.MoveNext()
742-
let b = ref move
743-
while b.Value.IsSome do
744-
do! f !count b.Value.Value
745-
let! moven = ie.MoveNext()
746-
do incr count
747-
b := moven
781+
let enum = source.GetEnumerator()
782+
use optimizer = new OptimizedIteriAsyncEnumerator<_>(enum, f)
783+
return! optimizer.IterateAsync()
748784
}
749785

750786
let iterAsync (f: 'T -> Async<unit>) (source: AsyncSeq<'T>) =
751787
match source with
752788
| :? AsyncSeqOp<'T> as source -> source.IterAsync f
753-
| _ -> iteriAsync (fun i x -> f x) source
789+
| _ ->
790+
async {
791+
let enum = source.GetEnumerator()
792+
use optimizer = new OptimizedIterAsyncEnumerator<_>(enum, f)
793+
return! optimizer.IterateAsync()
794+
}
754795

755796
let iteri (f: int -> 'T -> unit) (inp: AsyncSeq<'T>) = iteriAsync (fun i x -> async.Return (f i x)) inp
756797

0 commit comments

Comments
 (0)