diff --git a/Recyclable.Collections.Benchmarks/Program.cs b/Recyclable.Collections.Benchmarks/Program.cs index e0e3068..c5afe52 100644 --- a/Recyclable.Collections.Benchmarks/Program.cs +++ b/Recyclable.Collections.Benchmarks/Program.cs @@ -16,7 +16,7 @@ internal class Program benchmark.Setup(); foreach (var _ in Enumerable.Range(1, 1000)) { - benchmark.RecyclableList_Contains(); + //benchmark.RecyclableList_Contains(); //benchmark.RecyclableArrayList_AddRange_WithCapacity(); //benchmark.RecyclableArrayList_Add_WithCapacity(); diff --git a/Recyclable.Collections.Benchmarks/RecyclableCollectionsBenchmarks.List.cs b/Recyclable.Collections.Benchmarks/RecyclableCollectionsBenchmarks.List.cs index d1c3a12..b6cb1a4 100644 --- a/Recyclable.Collections.Benchmarks/RecyclableCollectionsBenchmarks.List.cs +++ b/Recyclable.Collections.Benchmarks/RecyclableCollectionsBenchmarks.List.cs @@ -112,28 +112,52 @@ namespace Recyclable.Collections.Benchmarks DoNothing(data.Count); } - //[Benchmark(Baseline = true)] - public void List_Contains() + [Benchmark(Baseline = true)] + public void List_Contains_1_000_FirstItems() { var data = TestObjects; var list = TestObjectsAsList; - var dataCount = 100_000; + var dataCount = 1_000; for (var i = 0; i < dataCount; i++) { DoNothing(list.Contains(data[i])); } } - [Benchmark(Baseline = true)] - public void List_IndexOf() + [Benchmark] + public void List_Contains_1_000_LastItems() { var data = TestObjects; var list = TestObjectsAsList; - var dataCount = 100_000; + var dataCount = 1_000; + for (var i = 0; i < dataCount; i++) + { + DoNothing(list.Contains(data[^(i + 1)])); + } + } + + //[Benchmark(Baseline = true)] + public void List_IndexOf_1_000_FirstItems() + { + var data = TestObjects; + var list = TestObjectsAsList; + var dataCount = 1_000; for (var i = 0; i < dataCount; i++) { DoNothing(list.IndexOf(data[i])); } } + + //[Benchmark] + public void List_IndexOf_1_000_LastItems() + { + var data = TestObjects; + var list = TestObjectsAsList; + var dataCount = 1_000; + for (var i = 0; i < dataCount; i++) + { + DoNothing(list.IndexOf(data[^(i + 1)])); + } + } } } diff --git a/Recyclable.Collections.Benchmarks/RecyclableCollectionsBenchmarks.RecyclableList.cs b/Recyclable.Collections.Benchmarks/RecyclableCollectionsBenchmarks.RecyclableList.cs index b39689b..f8fb793 100644 --- a/Recyclable.Collections.Benchmarks/RecyclableCollectionsBenchmarks.RecyclableList.cs +++ b/Recyclable.Collections.Benchmarks/RecyclableCollectionsBenchmarks.RecyclableList.cs @@ -78,12 +78,12 @@ namespace Recyclable.Collections.Benchmarks DoNothing(data.LongCount); } - //[Benchmark] - public void RecyclableList_Contains() + [Benchmark] + public void RecyclableList_Contains_1_000_FirstItems() { var data = TestObjects; var list = TestObjectsAsRecyclableList; - var dataCount = 100_000; + var dataCount = 1000; for (var i = 0; i < dataCount; i++) { DoNothing(list.Contains(data[i])); @@ -91,15 +91,39 @@ namespace Recyclable.Collections.Benchmarks } [Benchmark] - public void RecyclableList_IndexOf() + public void RecyclableList_Contains_1_000_LastItems() { var data = TestObjects; var list = TestObjectsAsRecyclableList; - var dataCount = 100_000; + var dataCount = 1_000; + for (var i = 0; i < dataCount; i++) + { + DoNothing(list.Contains(data[^(i + 1)])); + } + } + + //[Benchmark] + public void RecyclableList_IndexOf_1_000_FirstItems() + { + var data = TestObjects; + var list = TestObjectsAsRecyclableList; + var dataCount = 1000; for (var i = 0; i < dataCount; i++) { DoNothing(list.IndexOf(data[i])); } } + + //[Benchmark] + public void RecyclableList_IndexOf_1_000_LastItems() + { + var data = TestObjects; + var list = TestObjectsAsRecyclableList; + var dataCount = 1_000; + for (var i = 0; i < dataCount; i++) + { + DoNothing(list.IndexOf(data[^(i + 1)])); + } + } } } diff --git a/Recyclable.Collections/RecyclableList.cs b/Recyclable.Collections/RecyclableList.cs index 8bb4db8..5fecbe4 100644 --- a/Recyclable.Collections/RecyclableList.cs +++ b/Recyclable.Collections/RecyclableList.cs @@ -515,221 +515,27 @@ namespace Recyclable.Collections return false; } - int blockSize = _blockSize; - int itemIndex = 0; - int blockIndex = 0; - int lastBlockIndex = _lastBlockIndex; - + int itemIndex, blockIndex = 0, lastBlockIndex = _lastBlockIndex, blockSize = _blockSize; Span memoryBlocksSpan = new(_memoryBlocks); - Span blockArraySpan = new(memoryBlocksSpan[blockIndex]); - if (item != null) + while (blockIndex < lastBlockIndex) { - while (blockIndex < lastBlockIndex) + itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex], item, 0, blockSize); + if (itemIndex >= 0) { - if (item.Equals(blockArraySpan[itemIndex])) - { - return true; - } - - if (itemIndex + 1 < blockSize && item.Equals(blockArraySpan[itemIndex + 1])) - { - return true; - } - - if (itemIndex + 2 < blockSize && item.Equals(blockArraySpan[itemIndex + 2])) - { - return true; - } - - if (itemIndex + 3 < blockSize && item.Equals(blockArraySpan[itemIndex + 3])) - { - return true; - } - - if (itemIndex + 4 < blockSize && item.Equals(blockArraySpan[itemIndex + 4])) - { - return true; - } - - if (itemIndex + 5 < blockSize && item.Equals(blockArraySpan[itemIndex + 5])) - { - return true; - } - - if (itemIndex + 6 < blockSize && item.Equals(blockArraySpan[itemIndex + 6])) - { - return true; - } - - if (itemIndex + 7 < blockSize && item.Equals(blockArraySpan[itemIndex + 7])) - { - return true; - } - - itemIndex += 8; - if (itemIndex >= blockSize) - { - blockIndex++; - blockArraySpan = new(memoryBlocksSpan[blockIndex]); - itemIndex = 0; - } + return true; } - blockArraySpan = new(memoryBlocksSpan[lastBlockIndex]); - var nextItemIndex = _nextItemIndex; - while (itemIndex < nextItemIndex) - { - if (item.Equals(blockArraySpan[itemIndex])) - { - return true; - } - - if (itemIndex + 1 < nextItemIndex && item.Equals(blockArraySpan[itemIndex + 1])) - { - return true; - } - - if (itemIndex + 2 < nextItemIndex && item.Equals(blockArraySpan[itemIndex + 2])) - { - return true; - } - - if (itemIndex + 3 < nextItemIndex && item.Equals(blockArraySpan[itemIndex + 3])) - { - return true; - } - - if (itemIndex + 4 < nextItemIndex && item.Equals(blockArraySpan[itemIndex + 4])) - { - return true; - } - - if (itemIndex + 5 < nextItemIndex && item.Equals(blockArraySpan[itemIndex + 5])) - { - return true; - } - - if (itemIndex + 6 < nextItemIndex && item.Equals(blockArraySpan[itemIndex + 6])) - { - return true; - } - - if (itemIndex + 7 < nextItemIndex && item.Equals(blockArraySpan[itemIndex + 7])) - { - return true; - } - - itemIndex += 8; - } - } - else - { - while (blockIndex < lastBlockIndex) - { - if (blockArraySpan[itemIndex] == null) - { - return true; - } - - if (itemIndex + 1 < blockSize && blockArraySpan[itemIndex + 1] == null) - { - return true; - } - - if (itemIndex + 2 < blockSize && blockArraySpan[itemIndex + 2] == null) - { - return true; - } - - if (itemIndex + 3 < blockSize && blockArraySpan[itemIndex + 3] == null) - { - return true; - } - - if (itemIndex + 4 < blockSize && blockArraySpan[itemIndex + 4] == null) - { - return true; - } - - if (itemIndex + 5 < blockSize && blockArraySpan[itemIndex + 5] == null) - { - return true; - } - - if (itemIndex + 6 < blockSize && blockArraySpan[itemIndex + 6] == null) - { - return true; - } - - if (itemIndex + 7 < blockSize && blockArraySpan[itemIndex + 7] == null) - { - return true; - } - - itemIndex += 8; - if (itemIndex >= blockSize) - { - blockIndex++; - blockArraySpan = new(memoryBlocksSpan[blockIndex]); - itemIndex = 0; - } - } - - blockArraySpan = new(memoryBlocksSpan[lastBlockIndex]); - var nextItemIndex = _nextItemIndex; - while (itemIndex < nextItemIndex) - { - if (blockArraySpan[itemIndex] == null) - { - return true; - } - - if (itemIndex + 1 < nextItemIndex && blockArraySpan[itemIndex + 1] == null) - { - return true; - } - - if (itemIndex + 2 < nextItemIndex && blockArraySpan[itemIndex + 2] == null) - { - return true; - } - - if (itemIndex + 3 < nextItemIndex && blockArraySpan[itemIndex + 3] == null) - { - return true; - } - - if (itemIndex + 4 < nextItemIndex && blockArraySpan[itemIndex + 4] == null) - { - return true; - } - - if (itemIndex + 5 < nextItemIndex && blockArraySpan[itemIndex + 5] == null) - { - return true; - } - - if (itemIndex + 6 < nextItemIndex && blockArraySpan[itemIndex + 6] == null) - { - return true; - } - - if (itemIndex + 7 < nextItemIndex && blockArraySpan[itemIndex + 7] == null) - { - return true; - } - - itemIndex += 8; - } + blockIndex++; } - return false; + return Array.IndexOf(memoryBlocksSpan[blockIndex], item, 0, _nextItemIndex) >= 0; } public void CopyTo(T[] array, int arrayIndex) => _memoryBlocks.CopyTo(0, _blockSize, _longCount, array, arrayIndex); public IEnumerator GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public int IndexOf(T item) { if (_longCount == 0) @@ -737,219 +543,52 @@ namespace Recyclable.Collections return ItemNotFoundIndex; } - int blockSize = _blockSize; - int itemIndex = 0; - int blockIndex = 0; - int lastBlockIndex = _lastBlockIndex; - + int itemIndex, blockIndex = 0, lastBlockIndex = _lastBlockIndex, blockSize = _blockSize; Span memoryBlocksSpan = new(_memoryBlocks); - Span blockArraySpan = new(memoryBlocksSpan[blockIndex]); - if (item != null) + while (blockIndex < lastBlockIndex) { - while (blockIndex < lastBlockIndex) + itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex], item, 0, blockSize); + if (itemIndex >= 0) { - if (item.Equals(blockArraySpan[itemIndex])) - { - return (blockIndex * blockSize) + itemIndex; - } - - if (itemIndex + 1 < blockSize && item.Equals(blockArraySpan[itemIndex + 1])) - { - return (blockIndex * blockSize) + itemIndex + 1; - } - - if (itemIndex + 2 < blockSize && item.Equals(blockArraySpan[itemIndex + 2])) - { - return (blockIndex * blockSize) + itemIndex + 2; - } - - if (itemIndex + 3 < blockSize && item.Equals(blockArraySpan[itemIndex + 3])) - { - return (blockIndex * blockSize) + itemIndex + 3; - } - - if (itemIndex + 4 < blockSize && item.Equals(blockArraySpan[itemIndex + 4])) - { - return (blockIndex * blockSize) + itemIndex + 4; - } - - if (itemIndex + 5 < blockSize && item.Equals(blockArraySpan[itemIndex + 5])) - { - return (blockIndex * blockSize) + itemIndex + 5; - } - - if (itemIndex + 6 < blockSize && item.Equals(blockArraySpan[itemIndex + 6])) - { - return (blockIndex * blockSize) + itemIndex + 6; - } - - if (itemIndex + 7 < blockSize && item.Equals(blockArraySpan[itemIndex + 7])) - { - return (blockIndex * blockSize) + itemIndex + 7; - } - - itemIndex += 8; - if (itemIndex >= blockSize) - { - blockIndex++; - blockArraySpan = new(memoryBlocksSpan[blockIndex]); - itemIndex = 0; - } + return (blockIndex * blockSize) + itemIndex; } - blockArraySpan = new(memoryBlocksSpan[blockIndex]); - var nextItemIndex = _nextItemIndex; - while (itemIndex < nextItemIndex) - { - if (item.Equals(blockArraySpan[itemIndex])) - { - return (blockIndex * blockSize) + itemIndex; - } - - if (itemIndex + 1 < nextItemIndex && item.Equals(blockArraySpan[itemIndex + 1])) - { - return (blockIndex * blockSize) + itemIndex + 1; - } - - if (itemIndex + 2 < nextItemIndex && item.Equals(blockArraySpan[itemIndex + 2])) - { - return (blockIndex * blockSize) + itemIndex + 2; - } - - if (itemIndex + 3 < nextItemIndex && item.Equals(blockArraySpan[itemIndex + 3])) - { - return (blockIndex * blockSize) + itemIndex + 3; - } - - if (itemIndex + 4 < nextItemIndex && item.Equals(blockArraySpan[itemIndex + 4])) - { - return (blockIndex * blockSize) + itemIndex + 4; - } - - if (itemIndex + 5 < nextItemIndex && item.Equals(blockArraySpan[itemIndex + 5])) - { - return (blockIndex * blockSize) + itemIndex + 5; - } - - if (itemIndex + 6 < nextItemIndex && item.Equals(blockArraySpan[itemIndex + 6])) - { - return (blockIndex * blockSize) + itemIndex + 6; - } - - if (itemIndex + 7 < nextItemIndex && item.Equals(blockArraySpan[itemIndex + 7])) - { - return (blockIndex * blockSize) + itemIndex + 7; - } - - itemIndex += 8; - } - } - else - { - while (blockIndex < lastBlockIndex) - { - if (blockArraySpan[itemIndex] == null) - { - return (blockIndex * blockSize) + itemIndex; - } - - if (itemIndex + 1 < blockSize && blockArraySpan[itemIndex + 1] == null) - { - return (blockIndex * blockSize) + itemIndex + 1; - } - - if (itemIndex + 2 < blockSize && blockArraySpan[itemIndex + 2] == null) - { - return (blockIndex * blockSize) + itemIndex + 2; - } - - if (itemIndex + 3 < blockSize && blockArraySpan[itemIndex + 3] == null) - { - return (blockIndex * blockSize) + itemIndex + 3; - } - - if (itemIndex + 4 < blockSize && blockArraySpan[itemIndex + 4] == null) - { - return (blockIndex * blockSize) + itemIndex + 4; - } - - if (itemIndex + 5 < blockSize && blockArraySpan[itemIndex + 5] == null) - { - return (blockIndex * blockSize) + itemIndex + 5; - } - - if (itemIndex + 6 < blockSize && blockArraySpan[itemIndex + 6] == null) - { - return (blockIndex * blockSize) + itemIndex + 6; - } - - if (itemIndex + 7 < blockSize && blockArraySpan[itemIndex + 7] == null) - { - return (blockIndex * blockSize) + itemIndex + 7; - } - - itemIndex += 8; - if (itemIndex >= blockSize) - { - blockIndex++; - blockArraySpan = new(memoryBlocksSpan[blockIndex]); - itemIndex = 0; - } - } - - blockArraySpan = new(memoryBlocksSpan[lastBlockIndex]); - var nextItemIndex = _nextItemIndex; - while (itemIndex < nextItemIndex) - { - if (blockArraySpan[itemIndex] == null) - { - return (blockIndex * blockSize) + itemIndex; - } - - if (itemIndex + 1 < nextItemIndex && blockArraySpan[itemIndex + 1] == null) - { - return (blockIndex * blockSize) + itemIndex + 1; - } - - if (itemIndex + 2 < nextItemIndex && blockArraySpan[itemIndex + 2] == null) - { - return (blockIndex * blockSize) + itemIndex + 2; - } - - if (itemIndex + 3 < nextItemIndex && blockArraySpan[itemIndex + 3] == null) - { - return (blockIndex * blockSize) + itemIndex + 3; - } - - if (itemIndex + 4 < nextItemIndex && blockArraySpan[itemIndex + 4] == null) - { - return (blockIndex * blockSize) + itemIndex + 4; - } - - if (itemIndex + 5 < nextItemIndex && blockArraySpan[itemIndex + 5] == null) - { - return (blockIndex * blockSize) + itemIndex + 5; - } - - if (itemIndex + 6 < nextItemIndex && blockArraySpan[itemIndex + 6] == null) - { - return (blockIndex * blockSize) + itemIndex + 6; - } - - if (itemIndex + 7 < nextItemIndex && blockArraySpan[itemIndex + 7] == null) - { - return (blockIndex * blockSize) + itemIndex + 7; - } - - itemIndex += 8; - } + blockIndex++; } - return ItemNotFoundIndex; + itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex], item, 0, _nextItemIndex); + return itemIndex >= 0 + ? (blockIndex * blockSize) + itemIndex + : ItemNotFoundIndex; } public void Insert(int index, T item) => throw new NotSupportedException(); - public long LongIndexOf(T item) => _memoryBlocks.LongIndexOf(_blockSize, item, _equalityComparer); + public long LongIndexOf(T item) + { + if (_longCount == 0) + { + return ItemNotFoundIndex; + } + + int itemIndex, blockIndex = 0, lastBlockIndex = _lastBlockIndex, blockSize = _blockSize; + Span memoryBlocksSpan = new(_memoryBlocks); + while (blockIndex < lastBlockIndex) + { + itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex], item, 0, blockSize); + if (itemIndex >= 0) + { + return ((long)blockIndex * blockSize) + itemIndex; + } + + blockIndex++; + } + + itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex], item, 0, _nextItemIndex); + return itemIndex >= 0 + ? ((long)blockIndex * blockSize) + itemIndex + : ItemNotFoundIndex; + } + public bool Remove(T item) => throw new NotSupportedException(); public void RemoveBlock(int index) {