Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
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
Prev Previous commit
Derive OrderedEnumerable from Iterator
The OrderedEnumerable implementation today doesn't derive from Iterator, and it has a GetEnumerator method implemented with yield. That means the optimization that allows the enumerable to be reused as the enumerator doesn't kick in, and we end up allocating a separate enumerator object in order to iterate through an Order{By} enumerable.
  • Loading branch information
stephentoub committed Feb 23, 2024
commit 4e350d92f1aba8338362cfffe77151024451088f
109 changes: 87 additions & 22 deletions src/libraries/System.Linq/src/System/Linq/OrderedEnumerable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,17 @@ namespace System.Linq
{
public static partial class Enumerable
{
internal abstract partial class OrderedEnumerable<TElement> : IOrderedEnumerable<TElement>
internal abstract partial class OrderedEnumerable<TElement> : Iterator<TElement>, IOrderedEnumerable<TElement>
{
internal IEnumerable<TElement> _source;
internal readonly IEnumerable<TElement> _source;

protected OrderedEnumerable(IEnumerable<TElement> source) => _source = source;

private int[] SortedMap(TElement[] buffer) => GetEnumerableSorter().Sort(buffer, buffer.Length);
private protected int[] SortedMap(TElement[] buffer) => GetEnumerableSorter().Sort(buffer, buffer.Length);

private int[] SortedMap(TElement[] buffer, int minIdx, int maxIdx) =>
GetEnumerableSorter().Sort(buffer, buffer.Length, minIdx, maxIdx);

public virtual IEnumerator<TElement> GetEnumerator()
{
TElement[] buffer = _source.ToArray();
if (buffer.Length > 0)
{
int[] map = SortedMap(buffer);
for (int i = 0; i < buffer.Length; i++)
{
yield return buffer[map[i]];
}
}
}

internal IEnumerator<TElement> GetEnumerator(int minIdx, int maxIdx)
{
TElement[] buffer = _source.ToArray();
Expand Down Expand Up @@ -111,6 +98,8 @@ internal sealed partial class OrderedEnumerable<TElement, TKey> : OrderedEnumera
private readonly Func<TElement, TKey> _keySelector;
private readonly IComparer<TKey> _comparer;
private readonly bool _descending;
private TElement[]? _buffer;
private int[]? _map;

internal OrderedEnumerable(IEnumerable<TElement> source, Func<TElement, TKey> keySelector, IComparer<TKey>? comparer, bool descending, OrderedEnumerable<TElement>? parent) :
base(source)
Expand All @@ -130,6 +119,8 @@ internal OrderedEnumerable(IEnumerable<TElement> source, Func<TElement, TKey> ke
_descending = descending;
}

public override Iterator<TElement> Clone() => new OrderedEnumerable<TElement, TKey>(_source, _keySelector, _comparer, _descending, _parent);

internal override EnumerableSorter<TElement> GetEnumerableSorter(EnumerableSorter<TElement>? next)
{
// Special case the common use of string with default comparer. Comparer<string>.Default checks the
Expand Down Expand Up @@ -157,12 +148,56 @@ internal override CachingComparer<TElement> GetComparer(CachingComparer<TElement
: new CachingComparerWithChild<TElement, TKey>(_keySelector, _comparer, _descending, childComparer);
return _parent != null ? _parent.GetComparer(cmp) : cmp;
}

public override bool MoveNext()
{
int state = _state;

Initialized:
if (state > 1)
{
Debug.Assert(_buffer is not null);
Debug.Assert(_map is not null);
Debug.Assert(_map.Length == _buffer.Length);

int[] map = _map;
int i = state - 2;
if ((uint)i < (uint)map.Length)
{
_current = _buffer[map[i]];
_state++;
return true;
}
}
else if (state == 1)
{
TElement[] buffer = _source.ToArray();
if (buffer.Length != 0)
{
_map = SortedMap(buffer);
_buffer = buffer;
_state = state = 2;
goto Initialized;
}
}

Dispose();
return false;
}

public override void Dispose()
{
_buffer = null;
_map = null;
base.Dispose();
}
}

/// <summary>An ordered enumerable used by Order/OrderDescending for Ts that are bitwise indistinguishable for any considered equal.</summary>
internal sealed partial class OrderedImplicitlyStableEnumerable<TElement> : OrderedEnumerable<TElement>
{
private readonly bool _descending;
private TElement[]? _buffer;

public OrderedImplicitlyStableEnumerable(IEnumerable<TElement> source, bool descending) : base(source)
{
Expand All @@ -176,6 +211,8 @@ public OrderedImplicitlyStableEnumerable(IEnumerable<TElement> source, bool desc
_descending = descending;
}

public override Iterator<TElement> Clone() => new OrderedImplicitlyStableEnumerable<TElement>(_source, _descending);

internal override CachingComparer<TElement> GetComparer(CachingComparer<TElement>? childComparer) =>
childComparer == null ?
new CachingComparer<TElement, TElement>(EnumerableSorter<TElement>.IdentityFunc, Comparer<TElement>.Default, _descending) :
Expand All @@ -184,17 +221,45 @@ internal override CachingComparer<TElement> GetComparer(CachingComparer<TElement
internal override EnumerableSorter<TElement> GetEnumerableSorter(EnumerableSorter<TElement>? next) =>
new EnumerableSorter<TElement, TElement>(EnumerableSorter<TElement>.IdentityFunc, Comparer<TElement>.Default, _descending, next);

public override IEnumerator<TElement> GetEnumerator()
public override bool MoveNext()
{
TElement[] buffer = _source.ToArray();
if (buffer.Length > 0)
int state = _state;
TElement[]? buffer;

Initialized:
if (state > 1)
{
Sort(buffer, _descending);
for (int i = 0; i < buffer.Length; i++)
buffer = _buffer;
Debug.Assert(buffer is not null);

int i = state - 2;
if ((uint)i < (uint)buffer.Length)
{
_current = buffer[i];
_state++;
return true;
}
}
else if (state == 1)
{
buffer = _source.ToArray();
if (buffer.Length != 0)
{
yield return buffer[i];
Sort(buffer, _descending);
_buffer = buffer;
_state = state = 2;
goto Initialized;
}
}

Dispose();
return false;
}

public override void Dispose()
{
_buffer = null;
base.Dispose();
}

private static void Sort(Span<TElement> span, bool descending)
Expand Down