diff --git a/1.3/Assemblies/1Fishery.dll b/1.3/Assemblies/1Fishery.dll index 8062cf4..33a4226 100644 Binary files a/1.3/Assemblies/1Fishery.dll and b/1.3/Assemblies/1Fishery.dll differ diff --git a/1.3/Assemblies/1Fishery.pdb b/1.3/Assemblies/1Fishery.pdb index eed1a7c..71148c8 100644 Binary files a/1.3/Assemblies/1Fishery.pdb and b/1.3/Assemblies/1Fishery.pdb differ diff --git a/1.4/Assemblies/1Fishery.dll b/1.4/Assemblies/1Fishery.dll index 1f30969..7d5428b 100644 Binary files a/1.4/Assemblies/1Fishery.dll and b/1.4/Assemblies/1Fishery.dll differ diff --git a/1.4/Assemblies/1Fishery.pdb b/1.4/Assemblies/1Fishery.pdb index 6693544..f0f2663 100644 Binary files a/1.4/Assemblies/1Fishery.pdb and b/1.4/Assemblies/1Fishery.pdb differ diff --git a/Source/FisheryLib/Collections/FishSet.cs b/Source/FisheryLib/Collections/FishSet.cs index 5517cda..fd62cf1 100644 --- a/Source/FisheryLib/Collections/FishSet.cs +++ b/Source/FisheryLib/Collections/FishSet.cs @@ -12,6 +12,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using FisheryLib.FunctionPointers; +using FisheryLib.Pools; using FisheryLib.Utility; using JetBrains.Annotations; @@ -227,14 +228,14 @@ public class FishSet : ISet, IReadOnlyCollection, ICollection if (IsTail(bucket, bucketIndex)) { var previousEntry = bucket; - var tailingEntries = TryGetAndClearTailingEntries(bucketIndex); + using var tailingEntries = TryGetAndClearTailingEntries(bucketIndex); SetParentTail(bucketIndex, Tails.SOLO); SetEntryWithoutTail(bucketIndex, entry); InsertEntry(previousEntry, ReplaceBehaviour.Throw, true); - if (tailingEntries != null) - InsertEntries(tailingEntries, ReplaceBehaviour.Throw, true); + if (tailingEntries != default) + InsertEntries(tailingEntries.List, ReplaceBehaviour.Throw, true); return true; } @@ -245,9 +246,9 @@ public class FishSet : ISet, IReadOnlyCollection, ICollection } } - private void InsertEntries(T[] tailingEntries, ReplaceBehaviour replaceBehaviour, bool shifting = false) + private void InsertEntries(List tailingEntries, ReplaceBehaviour replaceBehaviour, bool shifting = false) { - for (var i = 0; i < tailingEntries.Length; i++) + for (var i = 0; i < tailingEntries.Count; i++) InsertEntry(tailingEntries[i], replaceBehaviour, shifting); } @@ -329,32 +330,32 @@ public class FishSet : ISet, IReadOnlyCollection, ICollection } var previousEntry = _buckets[bucketIndex]; - var tailingEntries = TryGetAndClearTailingEntries(bucketIndex); + using var tailingEntries = TryGetAndClearTailingEntries(bucketIndex); SetParentTail(bucketIndex, Tails.SOLO); SetEntryAsTail(bucketIndex, entry, parentIndex, offset); InsertEntry(previousEntry!, ReplaceBehaviour.Throw, true); - if (tailingEntries != null) - InsertEntries(tailingEntries, ReplaceBehaviour.Throw, true); + if (tailingEntries != default) + InsertEntries(tailingEntries.List, ReplaceBehaviour.Throw, true); return true; } - private T[]? TryGetAndClearTailingEntries(int bucketIndex) + private PooledIList> TryGetAndClearTailingEntries(int bucketIndex) { if (!HasTail(bucketIndex)) - return null; - - var tailingEntries = new T[GetTailCount(bucketIndex)]; - var i = 0; + return default; + + var tailingEntries = new PooledIList>(); + var tailingEntriesList = tailingEntries.List; var tailIndex = GetTailIndex(bucketIndex); while (true) { ref var tailingBucket = ref _buckets[tailIndex]; - tailingEntries[i++] = tailingBucket!; + tailingEntriesList.Add(tailingBucket!); tailingBucket = default; if (HasTail(tailIndex)) diff --git a/Source/FisheryLib/Collections/FishTable.cs b/Source/FisheryLib/Collections/FishTable.cs index 4b3a74d..4e0504f 100644 --- a/Source/FisheryLib/Collections/FishTable.cs +++ b/Source/FisheryLib/Collections/FishTable.cs @@ -172,9 +172,9 @@ public class FishTable : IDictionary, IDictionary, I IEnumerable IReadOnlyDictionary.Values => Values; - object? IDictionary.this[object key] + object IDictionary.this[object key] { - get => this[AssertKeyType(key)]; + get => this[AssertKeyType(key)]!; set => this[AssertKeyType(key)] = AssertValueType(value); } @@ -198,7 +198,7 @@ public class FishTable : IDictionary, IDictionary, I return default!; } - public unsafe TValue? this[TKey key] + public unsafe TValue this[TKey key] { [CollectionAccess(CollectionAccessType.Read)] [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -211,7 +211,7 @@ public class FishTable : IDictionary, IDictionary, I ref var bucket = ref _buckets[bucketIndex]; if (_keyEqualityComparer(key, bucket.Key)) - return bucket.Value; + return bucket.Value!; ContinueWithTailOrThrow(ref bucketIndex, key); } @@ -352,14 +352,14 @@ public class FishTable : IDictionary, IDictionary, I if (IsTail(ref bucket, bucketIndex)) { var previousEntry = bucket; - var tailingEntries = TryGetAndClearTailingEntries(bucketIndex); + using var tailingEntries = TryGetAndClearTailingEntries(bucketIndex); SetParentTail(bucketIndex, Tails.SOLO); SetEntryWithoutTail(bucketIndex, entry); InsertEntry(previousEntry, ReplaceBehaviour.Throw, true); - if (tailingEntries != null) - InsertEntries(tailingEntries, ReplaceBehaviour.Throw, true); + if (tailingEntries != default) + InsertEntries(tailingEntries.List, ReplaceBehaviour.Throw, true); return true; } @@ -370,9 +370,9 @@ public class FishTable : IDictionary, IDictionary, I } } - private void InsertEntries(Entry[] tailingEntries, ReplaceBehaviour replaceBehaviour, bool shifting = false) + private void InsertEntries(List tailingEntries, ReplaceBehaviour replaceBehaviour, bool shifting = false) { - for (var i = 0; i < tailingEntries.Length; i++) + for (var i = 0; i < tailingEntries.Count; i++) InsertEntry(tailingEntries[i], replaceBehaviour, shifting); } @@ -473,31 +473,31 @@ public class FishTable : IDictionary, IDictionary, I } var previousEntry = _buckets[bucketIndex]; - var tailingEntries = TryGetAndClearTailingEntries(bucketIndex); + using var tailingEntries = TryGetAndClearTailingEntries(bucketIndex); SetParentTail(bucketIndex, Tails.SOLO); SetEntryAsTail(bucketIndex, entry, parentIndex, offset); InsertEntry(previousEntry, ReplaceBehaviour.Throw, true); - if (tailingEntries != null) - InsertEntries(tailingEntries, ReplaceBehaviour.Throw, true); + if (tailingEntries != default) + InsertEntries(tailingEntries.List, ReplaceBehaviour.Throw, true); return true; } - private Entry[]? TryGetAndClearTailingEntries(int bucketIndex) + private PooledIList> TryGetAndClearTailingEntries(int bucketIndex) { if (!HasTail(bucketIndex)) - return null; + return default; - var tailingEntries = new Entry[GetTailCount(bucketIndex)]; - var i = 0; + var tailingEntries = new PooledIList>(); + var tailingEntriesList = tailingEntries.List; var tailIndex = GetTailIndex(bucketIndex); while (true) { ref var tailingBucket = ref _buckets[tailIndex]; - tailingEntries[i++] = tailingBucket; + tailingEntriesList.Add(tailingBucket); tailingBucket = default; if (HasTail(tailIndex)) @@ -639,7 +639,7 @@ public class FishTable : IDictionary, IDictionary, I } [CollectionAccess(CollectionAccessType.Read | CollectionAccessType.UpdatedContent)] - public unsafe bool TryGetOrAddValue(TKey key, [MaybeNullWhen(false)] out TValue value) + public unsafe bool TryGetOrAddValue(TKey key, out TValue value) { var result = true; diff --git a/Source/FisheryLib/Fishery.cs b/Source/FisheryLib/Fishery.cs index 947f372..61087c7 100644 --- a/Source/FisheryLib/Fishery.cs +++ b/Source/FisheryLib/Fishery.cs @@ -28,11 +28,11 @@ namespace FisheryLib; [PublicAPI] public static class FisheryLib { - public const decimal VERSION = 0.39995M; + public const decimal VERSION = 0.5M; public static decimal CurrentlyLoadedVersion { get; } = VERSION; - public const string VERSION_STRING = "0.3.999.5"; + public const string VERSION_STRING = "0.5.0.0"; // public static string GetVersion() // { diff --git a/Source/FisheryLib/Fishery.csproj b/Source/FisheryLib/Fishery.csproj index db50f1b..3dd3a22 100644 --- a/Source/FisheryLib/Fishery.csproj +++ b/Source/FisheryLib/Fishery.csproj @@ -1,81 +1,81 @@  - - FisheryLib - 1Fishery - net48 - preview - AnyCPU - true - false - bradson - Copyright (c) 2022 bradson - LICENSE - True - preview-all - true - portable - prompt - 5 - enable - True - v1.4;v1.3 + + FisheryLib + 1Fishery + net48 + preview + AnyCPU + true + false + bradson + Copyright (c) 2022 bradson + LICENSE + True + preview-all + true + portable + prompt + 5 + enable + True + v1.4;v1.3 false - false - false + false + false - - - 1701;1702;IL2121;CS8500;CS9082;CA1000;CA1002;CA1003;CA1707;CA1010;CA1715;CA1716;CA1724;CA1031;CA1033; - CA1034;CA1043;CA1051;CA1305;CA1310;CA1710;CA1711;CA1810;CA1812;CA1819;CA1851;CA2002;CA5394;CA1725;CA1062 - - - - - - ..\..\1.4\Assemblies\ - - - - - - - - ..\..\1.3\Assemblies\ - - - - - - - - + + + 1701;1702;IL2121;CS8500;CS9082;CA1000;CA1002;CA1003;CA1707;CA1010;CA1715;CA1716;CA1724;CA1031;CA1033; + CA1034;CA1043;CA1051;CA1305;CA1310;CA1710;CA1711;CA1810;CA1812;CA1819;CA1851;CA2002;CA5394;CA1725;CA1062 + + + + + + ..\..\1.4\Assemblies\ + + + + + + + + ..\..\1.3\Assemblies\ + + + + + + + + - - + + - - + + - - - - - - - - + + + + + + + + - - - - + + + + - - - - True - - - + + + + True + + + \ No newline at end of file diff --git a/Source/FisheryLib/Pools/FishPool.cs b/Source/FisheryLib/Pools/FishPool.cs index e8ef6c3..9257766 100644 --- a/Source/FisheryLib/Pools/FishPool.cs +++ b/Source/FisheryLib/Pools/FishPool.cs @@ -9,29 +9,96 @@ namespace FisheryLib.Pools; public static class FishPool where T : IFishPoolable { - private static T?[] _freeItems = new T[4]; - private static int _freeItemCount; + [ThreadStatic] + private static T?[]? _itemsThreadStatic; + + [ThreadStatic] + private static int _itemCountThreadStatic; + + private static object _lockObject = new(); + private static T?[] _itemsGlobal = new T[16]; + private static int _itemCountGlobal; public static T Get() { - if (_freeItemCount == 0) - return Reflection.New(); + if (_itemCountThreadStatic == 0) + FetchThroughLock(); + + ref var itemBucket = ref _itemsThreadStatic![--_itemCountThreadStatic]; + var item = itemBucket; + itemBucket = default; + return item!; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void FetchThroughLock() + { + var itemsThreadStatic = _itemsThreadStatic ??= InitializeItemsThreadStatic(); + var createNew = false; - var freeItem = _freeItems[--_freeItemCount]; - _freeItems[_freeItemCount] = default; - return freeItem!; + lock (_lockObject) + { + if (_itemCountGlobal != 0) + { + for (var i = 8; i-- > 0;) + { + itemsThreadStatic[i] = _itemsGlobal[--_itemCountGlobal]; + _itemsGlobal[_itemCountGlobal] = default; + } + } + else + { + createNew = true; + } + } + + if (createNew) + { + for (var i = 8; i-- > 0;) + itemsThreadStatic[i] = Reflection.New(); + } + + _itemCountThreadStatic = 8; } public static void Return(T item) { item.Reset(); - - if (++_freeItemCount >= _freeItems.Length) - ExpandFreeItems(); - _freeItems[_freeItemCount - 1] = item; + var itemsThreadStatic = _itemsThreadStatic ??= InitializeItemsThreadStatic(); + + if (_itemCountThreadStatic == 16) + PushThroughLock(); + + itemsThreadStatic[_itemCountThreadStatic++] = item; } [MethodImpl(MethodImplOptions.NoInlining)] - private static void ExpandFreeItems() => Array.Resize(ref _freeItems, _freeItems.Length << 1); + private static void PushThroughLock() + { + var itemsThreadStatic = _itemsThreadStatic!; + + lock (_lockObject) + { + if (_itemCountGlobal + 8 > _itemsGlobal.Length) + ExpandGlobalItems(); + + for (var i = 16; i-- > 8;) + { + ref var itemBucket = ref itemsThreadStatic[i]; + var item = itemBucket; + itemBucket = default; + + _itemsGlobal[_itemCountGlobal++] = item; + } + } + + _itemCountThreadStatic = 8; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static T?[] InitializeItemsThreadStatic() => new T?[16]; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ExpandGlobalItems() => Array.Resize(ref _itemsGlobal, _itemsGlobal.Length << 1); } \ No newline at end of file diff --git a/Source/FisheryLib/Pools/PooledIList.cs b/Source/FisheryLib/Pools/PooledIList.cs index 25664d4..0f8340b 100644 --- a/Source/FisheryLib/Pools/PooledIList.cs +++ b/Source/FisheryLib/Pools/PooledIList.cs @@ -12,8 +12,15 @@ public record struct PooledIList() : IDisposable where T : IList, new() { public T List = Get(); - private static T?[] _freeItems = new T[4]; - private static int _freeItemCount; + [ThreadStatic] + private static T?[]? _itemsThreadStatic; + + [ThreadStatic] + private static int _itemCountThreadStatic; + + private static object _lockObject = new(); + private static T?[] _itemsGlobal = new T[16]; + private static int _itemCountGlobal; public void Dispose() { @@ -23,27 +30,87 @@ public record struct PooledIList() : IDisposable where T : IList, new() Return(List); List = default!; } - + public static T Get() { - if (_freeItemCount == 0) - return Reflection.New(); + if (_itemCountThreadStatic == 0) + FetchThroughLock(); + + ref var itemBucket = ref _itemsThreadStatic![--_itemCountThreadStatic]; + var item = itemBucket; + itemBucket = default; + return item!; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void FetchThroughLock() + { + var itemsThreadStatic = _itemsThreadStatic ??= InitializeItemsThreadStatic(); + var createNew = false; - var freeItem = _freeItems[--_freeItemCount]; - _freeItems[_freeItemCount] = default; - return freeItem!; + lock (_lockObject) + { + if (_itemCountGlobal != 0) + { + for (var i = 8; i-- > 0;) + { + itemsThreadStatic[i] = _itemsGlobal[--_itemCountGlobal]; + _itemsGlobal[_itemCountGlobal] = default; + } + } + else + { + createNew = true; + } + } + + if (createNew) + { + for (var i = 8; i-- > 0;) + itemsThreadStatic[i] = Reflection.New(); + } + + _itemCountThreadStatic = 8; } public static void Return(T item) { item.Clear(); - - if (++_freeItemCount >= _freeItems.Length) - ExpandFreeItems(); - _freeItems[_freeItemCount - 1] = item; + var itemsThreadStatic = _itemsThreadStatic ??= InitializeItemsThreadStatic(); + + if (_itemCountThreadStatic == 16) + PushThroughLock(); + + itemsThreadStatic[_itemCountThreadStatic++] = item; } [MethodImpl(MethodImplOptions.NoInlining)] - private static void ExpandFreeItems() => Array.Resize(ref _freeItems, _freeItems.Length << 1); + private static void PushThroughLock() + { + var itemsThreadStatic = _itemsThreadStatic!; + + lock (_lockObject) + { + if (_itemCountGlobal + 8 > _itemsGlobal.Length) + ExpandGlobalItems(); + + for (var i = 16; i-- > 8;) + { + ref var itemBucket = ref itemsThreadStatic[i]; + var item = itemBucket; + itemBucket = default; + + _itemsGlobal[_itemCountGlobal++] = item; + } + } + + _itemCountThreadStatic = 8; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static T?[] InitializeItemsThreadStatic() => new T?[16]; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ExpandGlobalItems() => Array.Resize(ref _itemsGlobal, _itemsGlobal.Length << 1); } \ No newline at end of file diff --git a/Source/FisheryLib/SpanExtensions.cs b/Source/FisheryLib/SpanExtensions.cs index 17d35e2..4a6f455 100644 --- a/Source/FisheryLib/SpanExtensions.cs +++ b/Source/FisheryLib/SpanExtensions.cs @@ -40,11 +40,11 @@ public static class SpanExtensions => ref Unsafe.Add(ref span.DangerousGetPinnableReference(), i); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Sort(this Span span, IComparer? comparer) + public static void Sort(this Span span, IComparer? comparer = null) => ArraySortHelper.Default.Sort(span, comparer); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Sort(this Span values, Span keys, IComparer? comparer) + public static void Sort(this Span values, Span keys, IComparer? comparer = null) => ArraySortHelper.Default.Sort(keys, values, comparer); [MethodImpl(MethodImplOptions.AggressiveInlining)]