diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..0da7cad --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,10 @@ +# License +The project & source code is free for educational & personal use. **Any kind** of commercial use requires a donation, as prescribed below. + +# Donation requirements for a life-time, commercial use +* Commercial license is paid one time. +* It can be obtained by donating $10 for each developer seat, using the link provided below. If you hire 2 developers, you need to donate $20 for a life-time, commercial use of this package. +* Your license stays with you, if your developer moves somewhere else. + +# Link for donations +[![Donate](buy-me-a-coffee.svg)](https://www.buymeacoffee.com/mlemanczyk) \ No newline at end of file diff --git a/README.md b/README.md index 6e10073..971c5ae 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,8 @@ -# Recyclable.Collections.TestData -TBD +# Collections.Benchmarks.Core +`Collections.Benchmarks.Core` project is a basic open source framework for performance measurements based on various collection types like `RecyclableList`, `RecyclableLongList`, `List`, `SortableList`, `PriorityQueue` & similar. + +## Included +* `BaselineVsActualBenchmarkBase` +* `CollectionsBenchmarksBase` +* `DataGenerator` +* `DoNothing` diff --git a/Recyclable.Collections.TestData.sln b/Recyclable.Collections.TestData.sln new file mode 100644 index 0000000..e6ca371 --- /dev/null +++ b/Recyclable.Collections.TestData.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34031.279 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Recyclable.Collections.TestData", "Recyclable.Collections.TestData\Recyclable.Collections.TestData.csproj", "{C78BEDC4-71C7-4893-B4A0-5C632E4D376E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C78BEDC4-71C7-4893-B4A0-5C632E4D376E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C78BEDC4-71C7-4893-B4A0-5C632E4D376E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C78BEDC4-71C7-4893-B4A0-5C632E4D376E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C78BEDC4-71C7-4893-B4A0-5C632E4D376E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E83A8F25-1E42-4766-BAC6-E38822D64F8A} + EndGlobalSection +EndGlobal diff --git a/Recyclable.Collections.TestData/CustomIList.cs b/Recyclable.Collections.TestData/CustomIList.cs new file mode 100644 index 0000000..42f367f --- /dev/null +++ b/Recyclable.Collections.TestData/CustomIList.cs @@ -0,0 +1,33 @@ +using System.Collections; + +namespace Recyclable.Collections.TestData +{ + public class CustomIList : IList + { + protected List _list; + + public CustomIList() + { + _list = new List(); + } + + public CustomIList(IEnumerable items) + { + _list = new List(items); + } + + public T this[int index] { get => _list[index]; set => _list[index] = value; } + public int Count => _list.Count; + public bool IsReadOnly => false; + public void Add(T item) => _list.Add(item); + public void Clear() => _list.Clear(); + public bool Contains(T item) => _list.Contains(item); + public void CopyTo(T[] array, int arrayIndex) => _list.CopyTo(0, array, arrayIndex, _list.Count); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + public int IndexOf(T item) => _list.IndexOf(item); + public void Insert(int index, T item) => _list.Insert(index, item); + public bool Remove(T item) => _list.Remove(item); + public void RemoveAt(int index) => _list.RemoveAt(index); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + } +} \ No newline at end of file diff --git a/Recyclable.Collections.TestData/EnumerableWithoutCount.cs b/Recyclable.Collections.TestData/EnumerableWithoutCount.cs new file mode 100644 index 0000000..fda438e --- /dev/null +++ b/Recyclable.Collections.TestData/EnumerableWithoutCount.cs @@ -0,0 +1,17 @@ +using System.Collections; + +namespace Recyclable.Collections.TestData +{ + public class EnumerableWithoutCount : IEnumerable + { + private readonly IEnumerable _testData; + + public EnumerableWithoutCount(IEnumerable testData) + { + _testData = testData; + } + + public IEnumerator GetEnumerator() => _testData.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _testData.GetEnumerator(); + } +} \ No newline at end of file diff --git a/Recyclable.Collections.TestData/Recyclable.Collections.TestData.csproj b/Recyclable.Collections.TestData/Recyclable.Collections.TestData.csproj new file mode 100644 index 0000000..07f389a --- /dev/null +++ b/Recyclable.Collections.TestData/Recyclable.Collections.TestData.csproj @@ -0,0 +1,42 @@ + + + + net6.0;net7.0 + enable + enable + Library + true + Recyclable.Collections.TestData + 0.0.4 + Marek Lemańczyk + N/A + Recyclable.Collections.TestData + Recyclable Array Pool List Sortable Sort Benchmark Benchmarks Collection Collections Test Data + `Recyclable.Collections.TestData` project is a basic open source package for containing test data useful for testing collections like `RecyclableList<T>`, `RecyclableLongList<T>`, `List<T>`, `SortableList<T>`, `PriorityQueue<T>` & similar. + https://github.com/mlemanczyk/Recyclable.Collections.TestData + https://github.com/mlemanczyk/Recyclable.Collections.TestData + Marek Lemańczyk 2023 + LICENSE.md + README.md + git + Initial version extracted from `Recyclable.Collections` + AnyCPU;x64 + logo.png + + + + + + + + + + + + + + + + + + diff --git a/Recyclable.Collections.TestData/RecyclableLongListTestData.cs b/Recyclable.Collections.TestData/RecyclableLongListTestData.cs new file mode 100644 index 0000000..e164fcb --- /dev/null +++ b/Recyclable.Collections.TestData/RecyclableLongListTestData.cs @@ -0,0 +1,280 @@ +using MoreLinq; +using Open.Numeric.Primes; +using Recyclable.Collections; +using System.Numerics; + +namespace Recyclable.Collections.TestData +{ + public static class RecyclableLongListTestData + { + public static object[] SourceRefData { get; } = Enumerable.Range(1, 100).Cast().ToArray(); + + public static IEnumerable CreateTestData(long itemsCount) + { + for (long index = 1; index <= itemsCount; index++) + { + yield return index; + } + } + + public static IEnumerable CreateRefTestData(long itemsCount) + { + for (long index = 1; index <= itemsCount; index++) + { + yield return index; + } + } + + public static readonly IEnumerable ItemsCountVariants = new long[] + { + //RecyclableDefaults.MinPooledArrayLength - 5, RecyclableDefaults.MinPooledArrayLength - 1, RecyclableDefaults.MinPooledArrayLength, RecyclableDefaults.MinPooledArrayLength + 1, RecyclableDefaults.MinPooledArrayLength + 5, 127, 128, 129, RecyclableDefaults.MinItemsCountForParallelization + 0, 1, 2, 3, 7, 10, 16, RecyclableDefaults.MinPooledArrayLength - 5, RecyclableDefaults.MinPooledArrayLength - 1, RecyclableDefaults.MinPooledArrayLength, RecyclableDefaults.MinPooledArrayLength + 1, RecyclableDefaults.MinPooledArrayLength + 5, RecyclableDefaults.MinPooledArrayLength * 100 + }; + + public static readonly IEnumerable ItemsCountTestCases = ItemsCountVariants.Select(x => new object[] { (int)x }); + + public static readonly IEnumerable BlockSizeVariants = new int[] + { + 1, 2, 4, 16, RecyclableDefaults.MinPooledArrayLength - 5, (int)BitOperations.RoundUpToPowerOf2(RecyclableDefaults.MinPooledArrayLength) + }; + + private static bool IsValidBlockSize(long itemsCount, long blockSize) => itemsCount is < 1024 || blockSize is > 8; + + private static IEnumerable GetTargetDataVariants() + { + foreach (var itemsCount in ItemsCountVariants) + { + foreach (var targetBlockSize in BlockSizeVariants) + { + if (!IsValidBlockSize(itemsCount, targetBlockSize)) + { + continue; + } + + yield return new object[] { itemsCount, targetBlockSize }; + } + } + } + + public static IEnumerable TargetDataVariants => GetTargetDataVariants(); + + private static IEnumerable CombineSourceDataWithBlockSize(IEnumerable sourceData) + { + foreach (var testData in sourceData) + { + foreach (var targetBlockSize in BlockSizeVariants) + { + if (!IsValidBlockSize((long)testData[2], targetBlockSize)) + { + continue; + } + + yield return new object[] { testData[0], testData[1], testData[2], targetBlockSize }; + } + } + } + + private static IEnumerable CreateSourceDataVariants(long itemsCount, bool refType) + { + GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); + + switch (refType) + { + case false: + long[] testData = CreateTestData(itemsCount).ToArray(); + + yield return new object[] { $"Array[{itemsCount}]", testData, itemsCount }; + yield return new object[] { $"ICollection[{itemsCount}]", testData, itemsCount }; + yield return new object[] { $"ICollection[{itemsCount}]", testData, itemsCount }; + yield return new object[] { $"IEnumerable[{itemsCount}]", testData, itemsCount }; + yield return new object[] { $"IEnumerable[{itemsCount}] without non-enumerated count", new EnumerableWithoutCount(testData), itemsCount }; + yield return new object[] { $"IList[{itemsCount}]", new CustomIList(testData), itemsCount }; + yield return new object[] { $"IReadOnlyList[{itemsCount}])", testData, itemsCount }; + yield return new object[] { $"List[{itemsCount}]", testData.ToList(), itemsCount }; + yield return new object[] { $"ReadOnlySpan[{itemsCount}]", testData, itemsCount }; + yield return new object[] { $"RecyclableList[{itemsCount}]", testData.ToRecyclableList(), itemsCount }; + + foreach (var sourceBlockSize in BlockSizeVariants) + { + if (!IsValidBlockSize(itemsCount, sourceBlockSize)) + { + continue; + } + + yield return new object[] + { + $"RecyclableLongList[itemsCount: {itemsCount}, minBlockSize: {sourceBlockSize}]", + testData.ToRecyclableLongList(sourceBlockSize), + itemsCount + }; + } + + yield return new object[] { $"Span[{itemsCount}]", testData, itemsCount }; + yield return new object[] { $"T[{itemsCount}]", testData, itemsCount }; + break; + + case true: + IEnumerable testRefData = CreateRefTestData(itemsCount).ToArray(); + + yield return new object[] { $"Array[{itemsCount}]", testRefData, itemsCount }; + yield return new object[] { $"ICollection(itemsCount: {itemsCount}", testRefData, itemsCount }; + yield return new object[] { $"ICollection(itemsCount: {itemsCount}", testRefData, itemsCount }; + yield return new object[] { $"IEnumerable(itemsCount: {itemsCount})", testRefData, itemsCount }; + yield return new object[] { $"IEnumerable(itemsCount: {itemsCount}) without non-enumerated count", new EnumerableWithoutCount(testRefData), itemsCount }; + yield return new object[] { $"IList(itemsCount: {itemsCount})", new CustomIList(testRefData), itemsCount }; + yield return new object[] { $"IReadOnlyList(itemsCount: {itemsCount})", testRefData, itemsCount }; + yield return new object[] { $"List(itemsCount: {itemsCount}", testRefData.ToList(), itemsCount }; + yield return new object[] { $"ReadOnlySpan[{itemsCount}]", testRefData, itemsCount }; + yield return new object[] { $"RecyclableList(itemsCount: {itemsCount})", testRefData.ToRecyclableList(), itemsCount }; + + foreach (var sourceBlockSize in BlockSizeVariants) + { + if (!IsValidBlockSize(itemsCount, sourceBlockSize)) + { + continue; + } + + yield return new object[] + { + $"RecyclableLongList(itemsCount: {itemsCount}, minBlockSize: {sourceBlockSize})", + testRefData.ToRecyclableLongList(sourceBlockSize), + itemsCount + }; + } + + yield return new object[] { $"Span[{itemsCount}]", testRefData, itemsCount }; + yield return new object[] { $"T[{itemsCount}]", testRefData, itemsCount }; + break; + } + } + + private static IEnumerable CreateSourceDataVariants(bool refType) + { + foreach (var itemsCount in ItemsCountVariants) + { + foreach (var variant in CreateSourceDataVariants(itemsCount, refType)) + { + yield return variant; + } + } + } + + public static IEnumerable EmptySourceDataVariants => CreateSourceDataVariants(0, false); + public static IEnumerable SourceDataVariants => CreateSourceDataVariants(false); + public static IEnumerable SourceDataWithItemIndexVariants + { + get + { + foreach (var testCase in CreateSourceDataVariants(false).Where(testCase => (long)testCase[2] > 0)) + { + var itemIndexes = CreateItemIndexVariants((long)testCase[2], RecyclableDefaults.BlockSize).ToArray(); + yield return new object[] { testCase[0], testCase[1], testCase[2], itemIndexes }; + } + } + } + + public static IEnumerable SourceRefDataWithItemIndexVariants + { + get + { + IEnumerable testCases = CreateSourceDataVariants(true).Where(testCase => (long)testCase[2] > 0); + foreach (var testCase in testCases) + { + var itemIndexes = CreateItemIndexVariants((long)testCase[2], RecyclableDefaults.BlockSize).ToArray(); + + yield return new object[] { testCase[0], testCase[1], testCase[2], itemIndexes }; + } + } + } + + public static IEnumerable SourceDataWithBlockSizeVariants => CombineSourceDataWithBlockSize(SourceDataVariants); + public static IEnumerable SourceRefDataVariants => CreateSourceDataVariants(true); + public static IEnumerable SourceRefDataWithBlockSizeVariants { get; } = CombineSourceDataWithBlockSize(SourceRefDataVariants); + + private static IEnumerable _initialItemIndexVariants => new long[] { 0, 1, 2, 3, 4, 5 }; + private static IEnumerable CreateItemIndexVariants(long itemsCount, int blockSize) + { + IEnumerable primes = _initialItemIndexVariants.Concat( + _initialItemIndexVariants.SelectMany(index => new long[] { blockSize - index, blockSize + index }) + //new long[] { blockSize - 3, blockSize - 2, blockSize - 1, blockSize, blockSize + 1, blockSize + 2, blockSize + 3 } + ).ToArray(); + + IEnumerable second = Prime.Numbers.StartingAt(7) + .TakeUntil(prime => + prime > ((blockSize * 2) - 1) || + prime >= itemsCount + ); + + primes = primes.Concat(second).ToArray(); + + primes = primes.Concat( + primes.Select(index => itemsCount - index - 1) + ) + .Where(index => itemsCount > 0 && index >= 0 && index < itemsCount).ToArray() + .Distinct().ToArray(); + + return primes; + + //IEnumerable CreateItemIndexVariants() + //{ + //if (itemsCount > 0) + //{ + // yield return 0; + //} + + //foreach (long prime in primes) + //{ + // yield return prime; + + // long index = itemsCount - prime; + // if (index >= 0) + // { + // yield return index; + // } + //} + + //for (long index = itemsCount - (blockSize * 2) - 1; index < itemsCount; index++) + //{ + // if (index >= 0) + // { + // yield return index; + // } + //} + + //if ((blockSize / 16 * 16) + 1 < itemsCount) + //{ + // yield return (blockSize / 16 * 16) + 1; + //} + //} + + //return CreateItemIndexVariants(); + } + + public static IEnumerable CombineSourceRefDataWithBlockSizeWithItemIndex(bool refType) + { + IEnumerable sourceData = refType switch + { + false => SourceDataWithBlockSizeVariants, + true => SourceRefDataWithBlockSizeVariants + }; + + foreach (var testCase in sourceData) + { + long itemsCount = (long)testCase[2]; + if (itemsCount == 0) + { + continue; + } + + long[] itemIndexes = CreateItemIndexVariants(itemsCount, (int)testCase[3]) + .Where(_ => IsValidBlockSize((long)testCase[2], (int)testCase[3])) + .ToArray(); + + yield return new object[] { testCase[0], testCase[1], (long)testCase[2], (int)testCase[3], itemIndexes }; + } + } + + public static IEnumerable SourceDataWithBlockSizeWithItemIndexVariants { get; } = CombineSourceRefDataWithBlockSizeWithItemIndex(false); + public static IEnumerable SourceRefDataWithBlockSizeWithItemIndexVariants { get; } = CombineSourceRefDataWithBlockSizeWithItemIndex(true); + } +} \ No newline at end of file diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..da81460 Binary files /dev/null and b/logo.png differ