diff --git a/src/Peachpie.Runtime/OrderedDictionary.cs b/src/Peachpie.Runtime/OrderedDictionary.cs index e11d02cfad..3fdf9dddd2 100644 --- a/src/Peachpie.Runtime/OrderedDictionary.cs +++ b/src/Peachpie.Runtime/OrderedDictionary.cs @@ -1,8 +1,8 @@ using System; +using System.Buffers; using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Runtime.CompilerServices; using System.Threading; using Pchp.Core.Utilities; @@ -188,6 +188,27 @@ public int CompareTo(IntStringKey other) [DebuggerDisplay("dictionary (count = {Count})")] public sealed class OrderedDictionary/**/ : IEnumerable>, IEnumerable { + #region BucketPool + + static class BucketPool + { + public static Bucket[] RentData(uint size) => ArrayPool.Shared.Rent(unchecked((int)size)); + + public static int[] RentHash(uint size) => ArrayPool.Shared.Rent(unchecked((int)size)); + + public static void Return(Bucket[] array) => ArrayPool.Shared.Return(array, clearArray: true); + + public static void Return(int[] array) + { + if (array != null) + { + ArrayPool.Shared.Return(array, clearArray: false); + } + } + } + + #endregion + [DebuggerDisplay("{DebugDisplay,nq}")] internal struct Bucket { @@ -302,12 +323,19 @@ private OrderedDictionary(OrderedDictionary/**/ from) if (from == null) throw new ArgumentNullException(nameof(from)); _mask = from._mask; - _data = from._data.AsSpan().ToArray(); - if (from._hash != null) - _hash = from._hash.AsSpan().ToArray(); + _size = from._size; _dataUsed = from._dataUsed; + + _data = BucketPool.RentData(_size); + Array.Copy(from._data, 0, _data, 0, _dataUsed); + + if (from._hash != null) + { + _hash = BucketPool.RentHash(_size); + Array.Copy(from._hash, 0, _hash, 0, _size); + } + _dataDeleted = from._dataDeleted; - _size = from._size; //nInternalPointer = from.nInternalPointer; _maxIntKey = from._maxIntKey; } @@ -376,7 +404,7 @@ private void _initialize(uint mask) // _mask = mask; - _data = new Bucket[size]; + _data = BucketPool.RentData(size); _hash = null; // no keys _dataUsed = 0; _dataDeleted = 0; @@ -392,9 +420,13 @@ private void _resize(uint size) //Array.Resize(ref this._data, (int)size); // slower - var newdata = new Bucket[size]; + var newdata = BucketPool.RentData(size); + Array.Copy(_data, 0, newdata, 0, _dataUsed); // faster than Memory.CopyTo() and Array.Resize - _data = newdata; + + BucketPool.Return( + Interlocked.Exchange(ref _data, newdata) + ); _mask = size - 1; _size = size; @@ -409,13 +441,13 @@ private void _resize(uint size) private void _rehash() { - var data = this._data; - var hash = this._hash; + var data = this._data.AsSpan(0, (int)_size); + var hash = this._hash.AsSpan(0, (int)_size); - Debug.Assert(hash != null, "no hash"); + Debug.Assert(hash.Length != 0, "no hash"); Debug.Assert(data.Length == hash.Length, "internal array size mismatch"); - hash.AsSpan().Fill(_invalidIndex); // some optimizations + hash.Fill(_invalidIndex); // some optimizations //Array.Fill(hash, _invalidIndex); // simple for-loop for (int i = this._dataUsed - 1; i >= 0; i--) @@ -441,7 +473,10 @@ private void _rehash() private void _createhash() { - this._hash = new int[this._size]; + BucketPool.Return( + Interlocked.Exchange(ref this._hash, BucketPool.RentHash(this._size)) + ); + _rehash(); } @@ -1069,7 +1104,7 @@ public void CopyTo(TValue[] array, int arrayIndex) } // FastEnumerator does not have to be disposed } - + /// /// Copy values into given array. /// @@ -1107,7 +1142,7 @@ public void Shuffle(Random/*!*/generator) // shuffle and compact elements: - var newData = new Bucket[_size]; + var newData = BucketPool.RentData(_size); var i = 0; // where to put next element var enumerator = GetEnumerator(); @@ -1131,7 +1166,10 @@ public void Shuffle(Random/*!*/generator) i++; } - _data = newData; + BucketPool.Return( + Interlocked.Exchange(ref _data, newData) // _data = newData; + ); + _dataDeleted = 0; _dataUsed = i; //nInternalPointer = 0; @@ -1140,7 +1178,7 @@ public void Shuffle(Random/*!*/generator) if (this._hash == null) { - this._hash = new int[this._size]; + this._hash = BucketPool.RentHash(this._size); } _rehash(); @@ -1158,7 +1196,7 @@ public void Reverse() // copy elements in reverse order and compact: - var newData = new Bucket[_size]; + var newData = BucketPool.RentData(_size); var i = Count; // where to put next element var enumerator = GetEnumerator(); @@ -1171,7 +1209,10 @@ public void Reverse() bucket.Value = current.Value; } - _data = newData; + BucketPool.Return( + Interlocked.Exchange(ref _data, newData) + ); + _dataUsed = Count; // before changing _dataDeleted !! _dataDeleted = 0; //nInternalPointer = 0; @@ -1180,7 +1221,7 @@ public void Reverse() if (this._hash == null) { - this._hash = new int[this._size]; + this._hash = BucketPool.RentHash(this._size); } _rehash(); @@ -1236,7 +1277,7 @@ public void Sort(IComparer>/*!*/comparer) { if (this._hash == null) { - this._hash = new int[this._size]; + this._hash = BucketPool.RentHash(this._size); } _rehash(); @@ -1251,9 +1292,12 @@ sealed class MultisortComparer : IComparer { readonly IComparer>[]/*!*/ comparers; - public MultisortComparer(IComparer>[]/*!*/ comparers) + readonly int length; + + public MultisortComparer(IComparer>[]/*!*/ comparers, int length) { this.comparers = comparers; + this.length = length; } public int Compare(Bucket[] x, Bucket[] y) @@ -1310,7 +1354,7 @@ public static void Sort( } // sort indices - Array.Sort(idx, comparer: new MultisortComparer(comparers)); + Array.Sort(idx, comparer: new MultisortComparer(comparers, hashtables.Length)); // for (int h = 0; h < hashtables.Length; h++)