diff --git a/Recyclable.Collections.Benchmarks/POC/WhenParallelSearchBenchmarks.cs b/Recyclable.Collections.Benchmarks/POC/WhenParallelSearchBenchmarks.cs index bad4c11..683dcdf 100644 --- a/Recyclable.Collections.Benchmarks/POC/WhenParallelSearchBenchmarks.cs +++ b/Recyclable.Collections.Benchmarks/POC/WhenParallelSearchBenchmarks.cs @@ -1,10 +1,11 @@ using BenchmarkDotNet.Attributes; +using Microsoft.Extensions.ObjectPool; using MoreLinq; using System.Runtime.CompilerServices; namespace Recyclable.Collections.Benchmarks.POC { - public enum WhenParallelSearchBenchmarkType + public enum WhenParallelSearchBenchmarkType { IndexOfSequentially, IndexOfParallel, @@ -13,6 +14,62 @@ namespace Recyclable.Collections.Benchmarks.POC [MemoryDiagnoser] public class WhenParallelSearchBenchmarks : PocBenchmarkBase { + #region ItemRange V2 + + private sealed class ItemRangeV2 + { + public int BlockIndex; + public long ItemsToSearchCount; + internal bool _returned; + + public ItemRangeV2() + { + } + + public ItemRangeV2(int blockIndex, long itemsToSearchCount) + { + BlockIndex = blockIndex; + ItemsToSearchCount = itemsToSearchCount; + } + + public void Dispose() + { + if (!_returned) + { + _returned = true; + ItemRangePoolV2.Shared.Return(this); + } + } + } + + #endregion + + #region ItemRangePoolV2 + + private static class ItemRangePoolV2 + { + private static readonly SpinLock _updateLock = new(false); + public static readonly ObjectPool Shared = new DefaultObjectPool(new DefaultPooledObjectPolicy()); + + public static ItemRangeV2 Create(int blockIndex, long itemsToSearchCount) + { + bool lockTaken = false; + _updateLock.Enter(ref lockTaken); + var itemRange = Shared.Get(); + if (lockTaken) + { + _updateLock.Exit(false); + } + + itemRange._returned = false; + itemRange.BlockIndex = blockIndex; + itemRange.ItemsToSearchCount = itemsToSearchCount; + return itemRange; + } + } + + #endregion + //[Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 32)] //public int NumberOfThreads { get; set; } = 3; @@ -66,7 +123,7 @@ namespace Recyclable.Collections.Benchmarks.POC } [MethodImpl(MethodImplOptions.AggressiveOptimization)] - private static long DoLongIndexOfParallel(RecyclableList list, in long itemToFind, ItemRange searchInfo, ManualResetEventSlim itemFoundSignal) + private static long DoLongIndexOfParallel(RecyclableList list, in long itemToFind, ItemRangeV2 searchInfo, ManualResetEventSlim itemFoundSignal) { int index; int blockSize = list.BlockSize; @@ -248,13 +305,13 @@ namespace Recyclable.Collections.Benchmarks.POC } protected override Action? GetTestMethod(WhenParallelSearchBenchmarkType benchmarkType) => BenchmarkType switch - { - WhenParallelSearchBenchmarkType.IndexOfSequentially => IndexOfSequentially, - WhenParallelSearchBenchmarkType.IndexOfParallel => IndexOfParallel, - _ => throw CreateUnknownBenchmarkTypeException(benchmarkType), - }; + { + WhenParallelSearchBenchmarkType.IndexOfSequentially => IndexOfSequentially, + WhenParallelSearchBenchmarkType.IndexOfParallel => IndexOfParallel, + _ => throw CreateUnknownBenchmarkTypeException(benchmarkType), + }; - public override void Cleanup() + public override void Cleanup() { _testData = default; base.Cleanup(); diff --git a/Recyclable.Collections/ItemRange.cs b/Recyclable.Collections/ItemRange.cs deleted file mode 100644 index 37a31fa..0000000 --- a/Recyclable.Collections/ItemRange.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Recyclable.Collections.Pools; - -namespace Recyclable.Collections -{ - public sealed class ItemRange - { - public int BlockIndex; - public long ItemsToSearchCount; - internal bool _returned; - - public ItemRange() - { - } - - public ItemRange(int blockIndex, long itemsToSearchCount) - { - BlockIndex = blockIndex; - ItemsToSearchCount = itemsToSearchCount; - } - - public void Dispose() - { - if (!_returned) - { - _returned = true; - ItemRangePool.Shared.Return(this); - } - } - } -} diff --git a/Recyclable.Collections/ItemRangesIterator.cs b/Recyclable.Collections/ItemRangesIterator.cs deleted file mode 100644 index 9a150aa..0000000 --- a/Recyclable.Collections/ItemRangesIterator.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Runtime.CompilerServices; -using Recyclable.Collections.Parallel; - -namespace Recyclable.Collections -{ - public static class ItemRangesIterator - { - [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.NoInlining)] - public static void Iterate(in ParallelSynchronizationContext context, in SearchConfiguration search, Func, ItemRange, bool> action) - { - ItemRange itemRange; - int blockIndex = search.StartingBlockIndex; - long itemsCount = search.ItemsCount; - while (itemsCount > search.BlockSize) - { - itemRange = new(blockIndex, search.BlockSize); - if (!action(context, search, itemRange)) - { - break; - } - - itemsCount -= search.BlockSize; - blockIndex++; - } - - itemRange = new(blockIndex, itemsCount); - _ = action(context, search, itemRange); - } - } -} diff --git a/Recyclable.Collections/Parallel/ParallelSynchronizationContext.cs b/Recyclable.Collections/Parallel/IndexOfSynchronizationContext.cs similarity index 71% rename from Recyclable.Collections/Parallel/ParallelSynchronizationContext.cs rename to Recyclable.Collections/Parallel/IndexOfSynchronizationContext.cs index 2a2fc31..91f0c25 100644 --- a/Recyclable.Collections/Parallel/ParallelSynchronizationContext.cs +++ b/Recyclable.Collections/Parallel/IndexOfSynchronizationContext.cs @@ -1,6 +1,6 @@ namespace Recyclable.Collections.Parallel { - public sealed class ParallelSynchronizationContext : IDisposable + public sealed class IndexOfSynchronizationContext : IDisposable { internal bool _isItemFound; @@ -9,12 +9,12 @@ namespace Recyclable.Collections.Parallel public Exception? Exception; internal bool _returned; - public ParallelSynchronizationContext() + public IndexOfSynchronizationContext() { AllDoneSignal = new(0); } - public ParallelSynchronizationContext(int participantCount) + public IndexOfSynchronizationContext(int participantCount) { AllDoneSignal = new(participantCount); } @@ -31,7 +31,7 @@ namespace Recyclable.Collections.Parallel if (!_returned) { _returned = true; - ParallelSynchronizationContextPool.Shared.Return(this); + IndexOfSynchronizationContextPool.Shared.Return(this); } else { diff --git a/Recyclable.Collections/Parallel/ParallelSynchronizationContextPool.cs b/Recyclable.Collections/Parallel/IndexOfSynchronizationContextPool.cs similarity index 73% rename from Recyclable.Collections/Parallel/ParallelSynchronizationContextPool.cs rename to Recyclable.Collections/Parallel/IndexOfSynchronizationContextPool.cs index aa0142b..9d7f65b 100644 --- a/Recyclable.Collections/Parallel/ParallelSynchronizationContextPool.cs +++ b/Recyclable.Collections/Parallel/IndexOfSynchronizationContextPool.cs @@ -2,12 +2,12 @@ using Microsoft.Extensions.ObjectPool; namespace Recyclable.Collections.Parallel { - public static class ParallelSynchronizationContextPool + public static class IndexOfSynchronizationContextPool { private static readonly SpinLock _updateLock = new(false); - public static readonly ObjectPool Shared = new DefaultObjectPool(new DefaultPooledObjectPolicy()); + public static readonly ObjectPool Shared = new DefaultObjectPool(new DefaultPooledObjectPolicy()); - public static ParallelSynchronizationContext GetWithOneParticipant() + public static IndexOfSynchronizationContext GetWithOneParticipant() { bool lockTaken = false; _updateLock.Enter(ref lockTaken); diff --git a/Recyclable.Collections/Pools/ItemRangePool.cs b/Recyclable.Collections/Pools/ItemRangePool.cs deleted file mode 100644 index 6b332fe..0000000 --- a/Recyclable.Collections/Pools/ItemRangePool.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Microsoft.Extensions.ObjectPool; - -namespace Recyclable.Collections.Pools -{ - public static class ItemRangePool - { - private static readonly SpinLock _updateLock = new(false); - public static readonly ObjectPool Shared = new DefaultObjectPool(new DefaultPooledObjectPolicy()); - - public static ItemRange Create(int blockIndex, long itemsToSearchCount) - { - bool lockTaken = false; - _updateLock.Enter(ref lockTaken); - var itemRange = Shared.Get(); - if (lockTaken) - { - _updateLock.Exit(false); - } - - itemRange._returned = false; - itemRange.BlockIndex = blockIndex; - itemRange.ItemsToSearchCount = itemsToSearchCount; - return itemRange; - } - } -} diff --git a/Recyclable.Collections/RecyclableList.RecyclableListIndexOfHelpers.cs b/Recyclable.Collections/RecyclableList.IndexOfHelpers.cs similarity index 95% rename from Recyclable.Collections/RecyclableList.RecyclableListIndexOfHelpers.cs rename to Recyclable.Collections/RecyclableList.IndexOfHelpers.cs index 03b00bc..2026354 100644 --- a/Recyclable.Collections/RecyclableList.RecyclableListIndexOfHelpers.cs +++ b/Recyclable.Collections/RecyclableList.IndexOfHelpers.cs @@ -5,12 +5,12 @@ namespace Recyclable.Collections { public partial class RecyclableList { - public static class RecyclableListIndexOfHelpers + public static class IndexOfHelpers { [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] // [MethodImpl(MethodImplOptions.AggressiveInlining)] // [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.NoOptimization)] - private static void ScheduleIndexOfTask(ParallelSynchronizationContext context, RecyclableList list, int blockIndex, T itemToFind) + private static void ScheduleIndexOfTask(IndexOfSynchronizationContext context, RecyclableList list, int blockIndex, T itemToFind) { _ = context.AllDoneSignal.AddParticipant(); // & WAS SLOWER @@ -123,7 +123,7 @@ namespace Recyclable.Collections [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] // [MethodImpl(MethodImplOptions.AggressiveInlining)] // [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.NoOptimization)] - private static void ScheduleIndexOfTaskSingle(ParallelSynchronizationContext context, RecyclableList list, int blockIndex, T itemToFind) + private static void ScheduleIndexOfTaskSingle(IndexOfSynchronizationContext context, RecyclableList list, int blockIndex, T itemToFind) { _ = context.AllDoneSignal.AddParticipant(); // & WAS SLOWER @@ -172,7 +172,7 @@ namespace Recyclable.Collections //} //using var context = new ParallelSynchronizationContext(1); - ParallelSynchronizationContext context = ParallelSynchronizationContextPool.GetWithOneParticipant(); + IndexOfSynchronizationContext context = IndexOfSynchronizationContextPool.GetWithOneParticipant(); // (long)(_longCount * 0.329) => most efficient step, based on benchmarks //Iterate(this, context, item); diff --git a/Recyclable.Collections/RecyclableList.cs b/Recyclable.Collections/RecyclableList.cs index 22713b4..c397ea1 100644 --- a/Recyclable.Collections/RecyclableList.cs +++ b/Recyclable.Collections/RecyclableList.cs @@ -857,7 +857,7 @@ namespace Recyclable.Collections } else if (_longCount < RecyclableDefaults.MinItemsCountForParallelization) { - return (int)RecyclableListIndexOfHelpers.IndexOfSequential(this, item); + return (int)IndexOfHelpers.IndexOfSequential(this, item); } else { @@ -866,7 +866,7 @@ namespace Recyclable.Collections if (_longCount > _blockSize) { itemIndex = Array.IndexOf(_memoryBlocks[1], item, 0, (int)Math.Min(_blockSize, _longCount - _blockSize)); - return itemIndex >= 0 ? _blockSize + itemIndex : (int)RecyclableListIndexOfHelpers.IndexOfParallel(this, item); + return itemIndex >= 0 ? _blockSize + itemIndex : (int)IndexOfHelpers.IndexOfParallel(this, item); } else { @@ -896,7 +896,7 @@ namespace Recyclable.Collections } else if (_longCount < RecyclableDefaults.MinItemsCountForParallelization) { - return RecyclableListIndexOfHelpers.IndexOfSequential(this, item); + return IndexOfHelpers.IndexOfSequential(this, item); } else { @@ -905,7 +905,7 @@ namespace Recyclable.Collections if (_longCount > _blockSize) { itemIndex = Array.IndexOf(_memoryBlocks[1], item, 0, (int)Math.Min(_blockSize, _longCount - _blockSize)); - return itemIndex >= 0 ? _blockSize + itemIndex : RecyclableListIndexOfHelpers.IndexOfParallel(this, item); + return itemIndex >= 0 ? _blockSize + itemIndex : IndexOfHelpers.IndexOfParallel(this, item); } else { diff --git a/Recyclable.CollectionsTests/ItemRangesIteratorTests.cs b/Recyclable.CollectionsTests/ItemRangesIteratorTests.cs deleted file mode 100644 index 57b4973..0000000 --- a/Recyclable.CollectionsTests/ItemRangesIteratorTests.cs +++ /dev/null @@ -1,146 +0,0 @@ -using FluentAssertions; -using Recyclable.Collections; -using Recyclable.Collections.Parallel; -using System.Numerics; - -namespace Recyclable.CollectionsTests -{ - public class ItemRangesIteratorTests - { - [Theory] - [MemberData(nameof(TestCases))] - public void IterateShouldYieldAllItems(int startingBlockIndex, int blockSize, long itemsCount, int step, IEnumerable<(int BlockIndex, long ItemsCount)> expected) - { - // Prepare - var mockList = new RecyclableList(); - byte blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)blockSize)); - - var synchronizationContext = new ParallelSynchronizationContext(1); - var search = new SearchConfiguration, long>() - { - List = mockList, - ItemToFind = 0, - BlockSizeMinus1 = blockSize - 1, - BlockSizePow2BitShift = blockSizePow2BitShift, - ItemsCount = itemsCount, - BlockSize = step, - }; - - var actualItemRanges = new List<(int BlockIndex, long ItemsCount)>(); - - // Act - ItemRangesIterator.Iterate(synchronizationContext, search, (context, search, itemRange) => - { - actualItemRanges.Add((itemRange.BlockIndex, itemRange.ItemsToSearchCount)); - return true; - }); - - // Validate - _ = actualItemRanges.Should().HaveCount(expected.Count()).And.ContainInConsecutiveOrder(expected); - } - - public static IEnumerable TestCases => new[] - { - new object[] - { - 0, 128, 333, (long)(333 * 0.329), new(int BlockIndex, long ItemsCount)[] - { - (0, 109), - (0, 109), - (1, 109), - (2, 6) - } - }, - - new object[] - { - 0, 2, 10, 2, new (int BlockIndex, long ItemsCount)[] - { - (0, 2), - (1, 2), - (2, 2), - (3, 2), - (4, 2), - } - }, - - new object[] - { - 0, 2, 10, 3, new (int BlockIndex, long ItemsCount)[] - { - (0, 3), - (1, 3), - (3, 3), - (4, 1), - } - }, - - new object[] - { - 0, 1, 10, 3, new (int BlockIndex, long ItemsCount)[] - { - (0, 3), - (3, 3), - (6, 3), - (9, 1), - } - }, - - new object[] - { - 0, 2, 10, 4, new (int BlockIndex, long ItemsCount)[] - { - (0, 4), - (2, 4), - (4, 2), - } - }, - - new object[] - { - 0, 1, 10, 2, new (int BlockIndex, long ItemsCount)[] - { - (0, 2), - (2, 2), - (4, 2), - (6, 2), - (8, 2), - } - }, - - new object[] - { - 0, 2, 10, 1, new (int BlockIndex, long ItemsCount)[] - { - (0, 1), - (0, 1), - (1, 1), - (1, 1), - (2, 1), - (2, 1), - (3, 1), - (3, 1), - (4, 1), - (4, 1), - } - }, - - new object[] - { - 0, 1, 10, 1, new (int BlockIndex, long ItemsCount)[] - { - (0, 1), - (1, 1), - (2, 1), - (3, 1), - (4, 1), - (5, 1), - (6, 1), - (7, 1), - (8, 1), - (9, 1), - } - }, - }; - } -}