diff --git a/src/System.Linq/src/System/Linq/Iterator.cs b/src/System.Linq/src/System/Linq/Iterator.cs index 7425046e2648..421b3efd7906 100644 --- a/src/System.Linq/src/System/Linq/Iterator.cs +++ b/src/System.Linq/src/System/Linq/Iterator.cs @@ -11,7 +11,7 @@ public static partial class Enumerable { internal abstract class Iterator : IEnumerable, IEnumerator { - private readonly int _threadId; + protected readonly int _threadId; internal int _state; internal TSource _current; diff --git a/src/System.Linq/src/System/Linq/OrderedEnumerable.cs b/src/System.Linq/src/System/Linq/OrderedEnumerable.cs index 44ae5e5a7290..db19f92a247c 100644 --- a/src/System.Linq/src/System/Linq/OrderedEnumerable.cs +++ b/src/System.Linq/src/System/Linq/OrderedEnumerable.cs @@ -2,34 +2,87 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Buffers; using System.Collections; using System.Collections.Generic; +using System.Threading; namespace System.Linq { - internal abstract class OrderedEnumerable : IOrderedEnumerable, IPartition + internal abstract class OrderedEnumerable : Enumerable.Iterator, IOrderedEnumerable, IPartition { + private const int ArrayPoolUseThreshold = 32; internal IEnumerable _source; + private Buffer _buffer; + private int _index; + private int _maxIdx = int.MaxValue; + private int[] _map; - private int[] SortedMap(Buffer buffer) + private int[] SortedMap(Buffer buffer, bool rent) { - return GetEnumerableSorter().Sort(buffer._items, buffer._count); + return GetEnumerableSorter().Sort(buffer._items, buffer._count, rent); } - private int[] SortedMap(Buffer buffer, int minIdx, int maxIdx) + private int[] SortedMap(Buffer buffer, int minIdx, int maxIdx, bool rent) { - return GetEnumerableSorter().Sort(buffer._items, buffer._count, minIdx, maxIdx); + return GetEnumerableSorter().Sort(buffer._items, buffer._count, minIdx, maxIdx, rent); } - public IEnumerator GetEnumerator() + public override bool MoveNext() { - Buffer buffer = new Buffer(_source); - if (buffer._count > 0) + switch (_state) + { + case 1: + Buffer buffer = new Buffer(_source); + if (buffer._count > 0) + { + if (_index != 0 || _maxIdx != int.MaxValue) + { + _map = SortedMap(buffer, _index, _maxIdx, buffer._count >= ArrayPoolUseThreshold); + } + else + { + _map = SortedMap(buffer, buffer._count >= ArrayPoolUseThreshold); + } + + _maxIdx = Math.Min(_maxIdx, buffer._count - 1); + _buffer = buffer; + _state = 2; + goto case 2; + } + + break; + case 2: + if (_index <= _maxIdx) + { + _current = _buffer._items[_map[_index]]; + ++_index; + return true; + } + break; + } + + Dispose(); + return false; + } + + public override void Dispose() + { + base.Dispose(); + if (_map != null) { - int[] map = SortedMap(buffer); - for (int i = 0; i < buffer._count; i++) + _buffer = default(Buffer); + if (_map.Length >= ArrayPoolUseThreshold) { - yield return buffer._items[map[i]]; + int[] rented = Interlocked.Exchange(ref _map, null); + if (rented != null) + { + ArrayPool.Shared.Return(rented); + } + } + else + { + _map = null; } } } @@ -45,12 +98,13 @@ public TElement[] ToArray() } TElement[] array = new TElement[count]; - int[] map = SortedMap(buffer); + int[] map = SortedMap(buffer, true); for (int i = 0; i != array.Length; i++) { array[i] = buffer._items[map[i]]; } + ArrayPool.Shared.Return(map); return array; } @@ -61,11 +115,13 @@ public List ToList() List list = new List(count); if (count > 0) { - int[] map = SortedMap(buffer); + int[] map = SortedMap(buffer, true); for (int i = 0; i != count; i++) { list.Add(buffer._items[map[i]]); } + + ArrayPool.Shared.Return(map); } return list; @@ -84,29 +140,11 @@ public int GetCount(bool onlyIfCheap) internal IEnumerator GetEnumerator(int minIdx, int maxIdx) { - Buffer buffer = new Buffer(_source); - int count = buffer._count; - if (count > minIdx) - { - if (count <= maxIdx) - { - maxIdx = count - 1; - } - - if (minIdx == maxIdx) - { - yield return GetEnumerableSorter().ElementAt(buffer._items, count, minIdx); - } - else - { - int[] map = SortedMap(buffer, minIdx, maxIdx); - while (minIdx <= maxIdx) - { - yield return buffer._items[map[minIdx]]; - ++minIdx; - } - } - } + OrderedEnumerable enumerator = _state == 0 && _threadId == Environment.CurrentManagedThreadId ? this : (OrderedEnumerable)Clone(); + enumerator._state = 1; + enumerator._index = minIdx; + enumerator._maxIdx = maxIdx; + return enumerator; } internal TElement[] ToArray(int minIdx, int maxIdx) @@ -128,7 +166,7 @@ internal TElement[] ToArray(int minIdx, int maxIdx) return new TElement[] { GetEnumerableSorter().ElementAt(buffer._items, count, minIdx) }; } - int[] map = SortedMap(buffer, minIdx, maxIdx); + int[] map = SortedMap(buffer, minIdx, maxIdx, true); TElement[] array = new TElement[maxIdx - minIdx + 1]; int idx = 0; while (minIdx <= maxIdx) @@ -138,6 +176,7 @@ internal TElement[] ToArray(int minIdx, int maxIdx) ++minIdx; } + ArrayPool.Shared.Return(map); return array; } @@ -160,7 +199,7 @@ internal List ToList(int minIdx, int maxIdx) return new List(1) { GetEnumerableSorter().ElementAt(buffer._items, count, minIdx) }; } - int[] map = SortedMap(buffer, minIdx, maxIdx); + int[] map = SortedMap(buffer, minIdx, maxIdx, true); List list = new List(maxIdx - minIdx + 1); while (minIdx <= maxIdx) { @@ -168,6 +207,7 @@ internal List ToList(int minIdx, int maxIdx) ++minIdx; } + ArrayPool.Shared.Return(map); return list; } @@ -481,6 +521,11 @@ internal OrderedEnumerable(IEnumerable source, Func ke _descending = descending; } + public override Enumerable.Iterator Clone() + { + return new OrderedEnumerable(_source, _keySelector, _comparer, _descending, _parent); + } + internal override EnumerableSorter GetEnumerableSorter(EnumerableSorter next) { EnumerableSorter sorter = new EnumerableSorter(_keySelector, _comparer, _descending, next); @@ -584,10 +629,10 @@ internal abstract class EnumerableSorter internal abstract int CompareAnyKeys(int index1, int index2); - private int[] ComputeMap(TElement[] elements, int count) + private int[] ComputeMap(TElement[] elements, int count, bool rent) { ComputeKeys(elements, count); - int[] map = new int[count]; + int[] map = rent ? ArrayPool.Shared.Rent(count) : new int[count]; for (int i = 0; i < count; i++) { map[i] = i; @@ -596,23 +641,26 @@ private int[] ComputeMap(TElement[] elements, int count) return map; } - internal int[] Sort(TElement[] elements, int count) + internal int[] Sort(TElement[] elements, int count, bool rent) { - int[] map = ComputeMap(elements, count); + int[] map = ComputeMap(elements, count, rent); QuickSort(map, 0, count - 1); return map; } - internal int[] Sort(TElement[] elements, int count, int minIdx, int maxIdx) + internal int[] Sort(TElement[] elements, int count, int minIdx, int maxIdx, bool rent) { - int[] map = ComputeMap(elements, count); + int[] map = ComputeMap(elements, count, rent); PartialQuickSort(map, 0, count - 1, minIdx, maxIdx); return map; } internal TElement ElementAt(TElement[] elements, int count, int idx) { - return elements[QuickSelect(ComputeMap(elements, count), count - 1, idx)]; + int[] map = ComputeMap(elements, count, true); + var index = QuickSelect(map, count - 1, idx); + ArrayPool.Shared.Return(map); + return elements[index]; } private int CompareKeys(int index1, int index2) diff --git a/src/System.Linq/src/project.json b/src/System.Linq/src/project.json index da77e581e452..e98a392e178b 100644 --- a/src/System.Linq/src/project.json +++ b/src/System.Linq/src/project.json @@ -1,4 +1,7 @@ { + "dependencies": { + "System.Buffers": "4.0.0-rc3-24117-00" + }, "frameworks": { "netstandard1.5": { "dependencies": {