RecyclableList.IndexOf performance improvements.
This commit is contained in:
1 parent
c4e633e7d3
commit
29cb16793a
3 files changed
+152
-152
No files matched your search
@@ -1,22 +1,71 @@
|
||||
namespace Recyclable.Collections.Parallel
|
||||
{
|
||||
public sealed class IndexOfSynchronizationContext : IDisposable
|
||||
public sealed class IndexOfSynchronizationContext
|
||||
{
|
||||
private readonly SpinWait _participantsSpinWait = new();
|
||||
|
||||
internal bool _isItemFound;
|
||||
internal int _participants;
|
||||
internal bool _returned;
|
||||
|
||||
public void AddParticipant()
|
||||
{
|
||||
_ = Interlocked.Increment(ref _participants);
|
||||
}
|
||||
|
||||
public void AddParticipants(int newParticipants)
|
||||
{
|
||||
_ = Interlocked.Add(ref _participants, newParticipants);
|
||||
}
|
||||
|
||||
public void RemoveParticipants(int participantsToRemove)
|
||||
{
|
||||
if (_participants < participantsToRemove)
|
||||
{
|
||||
ThrowNotEnoughParticipants();
|
||||
}
|
||||
|
||||
_ = Interlocked.Add(ref _participants, -participantsToRemove);
|
||||
}
|
||||
|
||||
public void RemoveParticipant()
|
||||
{
|
||||
if (_participants == 0)
|
||||
{
|
||||
ThrowNoMoreParticipants();
|
||||
}
|
||||
|
||||
_ = Interlocked.Decrement(ref _participants);
|
||||
}
|
||||
|
||||
public void SignalAndWait()
|
||||
{
|
||||
if (_participants == 0)
|
||||
{
|
||||
ThrowNoMoreParticipants();
|
||||
}
|
||||
|
||||
_ = Interlocked.Decrement(ref _participants);
|
||||
while (_participants > 0)
|
||||
{
|
||||
_participantsSpinWait.SpinOnce();
|
||||
}
|
||||
}
|
||||
|
||||
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 readonly Barrier AllDoneSignal;
|
||||
public long FoundItemIndex;
|
||||
public Exception? Exception;
|
||||
internal bool _returned;
|
||||
public int Participants => _participants;
|
||||
|
||||
public IndexOfSynchronizationContext()
|
||||
{
|
||||
AllDoneSignal = new(0);
|
||||
}
|
||||
|
||||
public IndexOfSynchronizationContext(int participantCount)
|
||||
{
|
||||
AllDoneSignal = new(participantCount);
|
||||
_participants = participantCount;
|
||||
}
|
||||
|
||||
public void SetItemFound()
|
||||
@@ -33,11 +82,6 @@ namespace Recyclable.Collections.Parallel
|
||||
_returned = true;
|
||||
IndexOfSynchronizationContextPool.Shared.Return(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
AllDoneSignal.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,14 +22,14 @@ namespace Recyclable.Collections.Parallel
|
||||
context._isItemFound = false;
|
||||
}
|
||||
|
||||
switch (context.AllDoneSignal.ParticipantsRemaining)
|
||||
switch (context._participants)
|
||||
{
|
||||
case > 1:
|
||||
context.AllDoneSignal.RemoveParticipants(context.AllDoneSignal.ParticipantsRemaining - 1);
|
||||
context.RemoveParticipants(context._participants - 1);
|
||||
break;
|
||||
|
||||
case 0:
|
||||
_ = context.AllDoneSignal.AddParticipant();
|
||||
context.AddParticipant();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,161 +8,102 @@ namespace Recyclable.Collections
|
||||
public static class IndexOfHelpers
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.NoOptimization)]
|
||||
private static void ScheduleIndexOfTask(IndexOfSynchronizationContext context, RecyclableLongList<T> list, int blockIndex, T itemToFind)
|
||||
private static void IndexOfIn4ConsecutiveBlocks(IndexOfSynchronizationContext context, RecyclableLongList<T> list, int blockIndex, T itemToFind)
|
||||
{
|
||||
_ = context.AllDoneSignal.AddParticipant();
|
||||
// & WAS SLOWER
|
||||
// _ = ThreadPool.QueueUserWorkItem<object?>((_) =>
|
||||
_ = Task.Factory.StartNew(() =>
|
||||
try
|
||||
{
|
||||
try
|
||||
if (context._isItemFound)
|
||||
{
|
||||
if (context._isItemFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
long index = Array.IndexOf(list._memoryBlocks[blockIndex], itemToFind, 0, list._blockSize);
|
||||
if (context._isItemFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (index >= 0)
|
||||
{
|
||||
context._isItemFound = true;
|
||||
_ = Interlocked.Exchange(ref context.FoundItemIndex, (blockIndex << list._blockSizePow2BitShift) + index);
|
||||
return;
|
||||
}
|
||||
|
||||
index = Array.IndexOf(list._memoryBlocks[blockIndex + 1], itemToFind, 0, list._blockSize);
|
||||
if (context._isItemFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (index >= 0)
|
||||
{
|
||||
context._isItemFound = true;
|
||||
_ = Interlocked.Exchange(ref context.FoundItemIndex, ((blockIndex + 1) << list._blockSizePow2BitShift) + index);
|
||||
return;
|
||||
}
|
||||
|
||||
index = Array.IndexOf(list._memoryBlocks[blockIndex + 2], itemToFind, 0, list._blockSize);
|
||||
if (context._isItemFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (index >= 0)
|
||||
{
|
||||
context._isItemFound = true;
|
||||
_ = Interlocked.Exchange(ref context.FoundItemIndex, ((blockIndex + 2) << list._blockSizePow2BitShift) + index);
|
||||
return;
|
||||
}
|
||||
|
||||
index = Array.IndexOf(list._memoryBlocks[blockIndex + 3], itemToFind, 0, list._blockSize);
|
||||
if (!context._isItemFound && index >= 0)
|
||||
{
|
||||
context._isItemFound = true;
|
||||
_ = Interlocked.Exchange(ref context.FoundItemIndex, ((blockIndex + 3) << list._blockSizePow2BitShift) + index);
|
||||
}
|
||||
|
||||
//itemsToSearch -= blockSize;
|
||||
//if (itemsToSearch > 0)
|
||||
//{
|
||||
// if (itemsToSearch < blockSize)
|
||||
// {
|
||||
// blockSize = (int)itemsToSearch;
|
||||
// }
|
||||
|
||||
// index = Array.IndexOf(list._memoryBlocks[blockIndex + 2], itemToFind, 0, blockSize);
|
||||
// if (index >= 0)
|
||||
// {
|
||||
// if (!context.ItemFoundSignal.IsSet)
|
||||
// {
|
||||
// context.ItemFoundSignal.Set();
|
||||
// _ = Interlocked.Exchange(ref context.FoundItemIndex, ((blockIndex + 2) << list._blockSizePow2BitShift) + index);
|
||||
// }
|
||||
|
||||
// return;
|
||||
// }
|
||||
//}
|
||||
|
||||
//itemsToSearch -= blockSize;
|
||||
//if (itemsToSearch > 0)
|
||||
//{
|
||||
// if (itemsToSearch < blockSize)
|
||||
// {
|
||||
// blockSize = (int)itemsToSearch;
|
||||
// }
|
||||
|
||||
// index = Array.IndexOf(list._memoryBlocks[blockIndex + 3], itemToFind, 0, blockSize);
|
||||
// if (!context.ItemFoundSignal.IsSet && index >= 0)
|
||||
// {
|
||||
// context.ItemFoundSignal.Set();
|
||||
// _ = Interlocked.Exchange(ref context.FoundItemIndex, ((blockIndex + 3) << list._blockSizePow2BitShift) + index);
|
||||
|
||||
// return;
|
||||
// }
|
||||
//}
|
||||
return;
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
long index = Array.IndexOf(list._memoryBlocks[blockIndex], itemToFind, 0, list._blockSize);
|
||||
if (context._isItemFound)
|
||||
{
|
||||
e.Data.Add("list", list);
|
||||
e.Data.Add("itemToFind", itemToFind);
|
||||
e.Data.Add("blockIndex", blockIndex);
|
||||
_ = Interlocked.Exchange(ref context.Exception, e);
|
||||
return;
|
||||
}
|
||||
finally
|
||||
else if (index >= 0)
|
||||
{
|
||||
context.AllDoneSignal.RemoveParticipant();
|
||||
context._isItemFound = true;
|
||||
_ = Interlocked.Exchange(ref context.FoundItemIndex, (blockIndex << list._blockSizePow2BitShift) + index);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
index = Array.IndexOf(list._memoryBlocks[blockIndex + 1], itemToFind, 0, list._blockSize);
|
||||
if (context._isItemFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (index >= 0)
|
||||
{
|
||||
context._isItemFound = true;
|
||||
_ = Interlocked.Exchange(ref context.FoundItemIndex, ((blockIndex + 1) << list._blockSizePow2BitShift) + index);
|
||||
return;
|
||||
}
|
||||
|
||||
index = Array.IndexOf(list._memoryBlocks[blockIndex + 2], itemToFind, 0, list._blockSize);
|
||||
if (context._isItemFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (index >= 0)
|
||||
{
|
||||
context._isItemFound = true;
|
||||
_ = Interlocked.Exchange(ref context.FoundItemIndex, ((blockIndex + 2) << list._blockSizePow2BitShift) + index);
|
||||
return;
|
||||
}
|
||||
|
||||
index = Array.IndexOf(list._memoryBlocks[blockIndex + 3], itemToFind, 0, list._blockSize);
|
||||
if (!context._isItemFound && index >= 0)
|
||||
{
|
||||
context._isItemFound = true;
|
||||
_ = Interlocked.Exchange(ref context.FoundItemIndex, ((blockIndex + 3) << list._blockSizePow2BitShift) + index);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.Data.Add("list", list);
|
||||
e.Data.Add("itemToFind", itemToFind);
|
||||
e.Data.Add("blockIndex", blockIndex);
|
||||
_ = Interlocked.Exchange(ref context.Exception, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.RemoveParticipant();
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.NoOptimization)]
|
||||
private static void ScheduleIndexOfTaskSingle(IndexOfSynchronizationContext context, RecyclableLongList<T> list, int blockIndex, T itemToFind)
|
||||
private static void IndexOfIn1Block(IndexOfSynchronizationContext context, RecyclableLongList<T> list, int blockIndex, T itemToFind)
|
||||
{
|
||||
_ = context.AllDoneSignal.AddParticipant();
|
||||
// & WAS SLOWER
|
||||
// _ = ThreadPool.QueueUserWorkItem<object?>((_) =>
|
||||
_ = Task.Factory.StartNew(() =>
|
||||
try
|
||||
{
|
||||
try
|
||||
if (context._isItemFound)
|
||||
{
|
||||
if (context._isItemFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
long index = Array.IndexOf(list._memoryBlocks[blockIndex], itemToFind, 0, list._blockSize);
|
||||
if (context._isItemFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (index >= 0)
|
||||
{
|
||||
context._isItemFound = true;
|
||||
_ = Interlocked.Exchange(ref context.FoundItemIndex, (blockIndex << list._blockSizePow2BitShift) + index);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
long index = Array.IndexOf(list._memoryBlocks[blockIndex], itemToFind, 0, list._blockSize);
|
||||
if (!context._isItemFound && index >= 0)
|
||||
{
|
||||
_ = Interlocked.Exchange(ref context.Exception, e);
|
||||
context._isItemFound = true;
|
||||
_ = Interlocked.Exchange(ref context.FoundItemIndex, (blockIndex << list._blockSizePow2BitShift) + index);
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.AllDoneSignal.RemoveParticipant();
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ = Interlocked.Exchange(ref context.Exception, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.RemoveParticipant();
|
||||
}
|
||||
}
|
||||
|
||||
private const int BlocksForEachTask = 4;
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static long IndexOfParallel(RecyclableLongList<T> list, in T item)
|
||||
public static long IndexOfParallel(RecyclableLongList<T> list, T item)
|
||||
{
|
||||
// & WAS SLOWER
|
||||
//long itemsCount = Array.IndexOf(list._memoryBlocks[0], item, 0, (int)Math.Min(list._blockSize, list._longCount));
|
||||
@@ -176,8 +117,6 @@ namespace Recyclable.Collections
|
||||
// (long)(_longCount * 0.329) => most efficient step, based on benchmarks
|
||||
//Iterate(this, context, item);
|
||||
|
||||
// TODO: Switch to use _blockSize instead of list._longCountIndeexOfStep for simplifications & better performance.
|
||||
|
||||
// & WAS SLOWER without
|
||||
int blockIndex = 2;
|
||||
// & WAS SLOWER
|
||||
@@ -191,7 +130,16 @@ namespace Recyclable.Collections
|
||||
while (blockIndex < lastBlockWithData)
|
||||
{
|
||||
// At this point itemIndex is limited to blockSize range - int.
|
||||
ScheduleIndexOfTask(context, list, blockIndex, item);
|
||||
|
||||
// & WAS SLOWER
|
||||
// _ = ThreadPool.QueueUserWorkItem<object?>((_) =>
|
||||
// Task.Factory.StartNew(() =>
|
||||
context.AddParticipant();
|
||||
var startingBlock = blockIndex;
|
||||
Task.Run(() => IndexOfIn4ConsecutiveBlocks(context, list, startingBlock, item));
|
||||
|
||||
// & WAS SLOWER
|
||||
// ScheduleIndexOfTask(context, list, blockIndex, item);
|
||||
if (context._isItemFound)
|
||||
{
|
||||
break;
|
||||
@@ -204,7 +152,15 @@ namespace Recyclable.Collections
|
||||
{
|
||||
while (blockIndex < list._lastBlockWithData)
|
||||
{
|
||||
ScheduleIndexOfTaskSingle(context, list, blockIndex, item);
|
||||
// & WAS SLOWER
|
||||
// _ = ThreadPool.QueueUserWorkItem<object?>((_) =>
|
||||
// Task.Factory.StartNew(() =>
|
||||
var startingBlock = blockIndex;
|
||||
context.AddParticipant();
|
||||
Task.Run(() => IndexOfIn1Block(context, list, startingBlock, item));
|
||||
|
||||
// & WAS SLOWER
|
||||
// ScheduleIndexOfTaskSingle(context, list, blockIndex, item);
|
||||
if (context._isItemFound)
|
||||
{
|
||||
break;
|
||||
@@ -224,7 +180,7 @@ namespace Recyclable.Collections
|
||||
}
|
||||
}
|
||||
|
||||
context.AllDoneSignal.SignalAndWait();
|
||||
context.SignalAndWait();
|
||||
if (context.Exception == null)
|
||||
{
|
||||
try
|
||||
@@ -245,7 +201,7 @@ namespace Recyclable.Collections
|
||||
{
|
||||
context.Dispose();
|
||||
}
|
||||
|
||||
|
||||
return RecyclableDefaults.ItemNotFoundIndexLong;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user