Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions release-notes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Release notes:

1.0.0
- 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
- perf: simplify iter, fold, reduce, mapFold, tryLast, skipOrTake (Drop/Truncate) to use while! and remove manual go-flag and initial MoveNextAsync pre-advance, matching the pattern already used by sum/sumBy/average
- perf: TaskSeq.chunkBy and chunkByAsync reuse the ResizeArray buffer between chunks, reducing allocations on sequences with many chunk boundaries
- fixes: TaskSeq.insertAt, insertManyAt, removeAt, removeManyAt, updateAt now raise ArgumentNullException (not NullReferenceException) when given a null source; insertManyAt also validates the values argument
- refactor: simplify lengthBy and lengthBeforeMax to use while! and remove the redundant mutable 'go' and initial MoveNextAsync
Expand Down
113 changes: 34 additions & 79 deletions src/FSharp.Control.TaskSeq/TaskSeqInternal.fs
Original file line number Diff line number Diff line change
Expand Up @@ -357,68 +357,49 @@ module internal TaskSeqInternal =

task {
use e = source.GetAsyncEnumerator CancellationToken.None
let mutable go = true
let! step = e.MoveNextAsync()
go <- step

// this ensures that the inner loop is optimized for the closure
// though perhaps we need to split into individual functions after all to use
// InlineIfLambda?
// Each branch keeps its own while! loop so the match dispatch is hoisted out and
// the JIT sees a tight, single-case loop (same pattern as sum/sumBy etc.).
match action with
| CountableAction action ->
let mutable i = 0

while go do
do action i e.Current
let! step = e.MoveNextAsync()
while! e.MoveNextAsync() do
action i e.Current
i <- i + 1
go <- step

| SimpleAction action ->
while go do
do action e.Current
let! step = e.MoveNextAsync()
go <- step
while! e.MoveNextAsync() do
action e.Current

| AsyncCountableAction action ->
let mutable i = 0

while go do
while! e.MoveNextAsync() do
do! action i e.Current
let! step = e.MoveNextAsync()
i <- i + 1
go <- step

| AsyncSimpleAction action ->
while go do
while! e.MoveNextAsync() do
do! action e.Current
let! step = e.MoveNextAsync()
go <- step
}

let fold folder initial (source: TaskSeq<_>) =
checkNonNull (nameof source) source

task {
use e = source.GetAsyncEnumerator CancellationToken.None
let mutable go = true
let mutable result = initial
let! step = e.MoveNextAsync()
go <- step

match folder with
| FolderAction folder ->
while go do
while! e.MoveNextAsync() do
result <- folder result e.Current
let! step = e.MoveNextAsync()
go <- step

| AsyncFolderAction folder ->
while go do
while! e.MoveNextAsync() do
let! tempResult = folder result e.Current
result <- tempResult
let! step = e.MoveNextAsync()
go <- step

return result
}
Expand Down Expand Up @@ -457,22 +438,16 @@ module internal TaskSeqInternal =
raiseEmptySeq ()

let mutable result = e.Current
let! step = e.MoveNextAsync()
let mutable go = step

match folder with
| FolderAction folder ->
while go do
while! e.MoveNextAsync() do
result <- folder result e.Current
let! step = e.MoveNextAsync()
go <- step

| AsyncFolderAction folder ->
while go do
while! e.MoveNextAsync() do
let! tempResult = folder result e.Current
result <- tempResult
let! step = e.MoveNextAsync()
go <- step

return result
}
Expand All @@ -482,28 +457,21 @@ module internal TaskSeqInternal =

task {
use e = source.GetAsyncEnumerator CancellationToken.None
let mutable go = true
let mutable state = initial
let results = ResizeArray()
let! step = e.MoveNextAsync()
go <- step

match folder with
| MapFolderAction folder ->
while go do
while! e.MoveNextAsync() do
let result, newState = folder state e.Current
results.Add result
state <- newState
let! step = e.MoveNextAsync()
go <- step

| AsyncMapFolderAction folder ->
while go do
while! e.MoveNextAsync() do
let! (result, newState) = folder state e.Current
results.Add result
state <- newState
let! step = e.MoveNextAsync()
go <- step

return results.ToArray(), state
}
Expand Down Expand Up @@ -804,15 +772,10 @@ module internal TaskSeqInternal =

task {
use e = source.GetAsyncEnumerator CancellationToken.None
let mutable go = true
let mutable last = ValueNone
let! step = e.MoveNextAsync()
go <- step

while go do
while! e.MoveNextAsync() do
last <- ValueSome e.Current
let! step = e.MoveNextAsync()
go <- step

match last with
| ValueSome value -> return Some value
Expand Down Expand Up @@ -1233,24 +1196,19 @@ module internal TaskSeqInternal =
else
taskSeq {
use e = source.GetAsyncEnumerator CancellationToken.None
let mutable i = 0
let mutable cont = true

let! step = e.MoveNextAsync()
let mutable cont = step
let mutable pos = 0

// skip, or stop looping if we reached the end
while cont do
pos <- pos + 1

if pos < count then
let! moveNext = e.MoveNextAsync()
cont <- moveNext
else
cont <- false
// advance past 'count' elements; stop early if the source is shorter
while cont && i < count do
let! hasMore = e.MoveNextAsync()
if hasMore then i <- i + 1 else cont <- false

// return the rest
while! e.MoveNextAsync() do
yield e.Current
// return remaining elements; enumerator is at element (count-1) so one
// more MoveNext is needed to reach element (count)
if cont then
while! e.MoveNextAsync() do
yield e.Current

}
| Take ->
Expand All @@ -1277,19 +1235,16 @@ module internal TaskSeqInternal =
else
taskSeq {
use e = source.GetAsyncEnumerator CancellationToken.None
let mutable yielded = 0
let mutable cont = true

let! step = e.MoveNextAsync()
let mutable cont = step
let mutable pos = 0

// return items until we've exhausted the seq
while cont do
yield e.Current
pos <- pos + 1
// yield up to 'count' elements; stop when exhausted or limit reached
while cont && yielded < count do
let! hasMore = e.MoveNextAsync()

if pos < count then
let! moveNext = e.MoveNextAsync()
cont <- moveNext
if hasMore then
yield e.Current
yielded <- yielded + 1
else
cont <- false

Expand Down