Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SequenceReader performance; elide bounds checks and book-keeping #82765

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
580a164
SequenceReader; elide bounds checks by using ref-oriented approach
mgravell Feb 28, 2023
dfe7241
net6-: don't forget to increment _consumedAtStartOfCurrentSpan
mgravell Feb 28, 2023
481b49c
- don't use the Unsafe or MemoryMarshal evilness
mgravell Mar 1, 2023
a5bf93d
in search, avoid copying out the T value
mgravell Mar 1, 2023
836bc9c
drop unused method
mgravell Mar 1, 2023
4e8d89c
reinstate next position; needed to avoid double-fetch in GetNextSpan
mgravell Mar 1, 2023
f359dd0
use better API to get first span in ResetReader
mgravell Mar 1, 2023
454315e
simplify Position getter
mgravell Mar 1, 2023
d001302
move reset logic to constructor; use constructor in ResetReader
mgravell Mar 1, 2023
4ba4be6
always track consumed span when moving to next, even for single segment
mgravell Mar 1, 2023
ec519f7
for single-segment cases, length is trivial; record length at EOF; up…
mgravell Mar 1, 2023
f6d17c4
use named parameter to clarify meaning
mgravell Mar 1, 2023
7a17388
explicit types (IDE0008)
mgravell Mar 1, 2023
4781155
- fix increment bug at end of single-segment buffers
mgravell Mar 2, 2023
e49b863
simplify advance further
mgravell Mar 2, 2023
11a7b46
remove debug-check from AssertValidPosition() to make the compiler ha…
mgravell Mar 2, 2023
570d221
TryPeek/TryRead: avoid bounds check by hoisting into locals
mgravell Mar 2, 2023
c1c6d59
nit: use local for the addition too (cuts a `mov` from the JIT output)
mgravell Mar 2, 2023
594f879
test fixes
mgravell Mar 2, 2023
892a9e7
Advance: don't short-circuit (we expect both sides to pass)
mgravell Mar 3, 2023
eeb4bff
work around JIT regressions
mgravell Mar 3, 2023
0412ab1
nit: TryGetNextBuffer - defer updating result; empty segments are v.rare
mgravell Mar 7, 2023
e825679
Fix two rebase errors
jozkee Aug 17, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public bool TryReadTo(out ReadOnlySpan<T> span, T delimiter, bool advancePastDel

private bool TryReadToSlow(out ReadOnlySpan<T> span, T delimiter, bool advancePastDelimiter)
{
if (!TryReadToInternal(out ReadOnlySequence<T> sequence, delimiter, advancePastDelimiter, CurrentSpan.Length - CurrentSpanIndex))
if (!TryReadToInternal(out ReadOnlySequence<T> sequence, delimiter, advancePastDelimiter, _currentSpan.Length - _currentSpanIndex))
{
span = default;
return false;
Expand Down Expand Up @@ -132,7 +132,7 @@ private bool TryReadToSlow(out ReadOnlySequence<T> sequence, T delimiter, T deli
// Found the delimiter. Move to it, slice, then move past it.
AdvanceCurrentSpan(index);

sequence = Sequence.Slice(copy.Position, Position);
sequence = _sequence.Slice(copy.Position, Position);
if (advancePastDelimiter)
{
Advance(1);
Expand Down Expand Up @@ -198,7 +198,7 @@ private bool TryReadToInternal(out ReadOnlySequence<T> sequence, T delimiter, bo
Advance(skip);
ReadOnlySpan<T> remaining = UnreadSpan;

while (_moreData)
while (!End)
{
int index = remaining.IndexOf(delimiter);
if (index != -1)
Expand All @@ -209,7 +209,7 @@ private bool TryReadToInternal(out ReadOnlySequence<T> sequence, T delimiter, bo
AdvanceCurrentSpan(index);
}

sequence = Sequence.Slice(copy.Position, Position);
sequence = _sequence.Slice(copy.Position, Position);
if (advancePastDelimiter)
{
Advance(1);
Expand Down Expand Up @@ -243,7 +243,7 @@ public bool TryReadTo(out ReadOnlySequence<T> sequence, T delimiter, T delimiter
ReadOnlySpan<T> remaining = UnreadSpan;
bool priorEscape = false;

while (_moreData)
while (!End)
{
int index = remaining.IndexOf(delimiter);
if (index != -1)
Expand Down Expand Up @@ -286,7 +286,7 @@ public bool TryReadTo(out ReadOnlySequence<T> sequence, T delimiter, T delimiter
Advance(index);
}

sequence = Sequence.Slice(copy.Position, Position);
sequence = _sequence.Slice(copy.Position, Position);
if (advancePastDelimiter)
{
Advance(1);
Expand Down Expand Up @@ -344,7 +344,7 @@ public bool TryReadToAny(out ReadOnlySpan<T> span, scoped ReadOnlySpan<T> delimi

private bool TryReadToAnySlow(out ReadOnlySpan<T> span, scoped ReadOnlySpan<T> delimiters, bool advancePastDelimiter)
{
if (!TryReadToAnyInternal(out ReadOnlySequence<T> sequence, delimiters, advancePastDelimiter, CurrentSpan.Length - CurrentSpanIndex))
if (!TryReadToAnyInternal(out ReadOnlySequence<T> sequence, delimiters, advancePastDelimiter, _currentSpan.Length - _currentSpanIndex))
{
span = default;
return false;
Expand Down Expand Up @@ -387,7 +387,7 @@ private bool TryReadToAnyInternal(out ReadOnlySequence<T> sequence, scoped ReadO
AdvanceCurrentSpan(index);
}

sequence = Sequence.Slice(copy.Position, Position);
sequence = _sequence.Slice(copy.Position, Position);
if (advancePastDelimiter)
{
Advance(1);
Expand Down Expand Up @@ -481,7 +481,7 @@ public bool TryReadTo(out ReadOnlySequence<T> sequence, scoped ReadOnlySpan<T> d
// Probably a faster way to do this, potentially by avoiding the Advance in the previous TryReadTo call
if (advanced)
{
sequence = copy.Sequence.Slice(copy.Consumed, Consumed - copy.Consumed);
sequence = copy._sequence.Slice(copy.Consumed, Consumed - copy.Consumed);
}

if (advancePastDelimiter)
Expand Down Expand Up @@ -520,7 +520,7 @@ public bool TryReadExact(int count, out ReadOnlySequence<T> sequence)
return false;
}

sequence = Sequence.Slice(Position, count);
sequence = _sequence.Slice(Position, count);
if (count != 0)
{
Advance(count);
Expand Down Expand Up @@ -578,22 +578,22 @@ public long AdvancePast(T value)
{
// Advance past all matches in the current span
int i;
for (i = CurrentSpanIndex; i < CurrentSpan.Length && CurrentSpan[i].Equals(value); i++)
ReadOnlySpan<T> unread = UnreadSpan; // eat the slice to avoid bounds checks
for (i = 0; i < unread.Length && unread[i].Equals(value); i++)
{
}

int advanced = i - CurrentSpanIndex;
if (advanced == 0)
if (i == 0)
{
// Didn't advance at all in this span, exit.
break;
}

AdvanceCurrentSpan(advanced);
AdvanceCurrentSpan(i);

// If we're at position 0 after advancing and not at the End,
// we're in a new span and should continue the loop.
} while (CurrentSpanIndex == 0 && !End);
} while (_currentSpanIndex == 0 && !End);

return Consumed - start;
}
Expand All @@ -610,22 +610,22 @@ public long AdvancePastAny(scoped ReadOnlySpan<T> values)
{
// Advance past all matches in the current span
int i;
for (i = CurrentSpanIndex; i < CurrentSpan.Length && values.IndexOf(CurrentSpan[i]) != -1; i++)
ReadOnlySpan<T> unread = UnreadSpan; // eat the slice to avoid bounds checks
for (i = 0; i < unread.Length && values.IndexOf(unread[i]) != -1; i++)
{
}

int advanced = i - CurrentSpanIndex;
if (advanced == 0)
if (i == 0)
{
// Didn't advance at all in this span, exit.
break;
}

AdvanceCurrentSpan(advanced);
AdvanceCurrentSpan(i);

// If we're at position 0 after advancing and not at the End,
// we're in a new span and should continue the loop.
} while (CurrentSpanIndex == 0 && !End);
} while (_currentSpanIndex == 0 && !End);

return Consumed - start;
}
Expand All @@ -642,27 +642,27 @@ public long AdvancePastAny(T value0, T value1, T value2, T value3)
{
// Advance past all matches in the current span
int i;
for (i = CurrentSpanIndex; i < CurrentSpan.Length; i++)
ReadOnlySpan<T> unread = UnreadSpan; // eat the slice to avoid bounds checks
for (i = 0; i < unread.Length; i++)
{
T value = CurrentSpan[i];
ref readonly T value = ref unread[i];
if (!value.Equals(value0) && !value.Equals(value1) && !value.Equals(value2) && !value.Equals(value3))
{
break;
}
}

int advanced = i - CurrentSpanIndex;
if (advanced == 0)
if (i == 0)
{
// Didn't advance at all in this span, exit.
break;
}

AdvanceCurrentSpan(advanced);
AdvanceCurrentSpan(i);

// If we're at position 0 after advancing and not at the End,
// we're in a new span and should continue the loop.
} while (CurrentSpanIndex == 0 && !End);
} while (_currentSpanIndex == 0 && !End);

return Consumed - start;
}
Expand All @@ -679,27 +679,27 @@ public long AdvancePastAny(T value0, T value1, T value2)
{
// Advance past all matches in the current span
int i;
for (i = CurrentSpanIndex; i < CurrentSpan.Length; i++)
ReadOnlySpan<T> unread = UnreadSpan; // eat the slice to avoid bounds checks
for (i = 0; i < unread.Length; i++)
{
T value = CurrentSpan[i];
ref readonly T value = ref unread[i];
if (!value.Equals(value0) && !value.Equals(value1) && !value.Equals(value2))
{
break;
}
}

int advanced = i - CurrentSpanIndex;
if (advanced == 0)
if (i == 0)
{
// Didn't advance at all in this span, exit.
break;
}

AdvanceCurrentSpan(advanced);
AdvanceCurrentSpan(i);

// If we're at position 0 after advancing and not at the End,
// we're in a new span and should continue the loop.
} while (CurrentSpanIndex == 0 && !End);
} while (_currentSpanIndex == 0 && !End);

return Consumed - start;
}
Expand All @@ -716,27 +716,27 @@ public long AdvancePastAny(T value0, T value1)
{
// Advance past all matches in the current span
int i;
for (i = CurrentSpanIndex; i < CurrentSpan.Length; i++)
ReadOnlySpan<T> unread = UnreadSpan; // eat the slice to avoid bounds checks
for (i = 0; i < unread.Length; i++)
{
T value = CurrentSpan[i];
ref readonly T value = ref unread[i];
if (!value.Equals(value0) && !value.Equals(value1))
{
break;
}
}

int advanced = i - CurrentSpanIndex;
if (advanced == 0)
if (i == 0)
{
// Didn't advance at all in this span, exit.
break;
}

AdvanceCurrentSpan(advanced);
AdvanceCurrentSpan(i);

// If we're at position 0 after advancing and not at the End,
// we're in a new span and should continue the loop.
} while (CurrentSpanIndex == 0 && !End);
} while (_currentSpanIndex == 0 && !End);

return Consumed - start;
}
Expand All @@ -746,14 +746,18 @@ public long AdvancePastAny(T value0, T value1)
/// </summary>
public void AdvanceToEnd()
{
if (_moreData)
AssertValidPosition();
if (!End)
{
Consumed = Length;
CurrentSpan = default;
CurrentSpanIndex = 0;
_currentPosition = Sequence.End;
_nextPosition = default;
_moreData = false;
_consumedAtStartOfCurrentSpan = Length;
_currentSpanIndex = 0;
_currentSpan = default;

SequencePosition position = _sequence.End;
_currentPositionObject = position.GetObject();
_currentPositionInteger = position.GetInteger();

AssertValidPosition();
}
}

Expand All @@ -768,7 +772,7 @@ public bool IsNext(T next, bool advancePast = false)
if (End)
return false;

if (CurrentSpan[CurrentSpanIndex].Equals(next))
if (_currentSpan[_currentSpanIndex].Equals(next))
{
if (advancePast)
{
Expand Down Expand Up @@ -809,7 +813,7 @@ private bool IsNextSlow(scoped ReadOnlySpan<T> next, bool advancePast)
Debug.Assert(currentSpan.Length < next.Length);

int fullLength = next.Length;
SequencePosition nextPosition = _nextPosition;
object? segment = _currentPositionObject;

while (next.StartsWith(currentSpan))
{
Expand All @@ -823,25 +827,19 @@ private bool IsNextSlow(scoped ReadOnlySpan<T> next, bool advancePast)
return true;
}

// move past the values we have validated
next = next.Slice(currentSpan.Length);

// Need to check the next segment
while (true)
if (TryGetNextBuffer(in _sequence, ref segment, ref currentSpan) != TryGetNextBufferResult.SuccessHaveData)
{
if (!Sequence.TryGet(ref nextPosition, out ReadOnlyMemory<T> nextSegment, advance: true))
{
// Nothing left
return false;
}
// Nothing left
return false;
}

if (nextSegment.Length > 0)
{
next = next.Slice(currentSpan.Length);
currentSpan = nextSegment.Span;
if (currentSpan.Length > next.Length)
{
currentSpan = currentSpan.Slice(0, next.Length);
}
break;
}
if (currentSpan.Length > next.Length)
{
currentSpan = currentSpan.Slice(0, next.Length);
}
}

Expand Down
Loading