Skip to content

Commit 48ab97d

Browse files
perf: direct loop in toResizeArrayAsync; simplify tryItem loop
toResizeArrayAsync previously went through the iter function with a SimpleAction DU wrapper, causing a lambda closure allocation and discriminated-union wrapping on every call. The new direct loop eliminates these allocations and removes an extra layer of indirection. This benefits toArrayAsync, toListAsync, toResizeArrayAsync, and toIListAsync which all route through toResizeArrayAsync. tryItem previously used a while-loop with condition idx <= index and an inner if idx = index check on every iteration. The refactored version advances with idx < index and captures the current element after the loop, removing the redundant inner comparison from the hot path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 4873e88 commit 48ab97d

File tree

2 files changed

+17
-11
lines changed

2 files changed

+17
-11
lines changed

release-notes.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ Release notes:
33

44
1.0.0
55
- adds taskSeqDynamic computation expression and TaskSeqDynamic/TaskSeqDynamicInfo types for dynamic (FSI-compatible) resumable code, fixing issue where taskSeq would raise NotImplementedException in F# Interactive, #246
6+
- perf: toResizeArrayAsync (and therefore toArrayAsync, toListAsync, toResizeArrayAsync, toIListAsync) uses a direct loop instead of going through iter, avoiding a lambda and DU allocation per call
7+
- perf: tryItem uses a simpler loop that skips the redundant inner index check on every iteration
68
- perf: TaskSeq.chunkBy and chunkByAsync reuse the ResizeArray buffer between chunks, reducing allocations on sequences with many chunk boundaries
79
- fixes: TaskSeq.insertAt, insertManyAt, removeAt, removeManyAt, updateAt now raise ArgumentNullException (not NullReferenceException) when given a null source; insertManyAt also validates the values argument
810
- refactor: simplify lengthBy and lengthBeforeMax to use while! and remove the redundant mutable 'go' and initial MoveNextAsync

src/FSharp.Control.TaskSeq/TaskSeqInternal.fs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -532,12 +532,16 @@ module internal TaskSeqInternal =
532532
yield result
533533
}
534534

535-
let toResizeArrayAsync source =
535+
let toResizeArrayAsync (source: TaskSeq<'T>) =
536536
checkNonNull (nameof source) source
537537

538538
task {
539-
let res = ResizeArray()
540-
do! source |> iter (SimpleAction(fun item -> res.Add item))
539+
let res = ResizeArray<'T>()
540+
use e = source.GetAsyncEnumerator CancellationToken.None
541+
542+
while! e.MoveNextAsync() do
543+
res.Add e.Current
544+
541545
return res
542546
}
543547

@@ -915,14 +919,14 @@ module internal TaskSeqInternal =
915919
let! step = e.MoveNextAsync()
916920
go <- step
917921

918-
while go && idx <= index do
919-
if idx = index then
920-
foundItem <- Some e.Current
921-
go <- false
922-
else
923-
let! step = e.MoveNextAsync()
924-
go <- step
925-
idx <- idx + 1
922+
// advance past the first `index` elements, then capture the current element
923+
while go && idx < index do
924+
let! step = e.MoveNextAsync()
925+
go <- step
926+
idx <- idx + 1
927+
928+
if go then
929+
foundItem <- Some e.Current
926930

927931
return foundItem
928932
}

0 commit comments

Comments
 (0)