From 91abf1a61427ed5414f808d6c9b35b606e36b4d2 Mon Sep 17 00:00:00 2001 From: mlemanczyk Date: Wed, 1 Feb 2023 23:29:23 +0100 Subject: [PATCH] RecyclableList.Contains reworked - first version --- .../BenchmarkBase.cs | 2 + Recyclable.Collections.Benchmarks/Program.cs | 46 +++--- .../RecyclableCollectionsBenchmarks.cs | 2 + Recyclable.Collections/RecyclableList.cs | 136 ++++++++++++++++-- 4 files changed, 154 insertions(+), 32 deletions(-) diff --git a/Recyclable.Collections.Benchmarks/BenchmarkBase.cs b/Recyclable.Collections.Benchmarks/BenchmarkBase.cs index 3c10498..8fd7802 100644 --- a/Recyclable.Collections.Benchmarks/BenchmarkBase.cs +++ b/Recyclable.Collections.Benchmarks/BenchmarkBase.cs @@ -21,6 +21,8 @@ namespace Recyclable.Collections.Benchmarks protected RecyclableArrayList? _testObjectsAsRecyclableArrayList; protected RecyclableArrayList TestObjectsAsRecyclableArrayList => _testObjectsAsRecyclableArrayList ?? throw new NullReferenceException("Something is wrong and the field is not initialized"); + protected RecyclableList? _testObjectsAsRecyclableList; + protected RecyclableList TestObjectsAsRecyclableList => _testObjectsAsRecyclableList ?? throw new NullReferenceException("Something is wrong and the field is not initialized"); protected IEnumerable TestObjectsAsIEnumerable => Enumerable.Range(1, TestObjectCount).Select(static x => new object()); protected static void DoNothing(T obj) diff --git a/Recyclable.Collections.Benchmarks/Program.cs b/Recyclable.Collections.Benchmarks/Program.cs index 882322a..e0e3068 100644 --- a/Recyclable.Collections.Benchmarks/Program.cs +++ b/Recyclable.Collections.Benchmarks/Program.cs @@ -4,28 +4,40 @@ using Recyclable.Collections.Benchmarks; //BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(new[] { "-f*" }); -BenchmarkRunner.Run( - ManualConfig.Create(DefaultConfig.Instance).WithOptions(ConfigOptions.DisableOptimizationsValidator) -); - - //BenchmarkRunner.Run(); -//var benchmark = new RecyclableCollectionsBenchmarks(); -//benchmark.Setup(); -//foreach (var _ in Enumerable.Range(1, 1000)) -//{ -// //benchmark.RecyclableArrayList_AddRange_WithCapacity(); -// //benchmark.RecyclableArrayList_Add_WithCapacity(); -// //benchmark.RecyclableList_Create(); -// benchmark.RecyclableList_Create_WithCapacity(); -// //benchmark.RecyclableList_Add(); -// //benchmark.RecyclableList_Add_WithCapacity(); -//} -//benchmark.Cleanup(); +internal class Program +{ + void RunBenchmark() + { + var benchmark = new RecyclableCollectionsBenchmarks(); + benchmark.Setup(); + foreach (var _ in Enumerable.Range(1, 1000)) + { + benchmark.RecyclableList_Contains(); + //benchmark.RecyclableArrayList_AddRange_WithCapacity(); + //benchmark.RecyclableArrayList_Add_WithCapacity(); + //benchmark.RecyclableList_Create(); + //benchmark.RecyclableList_Create_WithCapacity(); + //benchmark.RecyclableList_Add(); + //benchmark.RecyclableList_Add_WithCapacity(); + } + + benchmark.Cleanup(); + } + + private static void Main(string[] args) + { + BenchmarkRunner.Run( + ManualConfig.Create(DefaultConfig.Instance).WithOptions(ConfigOptions.DisableOptimizationsValidator | ConfigOptions.JoinSummary) + ); + + //RunBenchmark(); + } +} // benchmark.Setup(); // benchmark.Cleanup(); diff --git a/Recyclable.Collections.Benchmarks/RecyclableCollectionsBenchmarks.cs b/Recyclable.Collections.Benchmarks/RecyclableCollectionsBenchmarks.cs index 8aabc86..3c21495 100644 --- a/Recyclable.Collections.Benchmarks/RecyclableCollectionsBenchmarks.cs +++ b/Recyclable.Collections.Benchmarks/RecyclableCollectionsBenchmarks.cs @@ -18,6 +18,7 @@ namespace Recyclable.Collections.Benchmarks _testArray = TestObjects.ToArray(); _testObjectsAsList = TestObjects.ToList(); _testObjectsAsRecyclableArrayList = TestObjects.ToRecyclableArrayList(); + _testObjectsAsRecyclableList = TestObjects.ToRecyclableList(); } [GlobalCleanup] @@ -28,6 +29,7 @@ namespace Recyclable.Collections.Benchmarks _testRecyclableList?.Dispose(); TestObjectsAsRecyclableArrayList?.Dispose(); _testObjectsAsRecyclableArrayList = default; + _testObjectsAsRecyclableList = default; _testObjectsAsList = default; _testObjects = default; } diff --git a/Recyclable.Collections/RecyclableList.cs b/Recyclable.Collections/RecyclableList.cs index 710af38..ca70c76 100644 --- a/Recyclable.Collections/RecyclableList.cs +++ b/Recyclable.Collections/RecyclableList.cs @@ -1,6 +1,10 @@ using System.Buffers; using System.Collections; +using System.Linq; +using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Security.Cryptography.X509Certificates; namespace Recyclable.Collections { @@ -14,7 +18,7 @@ namespace Recyclable.Collections private int _blockSize; private int _lastBlockIndex; - private int _lastItemIndex; + private int _nextItemIndex; protected ArrayPool _memoryBlocksPool; protected ArrayPool _blockArrayPool; protected T[][] _memoryBlocks; @@ -48,14 +52,14 @@ namespace Recyclable.Collections list._longCount--; int blockSize = list._blockSize; - if (list._lastItemIndex > 0) + if (list._nextItemIndex > 0) { - list._lastItemIndex--; + list._nextItemIndex--; } else { list._lastBlockIndex--; - list._lastItemIndex = blockSize; + list._nextItemIndex = blockSize; } if ((list._capacity * blockSize) - oldCountMinus1 == blockSize) @@ -243,11 +247,11 @@ namespace Recyclable.Collections _ = EnsureCapacity(_longCount + 1); } - _memoryBlocks[_lastBlockIndex][_lastItemIndex++] = item; - if (_lastItemIndex == _blockSize) + _memoryBlocks[_lastBlockIndex][_nextItemIndex++] = item; + if (_nextItemIndex == _blockSize) { _lastBlockIndex++; - _lastItemIndex = 0; + _nextItemIndex = 0; } _longCount++; @@ -287,7 +291,7 @@ namespace Recyclable.Collections _longCount = targetCapacity; _lastBlockIndex = targetBlockIdx - 1; - _lastItemIndex = targetItemIdx; + _nextItemIndex = targetItemIdx; } public void AddRange(in List items) @@ -324,7 +328,7 @@ namespace Recyclable.Collections _longCount = targetCapacity; _lastBlockIndex = targetBlockIdx - 1; - _lastItemIndex = targetItemIdx; + _nextItemIndex = targetItemIdx; } public void AddRange(in IList items) @@ -361,7 +365,7 @@ namespace Recyclable.Collections _longCount = targetCapacity; _lastBlockIndex = targetBlockIdx - 1; - _lastItemIndex = targetItemIdx; + _nextItemIndex = targetItemIdx; } private void AddRange(IEnumerable source, int growByCount = RecyclableDefaults.MinPooledArrayLength) @@ -431,7 +435,7 @@ namespace Recyclable.Collections _longCount = targetBlockIdx * blockSize + targetItemIdx; _lastBlockIndex = targetBlockIdx; - _lastItemIndex = targetItemIdx; + _nextItemIndex = targetItemIdx; return; } @@ -484,7 +488,7 @@ namespace Recyclable.Collections _longCount = targetItemIdx; _lastBlockIndex = targetBlockIdx - 1; - _lastItemIndex = targetItemIdx; + _nextItemIndex = targetItemIdx; } public void Clear() @@ -502,11 +506,113 @@ namespace Recyclable.Collections _capacity = 0; _lastBlockIndex = 0; - _lastItemIndex = 0; + _nextItemIndex = 0; _longCount = 0; } - public bool Contains(T item) => _memoryBlocks.Any(x => x.Contains(item)); + public bool Contains(T item) + { + if (_longCount == 0) + { + return false; + } + + int midPoint = _blockSize / 2; + int lastBlockIndex = _lastBlockIndex; + int itemIndex0 = 0; + int itemIndex1 = midPoint; + int blockIndex = 0; + + Span memoryBlocksSpan = new(_memoryBlocks); + Span blockArraySpan = new(memoryBlocksSpan[blockIndex]); + if (item != null) + { + while (blockIndex <= lastBlockIndex) + { + if (item.Equals(blockArraySpan[itemIndex0++])) + { + return true; + } + + if (item.Equals(blockArraySpan[itemIndex1++])) + { + return true; + } + + if (itemIndex0 == midPoint) + { + blockIndex++; + if (blockIndex == lastBlockIndex) + { + int nextItemIndex = _nextItemIndex; + if (nextItemIndex == 0) + { + return false; + } + + midPoint = (nextItemIndex / 2) + (nextItemIndex % 2); + } + + blockArraySpan = new(memoryBlocksSpan[blockIndex]); + itemIndex0 = 0; + itemIndex1 = midPoint; + } + } + } + else + { + while (blockIndex < lastBlockIndex) + { + if (blockArraySpan[itemIndex0++] == null) + { + return true; + } + + if (blockArraySpan[itemIndex1++] == null) + { + return true; + } + + if (itemIndex0 == midPoint) + { + blockIndex++; + if (blockIndex == lastBlockIndex) + { + if (_nextItemIndex == 0) + { + return false; + } + + midPoint = ((_nextItemIndex - 1) / 2) + ((_nextItemIndex - 1) % 2); + } + + blockArraySpan = new(memoryBlocksSpan[blockIndex]); + itemIndex0 = 0; + itemIndex1 = 0; + } + } + } + + return false; + + //for (memoryBlockIndex = 0; memoryBlockIndex < lastBlockIndex; memoryBlockIndex++) + //{ + // //T? found = Array.Find(memoryBlocksSpan[memoryBlockIndex], x => comparer.Equals(x, item)); + // //if (found != null) + // //{ + // // return true; + // //} + + // blockArraySpan = new(memoryBlocksSpan[memoryBlockIndex]); + // for (itemIndex = 0; itemIndex < mainBlockSize; itemIndex++) + // { + // if (comparer.Equals(blockArraySpan[itemIndex], item)) + // { + // return true; + // } + // } + //} + } public void CopyTo(T[] array, int arrayIndex) => _memoryBlocks.CopyTo(0, _blockSize, _longCount, array, arrayIndex); @@ -530,7 +636,7 @@ namespace Recyclable.Collections _capacity -= _blockSize; if (_lastBlockIndex == index) { - _lastItemIndex = 0; + _nextItemIndex = 0; } else if (_lastBlockIndex > index) {