RecyclableLongList.IndexOf stability & performance improvements.
This commit is contained in:
1 parent
e3ca5b4f87
commit
fb689e84d6
4 files changed
+298
-81
No files matched your search
@@ -2,11 +2,9 @@ namespace Recyclable.Collections.Parallel
|
||||
{
|
||||
public sealed class IndexOfSynchronizationContext
|
||||
{
|
||||
private readonly SpinWait _participantsSpinWait = new();
|
||||
private readonly SpinLock _participantsSpinLock = new(false);
|
||||
private readonly SpinLock _indexOfSpinLock = new(false);
|
||||
private bool _indexOfLockTaken;
|
||||
|
||||
private SpinWait _participantsSpinWait = new();
|
||||
private SpinLockSlimmer _participantsSpinLock = new();
|
||||
private SpinLockSlimmer _indexOfSpinLock = new();
|
||||
|
||||
internal bool _isItemFound;
|
||||
internal int _participants;
|
||||
@@ -14,29 +12,21 @@ namespace Recyclable.Collections.Parallel
|
||||
|
||||
public void AddParticipant()
|
||||
{
|
||||
bool lockTaken = false;
|
||||
_participantsSpinLock.Enter(ref lockTaken);
|
||||
_participantsSpinLock.Enter();
|
||||
_participants++;
|
||||
if (lockTaken)
|
||||
{
|
||||
_participantsSpinLock.Exit(false);
|
||||
}
|
||||
_participantsSpinLock.Exit();
|
||||
}
|
||||
|
||||
public void AddParticipants(int newParticipants)
|
||||
{
|
||||
bool lockTaken = false;
|
||||
_participantsSpinLock.Enter(ref lockTaken);
|
||||
_participantsSpinLock.Enter();
|
||||
_participants += newParticipants;
|
||||
if (lockTaken)
|
||||
{
|
||||
_participantsSpinLock.Exit(false);
|
||||
}
|
||||
_participantsSpinLock.Exit();
|
||||
}
|
||||
|
||||
public void Lock()
|
||||
{
|
||||
_indexOfSpinLock.Enter(ref _indexOfLockTaken);
|
||||
_indexOfSpinLock.Enter();
|
||||
}
|
||||
|
||||
public void RemoveParticipants(int participantsToRemove)
|
||||
@@ -46,13 +36,9 @@ namespace Recyclable.Collections.Parallel
|
||||
ThrowNotEnoughParticipants();
|
||||
}
|
||||
|
||||
bool lockTaken = false;
|
||||
_participantsSpinLock.Enter(ref lockTaken);
|
||||
_participantsSpinLock.Enter();
|
||||
_participants -= participantsToRemove;
|
||||
if (lockTaken)
|
||||
{
|
||||
_participantsSpinLock.Exit(false);
|
||||
}
|
||||
_participantsSpinLock.Exit();
|
||||
}
|
||||
|
||||
public void RemoveParticipant()
|
||||
@@ -62,13 +48,9 @@ namespace Recyclable.Collections.Parallel
|
||||
ThrowNoMoreParticipants();
|
||||
}
|
||||
|
||||
bool lockTaken = false;
|
||||
_participantsSpinLock.Enter(ref lockTaken);
|
||||
_participantsSpinLock.Enter();
|
||||
_participants--;
|
||||
if (lockTaken)
|
||||
{
|
||||
_participantsSpinLock.Exit(false);
|
||||
}
|
||||
_participantsSpinLock.Exit();
|
||||
}
|
||||
|
||||
public void SignalAndWait()
|
||||
@@ -78,13 +60,9 @@ namespace Recyclable.Collections.Parallel
|
||||
ThrowNoMoreParticipants();
|
||||
}
|
||||
|
||||
bool lockTaken = false;
|
||||
_participantsSpinLock.Enter(ref lockTaken);
|
||||
_participantsSpinLock.Enter();
|
||||
_participants--;
|
||||
if (lockTaken)
|
||||
{
|
||||
_participantsSpinLock.Exit(false);
|
||||
}
|
||||
_participantsSpinLock.Exit();
|
||||
|
||||
while (_participants > 0)
|
||||
{
|
||||
@@ -94,15 +72,13 @@ namespace Recyclable.Collections.Parallel
|
||||
|
||||
public void Unlock()
|
||||
{
|
||||
if (_indexOfLockTaken)
|
||||
{
|
||||
_indexOfSpinLock.Exit(false);
|
||||
}
|
||||
_indexOfSpinLock.Exit();
|
||||
}
|
||||
|
||||
private static void ThrowNotEnoughParticipants() => throw new InvalidOperationException("Participant cannot be removed, because there are not enough participants.");
|
||||
private static void ThrowNoMoreParticipants() => throw new InvalidOperationException("Participant cannot be removed, because there are no more participants.");
|
||||
|
||||
public long FoundBlockIndex;
|
||||
public long FoundItemIndex;
|
||||
public Exception? Exception;
|
||||
public int Participants => _participants;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace Recyclable.Collections.Parallel
|
||||
{
|
||||
public struct SpinLockSlimmer
|
||||
{
|
||||
private volatile int _locked;
|
||||
|
||||
public void Enter()
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref _locked, 1, 0) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SpinWait waiter = new();
|
||||
while (Interlocked.CompareExchange(ref _locked, 1, 0) == 1)
|
||||
{
|
||||
waiter.SpinOnce();
|
||||
}
|
||||
}
|
||||
|
||||
public void Exit()
|
||||
{
|
||||
_locked = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,12 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
// #define MAX_BLOCKS_FOR_TASK_2
|
||||
// #define MAX_BLOCKS_FOR_TASK_3
|
||||
#define MAX_BLOCKS_FOR_TASK_4
|
||||
// #define MAX_BLOCKS_FOR_TASK_5
|
||||
// #define MAX_BLOCKS_FOR_TASK_6
|
||||
// #define MAX_BLOCKS_FOR_TASK_7
|
||||
// #define MAX_BLOCKS_FOR_TASK_8
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
using Recyclable.Collections.Parallel;
|
||||
|
||||
namespace Recyclable.Collections
|
||||
@@ -8,65 +16,105 @@ namespace Recyclable.Collections
|
||||
public static class IndexOfHelpers
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static void IndexOfIn4ConsecutiveBlocks(IndexOfSynchronizationContext context, RecyclableLongList<T> list, int blockIndex, T itemToFind)
|
||||
private static void IndexOfInConsecutiveBlocks(IndexOfSynchronizationContext context, RecyclableLongList<T> list, int blockIndex, T itemToFind)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (context._isItemFound)
|
||||
if (context._isItemFound && blockIndex >= context.FoundBlockIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
long index = Array.IndexOf(list._memoryBlocks[blockIndex], itemToFind, 0, list._blockSize);
|
||||
if (context._isItemFound)
|
||||
if (index >= 0 && (!context._isItemFound || index < context.FoundItemIndex))
|
||||
{
|
||||
ExchangeFoundItemIndexIfLower(context, blockIndex, ((long)blockIndex << list._blockSizePow2BitShift) + index);
|
||||
return;
|
||||
}
|
||||
else if (context._isItemFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (index >= 0)
|
||||
{
|
||||
context._isItemFound = true;
|
||||
context.Lock();
|
||||
context.FoundItemIndex = ((long)blockIndex << list._blockSizePow2BitShift) + index;
|
||||
context.Unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
#if MAX_BLOCKS_FOR_TASK_2 || MAX_BLOCKS_FOR_TASK_3 || MAX_BLOCKS_FOR_TASK_4 || MAX_BLOCKS_FOR_TASK_5 || MAX_BLOCKS_FOR_TASK_6 || MAX_BLOCKS_FOR_TASK_7 || MAX_BLOCKS_FOR_TASK_8
|
||||
|
||||
index = Array.IndexOf(list._memoryBlocks[blockIndex + 1], itemToFind, 0, list._blockSize);
|
||||
if (context._isItemFound)
|
||||
if (index >= 0)
|
||||
{
|
||||
ExchangeFoundItemIndexIfLower(context, blockIndex + 1, ((long)(blockIndex + 1) << list._blockSizePow2BitShift) + index);
|
||||
return;
|
||||
}
|
||||
else if (index >= 0)
|
||||
else if (context._isItemFound)
|
||||
{
|
||||
context._isItemFound = true;
|
||||
context.Lock();
|
||||
context.FoundItemIndex = ((long)(blockIndex + 1) << list._blockSizePow2BitShift) + index;
|
||||
context.Unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MAX_BLOCKS_FOR_TASK_3 || MAX_BLOCKS_FOR_TASK_4 || MAX_BLOCKS_FOR_TASK_5 || MAX_BLOCKS_FOR_TASK_6 || MAX_BLOCKS_FOR_TASK_7 || MAX_BLOCKS_FOR_TASK_8
|
||||
|
||||
index = Array.IndexOf(list._memoryBlocks[blockIndex + 2], itemToFind, 0, list._blockSize);
|
||||
if (context._isItemFound)
|
||||
if (index >= 0 && (!context._isItemFound || index < context.FoundItemIndex))
|
||||
{
|
||||
ExchangeFoundItemIndexIfLower(context, blockIndex + 2, ((long)(blockIndex + 2) << list._blockSizePow2BitShift) + index);
|
||||
return;
|
||||
}
|
||||
else if (context._isItemFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (index >= 0)
|
||||
{
|
||||
context._isItemFound = true;
|
||||
context.Lock();
|
||||
context.FoundItemIndex = ((long)(blockIndex + 2) << list._blockSizePow2BitShift) + index;
|
||||
context.Unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MAX_BLOCKS_FOR_TASK_4 || MAX_BLOCKS_FOR_TASK_5 || MAX_BLOCKS_FOR_TASK_6 || MAX_BLOCKS_FOR_TASK_7 || MAX_BLOCKS_FOR_TASK_8
|
||||
|
||||
index = Array.IndexOf(list._memoryBlocks[blockIndex + 3], itemToFind, 0, list._blockSize);
|
||||
if (!context._isItemFound && index >= 0)
|
||||
{
|
||||
context._isItemFound = true;
|
||||
context.Lock();
|
||||
context.FoundItemIndex = ((long)(blockIndex + 3) << list._blockSizePow2BitShift) + index;
|
||||
context.Unlock();
|
||||
ExchangeFoundItemIndexIfLower(context, blockIndex + 3, ((long)(blockIndex + 3) << list._blockSizePow2BitShift) + index);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MAX_BLOCKS_FOR_TASK_5 || MAX_BLOCKS_FOR_TASK_6 || MAX_BLOCKS_FOR_TASK_7 || MAX_BLOCKS_FOR_TASK_8
|
||||
|
||||
index = Array.IndexOf(list._memoryBlocks[blockIndex + 4], itemToFind, 0, list._blockSize);
|
||||
if (!context._isItemFound && index >= 0 && (!context._isItemFound || index < context.FoundItemIndex))
|
||||
{
|
||||
ExchangeFoundItemIndexIfLower(context, blockIndex + 4, ((long)(blockIndex + 4) << list._blockSizePow2BitShift) + index);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MAX_BLOCKS_FOR_TASK_6 || MAX_BLOCKS_FOR_TASK_7 || MAX_BLOCKS_FOR_TASK_8
|
||||
|
||||
index = Array.IndexOf(list._memoryBlocks[blockIndex + 5], itemToFind, 0, list._blockSize);
|
||||
if (!context._isItemFound && index >= 0 && (!context._isItemFound || index < context.FoundItemIndex))
|
||||
{
|
||||
ExchangeFoundItemIndexIfLower(context, blockIndex + 5, ((long)(blockIndex + 5) << list._blockSizePow2BitShift) + index);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MAX_BLOCKS_FOR_TASK_7 || MAX_BLOCKS_FOR_TASK_8
|
||||
|
||||
index = Array.IndexOf(list._memoryBlocks[blockIndex + 6], itemToFind, 0, list._blockSize);
|
||||
if (!context._isItemFound && index >= 0 && (!context._isItemFound || index < context.FoundItemIndex))
|
||||
{
|
||||
ExchangeFoundItemIndexIfLower(context, blockIndex + 6, ((long)(blockIndex + 6) << list._blockSizePow2BitShift) + index);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MAX_BLOCKS_FOR_TASK_8
|
||||
|
||||
index = Array.IndexOf(list._memoryBlocks[blockIndex + 7], itemToFind, 0, list._blockSize);
|
||||
if (!context._isItemFound && index >= 0 && (!context._isItemFound || index < context.FoundItemIndex))
|
||||
{
|
||||
ExchangeFoundItemIndexIfLower(context, blockIndex + 7, ((long)(blockIndex + 7) << list._blockSizePow2BitShift) + index);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -81,6 +129,21 @@ namespace Recyclable.Collections
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void ExchangeFoundItemIndexIfLower(IndexOfSynchronizationContext context, int blockIndex, long newItemIndex)
|
||||
{
|
||||
context.Lock();
|
||||
|
||||
if (newItemIndex < context.FoundItemIndex || context.FoundItemIndex == RecyclableDefaults.ItemNotFoundIndexLong)
|
||||
{
|
||||
context.FoundBlockIndex = blockIndex;
|
||||
context.FoundItemIndex = newItemIndex;
|
||||
}
|
||||
|
||||
context._isItemFound = true;
|
||||
context.Unlock();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static void IndexOfIn1Block(IndexOfSynchronizationContext context, RecyclableLongList<T> list, int blockIndex, T itemToFind)
|
||||
{
|
||||
@@ -94,10 +157,7 @@ namespace Recyclable.Collections
|
||||
long index = Array.IndexOf(list._memoryBlocks[blockIndex], itemToFind, 0, list._blockSize);
|
||||
if (!context._isItemFound && index >= 0)
|
||||
{
|
||||
context._isItemFound = true;
|
||||
context.Lock();
|
||||
context.FoundItemIndex = ((long)blockIndex << list._blockSizePow2BitShift) + index;
|
||||
context.Unlock();
|
||||
ExchangeFoundItemIndexIfLower(context, blockIndex, ((long)blockIndex << list._blockSizePow2BitShift) + index);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -110,7 +170,27 @@ namespace Recyclable.Collections
|
||||
}
|
||||
}
|
||||
|
||||
#if MAX_BLOCKS_FOR_TASK_2
|
||||
private const int BlocksForEachTask = 2;
|
||||
#endif
|
||||
#if MAX_BLOCKS_FOR_TASK_3
|
||||
private const int BlocksForEachTask = 3;
|
||||
#endif
|
||||
#if MAX_BLOCKS_FOR_TASK_4
|
||||
private const int BlocksForEachTask = 4;
|
||||
#endif
|
||||
#if MAX_BLOCKS_FOR_TASK_5
|
||||
private const int BlocksForEachTask = 5;
|
||||
#endif
|
||||
#if MAX_BLOCKS_FOR_TASK_6
|
||||
private const int BlocksForEachTask = 6;
|
||||
#endif
|
||||
#if MAX_BLOCKS_FOR_TASK_7
|
||||
private const int BlocksForEachTask = 7;
|
||||
#endif
|
||||
#if MAX_BLOCKS_FOR_TASK_8
|
||||
private const int BlocksForEachTask = 8;
|
||||
#endif
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static long IndexOfParallel(RecyclableLongList<T> list, T item)
|
||||
@@ -146,7 +226,7 @@ namespace Recyclable.Collections
|
||||
// Task.Factory.StartNew(() =>
|
||||
context.AddParticipant();
|
||||
var startingBlock = blockIndex;
|
||||
_ = Task.Run(() => IndexOfIn4ConsecutiveBlocks(context, list, startingBlock, item));
|
||||
_ = Task.Run(() => IndexOfInConsecutiveBlocks(context, list, startingBlock, item));
|
||||
|
||||
// & WAS SLOWER
|
||||
// ScheduleIndexOfTask(context, list, blockIndex, item);
|
||||
@@ -185,10 +265,7 @@ namespace Recyclable.Collections
|
||||
int itemIndex = Array.IndexOf(list._memoryBlocks[list._lastBlockWithData], item, 0, list._nextItemIndex > 0 ? list._nextItemIndex : list._blockSize);
|
||||
if (!context._isItemFound && itemIndex >= 0)
|
||||
{
|
||||
context._isItemFound = true;
|
||||
context.Lock();
|
||||
context.FoundItemIndex = ((long)blockIndex << list._blockSizePow2BitShift) + itemIndex;
|
||||
context.Unlock();
|
||||
ExchangeFoundItemIndexIfLower(context, blockIndex, ((long)blockIndex << list._blockSizePow2BitShift) + itemIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user