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

Use CollectionsMarshal.SetCount in LINQ to deduplicate ToArray/ToList implementations #85288

Merged
merged 2 commits into from
Apr 25, 2023
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
11 changes: 11 additions & 0 deletions src/libraries/System.Linq/src/System/Linq/Enumerable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ public static partial class Enumerable
{
public static IEnumerable<TSource> AsEnumerable<TSource>(this IEnumerable<TSource> source) => source;

/// <summary>
/// Sets the <paramref name="list"/>'s <see cref="List{T}.Count"/> to be <paramref name="count"/>
/// and returns the relevant portion of the list's backing array as a span.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Span<T> SetCountAndGetSpan<T>(List<T> list, int count)
{
CollectionsMarshal.SetCount(list, count);
return CollectionsMarshal.AsSpan(list);
}

/// <summary>Validates that source is not null and then tries to extract a span from the source.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] // fast type checks that don't add a lot of overhead
private static bool TryGetSpan<TSource>(this IEnumerable<TSource> source, out ReadOnlySpan<TSource> span)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,7 @@ public virtual TElement[] ToArray()
}

TElement[] array = new TElement[count];
int[] map = SortedMap(buffer);
for (int i = 0; i < array.Length; i++)
{
array[i] = buffer._items[map[i]];
}

Fill(buffer, array);
return array;
}

Expand All @@ -36,16 +31,21 @@ public virtual List<TElement> ToList()
List<TElement> list = new List<TElement>(count);
if (count > 0)
{
int[] map = SortedMap(buffer);
for (int i = 0; i != count; i++)
{
list.Add(buffer._items[map[i]]);
}
Fill(buffer, Enumerable.SetCountAndGetSpan(list, count));
}

return list;
}

private void Fill(Buffer<TElement> buffer, Span<TElement> destination)
{
int[] map = SortedMap(buffer);
for (int i = 0; i < destination.Length; i++)
{
destination[i] = buffer._items[map[i]];
}
}

public int GetCount(bool onlyIfCheap)
{
if (_source is IIListProvider<TElement> listProv)
Expand Down Expand Up @@ -75,15 +75,9 @@ internal TElement[] ToArray(int minIdx, int maxIdx)
return new TElement[] { GetEnumerableSorter().ElementAt(buffer._items, count, minIdx) };
}

int[] map = SortedMap(buffer, minIdx, maxIdx);
TElement[] array = new TElement[maxIdx - minIdx + 1];
int idx = 0;
while (minIdx <= maxIdx)
{
array[idx] = buffer._items[map[minIdx]];
++idx;
++minIdx;
}

Fill(minIdx, maxIdx, buffer, array);

return array;
}
Expand All @@ -107,15 +101,21 @@ internal List<TElement> ToList(int minIdx, int maxIdx)
return new List<TElement>(1) { GetEnumerableSorter().ElementAt(buffer._items, count, minIdx) };
}

int[] map = SortedMap(buffer, minIdx, maxIdx);
List<TElement> list = new List<TElement>(maxIdx - minIdx + 1);
Fill(minIdx, maxIdx, buffer, Enumerable.SetCountAndGetSpan(list, maxIdx - minIdx + 1));
return list;
}

private void Fill(int minIdx, int maxIdx, Buffer<TElement> buffer, Span<TElement> destination)
{
int[] map = SortedMap(buffer, minIdx, maxIdx);
int idx = 0;
while (minIdx <= maxIdx)
{
list.Add(buffer._items[map[minIdx]]);
destination[idx] = buffer._items[map[minIdx]];
++idx;
++minIdx;
}

return list;
}

internal int GetCount(int minIdx, int maxIdx, bool onlyIfCheap)
Expand Down
19 changes: 9 additions & 10 deletions src/libraries/System.Linq/src/System/Linq/Partition.SpeedOpt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,11 +254,7 @@ public TSource[] ToArray()
}

TSource[] array = new TSource[count];
for (int i = 0, curIdx = _minIndexInclusive; i < array.Length; ++i, ++curIdx)
{
array[i] = _source[curIdx];
}

Fill(_source, array, _minIndexInclusive);
return array;
}

Expand All @@ -271,13 +267,16 @@ public List<TSource> ToList()
}

List<TSource> list = new List<TSource>(count);
int end = _minIndexInclusive + count;
for (int i = _minIndexInclusive; i != end; ++i)
Fill(_source, SetCountAndGetSpan(list, count), _minIndexInclusive);
return list;
}

private static void Fill(IList<TSource> source, Span<TSource> destination, int sourceIndex)
{
for (int i = 0; i < destination.Length; i++, sourceIndex++)
{
list.Add(_source[i]);
destination[i] = source[sourceIndex];
}

return list;
}

public int GetCount(bool onlyIfCheap) => Count;
Expand Down
20 changes: 9 additions & 11 deletions src/libraries/System.Linq/src/System/Linq/Range.SpeedOpt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,23 @@ public override IEnumerable<TResult> Select<TResult>(Func<int, TResult> selector
public int[] ToArray()
{
int[] array = new int[_end - _start];
int cur = _start;
for (int i = 0; i < array.Length; ++i)
{
array[i] = cur;
++cur;
}

Fill(array, _start);
return array;
}

public List<int> ToList()
{
List<int> list = new List<int>(_end - _start);
for (int cur = _start; cur != _end; cur++)
Fill(SetCountAndGetSpan(list, _end - _start), _start);
return list;
}

private static void Fill(Span<int> destination, int value)
{
for (int i = 0; i < destination.Length; i++, value++)
{
list.Add(cur);
destination[i] = value;
}

return list;
}

public int GetCount(bool onlyIfCheap) => unchecked(_end - _start);
Expand Down
5 changes: 1 addition & 4 deletions src/libraries/System.Linq/src/System/Linq/Repeat.SpeedOpt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,7 @@ public TResult[] ToArray()
public List<TResult> ToList()
{
List<TResult> list = new List<TResult>(_count);
for (int i = 0; i != _count; ++i)
{
list.Add(_current);
}
SetCountAndGetSpan(list, _count).Fill(_current);
stephentoub marked this conversation as resolved.
Show resolved Hide resolved

return list;
}
Expand Down
Loading