RecyclableLongList.CopyFollowingItems performance improvements. Benchmarks added. Tests cleanup & normalization. Before cleanup.

This commit is contained in:
mlemanczyk committed 2023-08-03 01:50:09 +02:00
1 parent 2996553776
commit 1e5f963148
3 files changed
+119 -7

No files matched your search

@@ -0,0 +1,36 @@
using BenchmarkDotNet.Attributes;
namespace Recyclable.Collections.Benchmarks.POC
{
public enum RecyclableLongListHelpersCopyFollowingItemsBenchmarkType
{
Undefined,
Old,
New,
}
public class RecyclableLongListHelpersCopyFollowingItemsBenchmarks : RecyclableBenchmarkBase<RecyclableLongListHelpersCopyFollowingItemsBenchmarkType>
{
[Params(RecyclableLongListHelpersCopyFollowingItemsBenchmarkType.Old)]
public override RecyclableLongListHelpersCopyFollowingItemsBenchmarkType BaseDataType { get => base.BaseDataType; set => base.BaseDataType = value; }
[Params(RecyclableLongListHelpersCopyFollowingItemsBenchmarkType.New)]
public override RecyclableLongListHelpersCopyFollowingItemsBenchmarkType DataType { get => base.DataType; set => base.DataType = value; }
protected override Action GetTestMethod(RecyclableLongListHelpersCopyFollowingItemsBenchmarkType benchmarkType) => benchmarkType switch
{
RecyclableLongListHelpersCopyFollowingItemsBenchmarkType.Old => CopyFollowingItemsOld,
RecyclableLongListHelpersCopyFollowingItemsBenchmarkType.New => CopyFollowingItemsNew,
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
};
protected override void PrepareData<RecyclableLongListHelpersCopyFollowingItemsBenchmarkType>(RecyclableLongListHelpersCopyFollowingItemsBenchmarkType benchmarkType)
{
base.PrepareData(RecyclableCollectionsBenchmarkSource.Array);
base.PrepareData(RecyclableCollectionsBenchmarkSource.RecyclableLongList);
}
public void CopyFollowingItemsOld() => RecyclableLongList<long>.Helpers.CopyFollowingItemsOld(TestObjectsAsRecyclableLongList, 0);
public void CopyFollowingItemsNew() => RecyclableLongList<long>.Helpers.CopyFollowingItems(TestObjectsAsRecyclableLongList, 0);
}
}
@@ -1,5 +1,4 @@
using Recyclable.Collections.Pools;
using System.Buffers;
using System.Numerics;
using System.Runtime.CompilerServices;
@@ -10,7 +9,7 @@ namespace Recyclable.Collections
internal static class Helpers
{
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void CopyFollowingItems(RecyclableLongList<T> list, long destinationItemIndex)
public static void CopyFollowingItemsOld(RecyclableLongList<T> list, long destinationItemIndex)
{
T[][] memoryBlocks = list._memoryBlocks;
int nextItemIndex = list._nextItemIndex;
@@ -55,6 +54,80 @@ namespace Recyclable.Collections
}
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void CopyFollowingItems(RecyclableLongList<T> list, long destinationItemIndex)
{
T[][] memoryBlocks = list._memoryBlocks;
int blockSizeMinus1 = list._blockSizeMinus1,
targetItemIndex = (int)(destinationItemIndex & blockSizeMinus1),
targetBlockIndex = (int)(destinationItemIndex >> list._blockSizePow2BitShift) + (targetItemIndex != 0 ? 1 : 0),
lastTakenBlockIndex = list._lastBlockWithData - 1;
// long remainingItems,
// itemsCount = list._longCount;
// Each iteration should
// 1. copy the remaining items in the current block (those after the deleted item)
// 2. copy the 1st item from the next block. We deleted 1 item which leaves 1 item gap in the block.
// We need to fill it.
// T[] currentMemoryBlock = memoryBlocks[targetBlockIndex];
if (targetItemIndex < blockSizeMinus1)
{
// new Span<T>(currentMemoryBlock, targetItemIndex + 1, blockSizeMinus1 - targetItemIndex)
// .CopyTo(new Span<T>(currentMemoryBlock, targetItemIndex, blockSizeMinus1 - targetItemIndex));
// remainingItems = blockSizeMinus1 - targetItemIndex;
new Span<T>(memoryBlocks[targetBlockIndex], targetItemIndex + 1, blockSizeMinus1 - targetItemIndex)
.CopyTo(new Span<T>(memoryBlocks[targetBlockIndex], targetItemIndex, blockSizeMinus1 - targetItemIndex));
// new Span<T>(currentMemoryBlock, targetItemIndex + 1, unchecked((int)remainingItems))
// .CopyTo(new Span<T>(currentMemoryBlock, targetItemIndex, unchecked((int)remainingItems)));
// Array.Copy(currentMemoryBlock, targetItemIndex + 1, currentMemoryBlock, targetItemIndex, remainingItems);
}
// else
// {
// remainingItems = destinationItemIndex;
// }
while (targetBlockIndex < lastTakenBlockIndex)
{
new Span<T>(memoryBlocks[targetBlockIndex])[blockSizeMinus1] = memoryBlocks[++targetBlockIndex][0];
// currentMemoryBlock[blockSizeMinus1] = memoryBlocks[++targetBlockIndex][0];
// new Span<T>(currentMemoryBlock)[blockSizeMinus1] = memoryBlocks[++targetBlockIndex][0];
// currentMemoryBlock = memoryBlocks[targetBlockIndex];
// if (remainingItems > 1)
// {
new Span<T>(memoryBlocks[targetBlockIndex], 1, blockSizeMinus1)
.CopyTo(new Span<T>(memoryBlocks[targetBlockIndex]));
// new Span<T>(currentMemoryBlock, 1, blockSizeMinus1)
// .CopyTo(new Span<T>(currentMemoryBlock));
// Array.Copy(currentMemoryBlock, 1, currentMemoryBlock, 0, blockSizeMinus1);
// remainingItems += blockSize;
// }
// else
// {
// remainingItems = 0;
// }
// new Span<T>(currentMemoryBlock, 1, blockSizeMinus1).CopyTo(new Span<T>(currentMemoryBlock));
}
if (lastTakenBlockIndex >= 0)
{
new Span<T>(memoryBlocks[targetBlockIndex])[blockSizeMinus1] = memoryBlocks[++targetBlockIndex][0];
if (list._nextItemIndex != 0)
{
new Span<T>(memoryBlocks[targetBlockIndex], 1, list._nextItemIndex - 1)
.CopyTo(new Span<T>(memoryBlocks[targetBlockIndex]));
}
else
{
new Span<T>(memoryBlocks[targetBlockIndex], 1, blockSizeMinus1)
.CopyTo(new Span<T>(memoryBlocks[targetBlockIndex]));
}
}
// currentMemoryBlock = memoryBlocks[lastTakenBlockIndex];
// Array.ConstrainedCopy(currentMemoryBlock, 1, currentMemoryBlock, 0, remainderLength);
}
public static void CopyTo(T[][] sourceMemoryBlocks, long startingIndex, int blockSize, long itemsCount, T[] destinationArray, int destinationArrayIndex)
{
if (itemsCount <= 0)
@@ -589,6 +589,7 @@ namespace Recyclable.CollectionsTests
{
// Prepare
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
var listAsArray = list.ToArray();
// Act & Validate
for (var deleted = 1; deleted <= itemsCount; deleted++)
@@ -599,7 +600,7 @@ namespace Recyclable.CollectionsTests
// Validate
_ = list.Count.Should().Be((int)(itemsCount - deleted));
_ = list.LastBlockWithData.Should().Be((int)(list.LongCount >> list.BlockSizePow2BitShift) - ((list.LongCount & list.BlockSizeMinus1) != 0 ? 0 : 1));
_ = list.Should().ContainInConsecutiveOrder(testData.Skip(deleted));
_ = list.Should().ContainInConsecutiveOrder(listAsArray[deleted..]);
}
_ = list.Should().BeEmpty();
@@ -613,6 +614,7 @@ namespace Recyclable.CollectionsTests
{
// Prepare
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
var listAsArray = list.ToArray();
// Act & Validate
for (var deleted = 1; deleted <= itemsCount; deleted++)
@@ -623,7 +625,7 @@ namespace Recyclable.CollectionsTests
// Validate
_ = list.Count.Should().Be((int)(itemsCount - deleted));
_ = list.LastBlockWithData.Should().Be((int)(list.LongCount >> list.BlockSizePow2BitShift) - ((list.LongCount & list.BlockSizeMinus1) != 0 ? 0 : 1));
_ = list.Should().ContainInConsecutiveOrder(testData.Take((int)(itemsCount - deleted)));
_ = list.Should().ContainInConsecutiveOrder(listAsArray[..(int)(itemsCount - deleted)]);
}
_ = list.Should().BeEmpty();
@@ -637,6 +639,7 @@ namespace Recyclable.CollectionsTests
{
// Prepare
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
var listAsArray = list.ToArray();
// Act & Validate
for (var deleted = 1; deleted <= itemsCount; deleted++)
@@ -647,7 +650,7 @@ namespace Recyclable.CollectionsTests
// Validate
_ = list.Count.Should().Be((int)(itemsCount - deleted));
_ = list.LastBlockWithData.Should().Be((int)(list.LongCount >> list.BlockSizePow2BitShift) - ((list.LongCount & list.BlockSizeMinus1) != 0 ? 0 : 1));
_ = list.Should().ContainInConsecutiveOrder(testData.Skip(deleted));
_ = list.Should().ContainInConsecutiveOrder(listAsArray[deleted..]);
}
_ = list.Should().BeEmpty();
@@ -661,14 +664,14 @@ namespace Recyclable.CollectionsTests
{
// Prepare
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
var expectedData = testData.ToArray();
var listAsArray = testData.ToArray();
int removedCount = 0;
foreach (var item in testData.Reverse())
{
// Act & Validate
_ = list.Remove(item).Should().BeTrue($"we search for {item} which should exist");
removedCount++;
var expectedList = expectedData[0..((int)itemsCount - removedCount)];
var expectedList = listAsArray[..((int)itemsCount - removedCount)];
_ = list.LastBlockWithData.Should().Be((int)(list.LongCount >> list.BlockSizePow2BitShift) - ((list.LongCount & list.BlockSizeMinus1) != 0 ? 0 : 1));
_ = list.LongCount.Should().Be(expectedList.Length);
_ = list.Should().ContainInConsecutiveOrder(expectedList);