From f4a03406e3162a5ba17d78ef6b8a3104e5d9bf75 Mon Sep 17 00:00:00 2001 From: mlemanczyk Date: Sat, 14 Oct 2023 23:29:57 +0200 Subject: [PATCH] Release 0.0.1 --- Collections.Benchmarks.Core.sln | 31 ++++++ .../BaselineVsActualBenchmarkBase.cs | 99 ++++++++++++++++++ .../Collections.Benchmarks.Core.csproj | 42 ++++++++ .../CollectionsBenchmarksBase.cs | 92 ++++++++++++++++ .../CollectionsBenchmarksSource.cs | 12 +++ Collections.Benchmarks.Core/DataGenerator.cs | 29 +++++ Collections.Benchmarks.Core/DoNothing.cs | 24 +++++ LICENSE.md | 10 ++ README.md | 9 +- logo.png | Bin 0 -> 3100 bytes 10 files changed, 347 insertions(+), 1 deletion(-) create mode 100644 Collections.Benchmarks.Core.sln create mode 100644 Collections.Benchmarks.Core/BaselineVsActualBenchmarkBase.cs create mode 100644 Collections.Benchmarks.Core/Collections.Benchmarks.Core.csproj create mode 100644 Collections.Benchmarks.Core/CollectionsBenchmarksBase.cs create mode 100644 Collections.Benchmarks.Core/CollectionsBenchmarksSource.cs create mode 100644 Collections.Benchmarks.Core/DataGenerator.cs create mode 100644 Collections.Benchmarks.Core/DoNothing.cs create mode 100644 LICENSE.md create mode 100644 logo.png diff --git a/Collections.Benchmarks.Core.sln b/Collections.Benchmarks.Core.sln new file mode 100644 index 0000000..ddd3dd9 --- /dev/null +++ b/Collections.Benchmarks.Core.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34031.279 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Collections.Benchmarks.Core", "Collections.Benchmarks.Core\Collections.Benchmarks.Core.csproj", "{CDB4A29F-D53D-4779-8DE2-EF6CA050DB11}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CDB4A29F-D53D-4779-8DE2-EF6CA050DB11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CDB4A29F-D53D-4779-8DE2-EF6CA050DB11}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CDB4A29F-D53D-4779-8DE2-EF6CA050DB11}.Debug|x64.ActiveCfg = Debug|x64 + {CDB4A29F-D53D-4779-8DE2-EF6CA050DB11}.Debug|x64.Build.0 = Debug|x64 + {CDB4A29F-D53D-4779-8DE2-EF6CA050DB11}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CDB4A29F-D53D-4779-8DE2-EF6CA050DB11}.Release|Any CPU.Build.0 = Release|Any CPU + {CDB4A29F-D53D-4779-8DE2-EF6CA050DB11}.Release|x64.ActiveCfg = Release|x64 + {CDB4A29F-D53D-4779-8DE2-EF6CA050DB11}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3626D07F-F4BD-4292-8087-4DE458A2ADB6} + EndGlobalSection +EndGlobal diff --git a/Collections.Benchmarks.Core/BaselineVsActualBenchmarkBase.cs b/Collections.Benchmarks.Core/BaselineVsActualBenchmarkBase.cs new file mode 100644 index 0000000..4e69ef7 --- /dev/null +++ b/Collections.Benchmarks.Core/BaselineVsActualBenchmarkBase.cs @@ -0,0 +1,99 @@ +using BenchmarkDotNet.Attributes; +using System.Numerics; + +namespace Collections.Benchmarks.Core +{ + public abstract class BaselineVsActualBenchmarkBase + { + public virtual TBenchmarkType? DataType { get; set; } + public virtual TBenchmarkType? BaseDataType { get; set; } + + private Action? _testMethod; + protected Action TestMethod + { + get => _testMethod ?? throw new OperationCanceledException("Skip - No actual defined"); + set => _testMethod = value; + } + + private Action? _baselineMethod; + protected Action BaselineMethod + { + get => _baselineMethod ?? throw new OperationCanceledException("Skip - No baseline defined"); + set => _baselineMethod = value; + } + + [Benchmark(Baseline = true)] + public void Baseline() => BaselineMethod.Invoke(); + + [Benchmark] + public void Actual() => TestMethod.Invoke(); + + public virtual int BlockSize { get; protected set; } + public byte BlockSizePow2BitShift => checked((byte)(31 - BitOperations.LeadingZeroCount((uint)BlockSize))); + public virtual int Divider { get; protected set; } = 10; + public virtual int TestObjectCount { get; set; } = 0; + public virtual int TestObjectCountForSlowMethods { get; private set; } + + protected static Exception CreateUnknownBenchmarkTypeException(T benchmarkType) + { + return new InvalidOperationException($"******* UNKNOWN BENCHMARK TYPE {{{benchmarkType}}} *******"); + } + + protected static Exception CreateMethodNotFoundException(in string methodName, in string? className) + { + return new MethodAccessException($"******* METHOD {{{methodName}}} NOT FOUND IN CLASS {{{className}}} *******"); + } + + protected virtual void PrepareData(T benchmarkType) { } + protected virtual Action? GetTestMethod(TBenchmarkType? benchmarkType) => null; + + [GlobalCleanup] + public virtual void Cleanup() + { + _baselineMethod = null; + _testMethod = null; + GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); + } + + [GlobalSetup] + public virtual void Setup() + { + Console.WriteLine($"******* SETTING UP TEST CASE FOR BENCHMARK {{{DataType}}} *******"); + + TestObjectCountForSlowMethods = (TestObjectCount / 2) + 1; + + BlockSize = TestObjectCount switch + { + 0 => 1, + > 0 and <= 10_240 => TestObjectCount, + _ => TestObjectCount / Divider > 0 ? TestObjectCount / Divider : TestObjectCount + }; + + if (BlockSize > 0) + { + BlockSize = checked((int)BitOperations.RoundUpToPowerOf2((uint)BlockSize)); + } + + Console.WriteLine($"******* BaseDataType = {BaseDataType}; default = {default(TBenchmarkType)} *******"); + + if (!EqualityComparer.Default.Equals(BaseDataType, default)) + { + Console.WriteLine("******* SETTING UP BASELINE DATA *******"); + PrepareData(BaseDataType); + _baselineMethod = GetTestMethod(BaseDataType); + } + + //~ We don't want to prepare data if baseline data is the same is benchmark data. + //~ One would override another - waste of time & resources. + if (!EqualityComparer.Default.Equals(BaseDataType, DataType)) + { + Console.WriteLine("******* SETTING UP TEST CASE DATA *******"); + //~ If BaseDataType == null, then we'll come here only if DataType != null. + PrepareData(DataType!); + _testMethod = GetTestMethod(DataType); + } + + Console.WriteLine("******* DATA PREPARED *******"); + } + } +} diff --git a/Collections.Benchmarks.Core/Collections.Benchmarks.Core.csproj b/Collections.Benchmarks.Core/Collections.Benchmarks.Core.csproj new file mode 100644 index 0000000..badc0fc --- /dev/null +++ b/Collections.Benchmarks.Core/Collections.Benchmarks.Core.csproj @@ -0,0 +1,42 @@ + + + + net6.0;net7.0 + enable + enable + Library + true + Collections.Benchmarks.Core + 0.0.1 + Marek Lemańczyk + N/A + Collections.Benchmarks.Core + Recyclable Array Pool List Sortable Sort Benchmark Benchmarks Collection Collections + `Recyclable.Collections.Benchmarks.Core` project is a basic open source framework for performance measurements based on various collection types like `RecyclableList<T>`, `RecyclableLongList<T>`, `List<T>`, `SortableList<T>`, `PriorityQueue<T>` & similar. + https://github.com/mlemanczyk/Collections.Benchmarks.Core + https://github.com/mlemanczyk/Collections.Benchmarks.Core + Marek Lemańczyk 2023 + LICENSE.md + README.md + git + Initial version. + AnyCPU;x64 + https://github.com/mlemanczyk/Collections.Benchmarks.Core/logo.png + + + + + PreserveNewest + + + PreserveNewest + + + + + + + + + + diff --git a/Collections.Benchmarks.Core/CollectionsBenchmarksBase.cs b/Collections.Benchmarks.Core/CollectionsBenchmarksBase.cs new file mode 100644 index 0000000..181e4d4 --- /dev/null +++ b/Collections.Benchmarks.Core/CollectionsBenchmarksBase.cs @@ -0,0 +1,92 @@ +using BenchmarkDotNet.Attributes; +using Collections.Pooled; +using Recyclable.Collections; + +namespace Collections.Benchmarks.Core +{ + [MemoryDiagnoser] + public class CollectionsBenchmarksBase : BaselineVsActualBenchmarkBase + { + protected int[]? _testObjects; + protected int[] TestObjects => _testObjects ?? throw new NullReferenceException("Something is wrong and the field is not initialized"); + protected List? _testObjectsAsList; + protected List TestObjectsAsList => _testObjectsAsList ?? throw new NullReferenceException("Something went wrong and the field is not initialized"); + protected PooledList? _testObjectsAsPooledList; + public PooledList TestObjectsAsPooledList => _testObjectsAsPooledList ?? throw new NullReferenceException("Something is wrong and the field is not initialized"); + protected RecyclableList? _testObjectsAsRecyclableList; + protected RecyclableList TestObjectsAsRecyclableList => _testObjectsAsRecyclableList ?? throw new NullReferenceException("Something is wrong and the field is not initialized"); + protected RecyclableLongList? _testObjectsAsRecyclableLongList; + protected RecyclableLongList TestObjectsAsRecyclableLongList => _testObjectsAsRecyclableLongList ?? throw new NullReferenceException("Something is wrong and the field is not initialized"); + protected IEnumerable TestObjectsAsIEnumerable + { + get + { + for (int i = 1; i <= TestObjectCount; i++) + { + yield return i; + } + } + } + + protected override void PrepareData(T benchmarkType) + { + Console.WriteLine($"******* PREPARING DATA FOR {benchmarkType} *******"); + switch (benchmarkType) + { + case CollectionsBenchmarksSource.Unknown: + break; + + case CollectionsBenchmarksSource.Array: + _testObjects ??= DataGenerator.EnumerateTestObjects(TestObjectCount); + break; + + case CollectionsBenchmarksSource.List: + _testObjectsAsList ??= TestObjects.ToList(); + break; + + case CollectionsBenchmarksSource.PooledList: + _testObjectsAsPooledList ??= new(TestObjects, ClearMode.Auto); + break; + + case CollectionsBenchmarksSource.RecyclableList: + _testObjectsAsRecyclableList ??= new(TestObjects, initialCapacity: TestObjectCount); + break; + + case CollectionsBenchmarksSource.RecyclableLongList: + _testObjectsAsRecyclableLongList ??= new(TestObjects, BlockSize, initialCapacity: base.TestObjectCount); + break; + + default: + throw CreateUnknownBenchmarkTypeException(benchmarkType); + } + + base.PrepareData(benchmarkType); + } + + public override void Cleanup() + { + Console.WriteLine("******* GLOBAL CLEANUP *******"); + + if (_testObjects?.Length > 0 && !_testObjects[0].GetType().IsValueType) + { + Array.Fill(_testObjects, 0); + } + + _testObjects = default; + _testObjectsAsList = default; + _testObjectsAsPooledList?.Dispose(); + _testObjectsAsPooledList = default; + _testObjectsAsRecyclableList?.Dispose(); + _testObjectsAsRecyclableList = default; + _testObjectsAsRecyclableLongList?.Dispose(); + _testObjectsAsRecyclableLongList = default; + base.Cleanup(); + } + + public override void Setup() + { + PrepareData(CollectionsBenchmarksSource.Array); + base.Setup(); + } + } +} diff --git a/Collections.Benchmarks.Core/CollectionsBenchmarksSource.cs b/Collections.Benchmarks.Core/CollectionsBenchmarksSource.cs new file mode 100644 index 0000000..647d546 --- /dev/null +++ b/Collections.Benchmarks.Core/CollectionsBenchmarksSource.cs @@ -0,0 +1,12 @@ +namespace Collections.Benchmarks.Core +{ + public enum CollectionsBenchmarksSource + { + Unknown, + Array, + List, + PooledList, + RecyclableList, + RecyclableLongList, + } +} diff --git a/Collections.Benchmarks.Core/DataGenerator.cs b/Collections.Benchmarks.Core/DataGenerator.cs new file mode 100644 index 0000000..08a2b1f --- /dev/null +++ b/Collections.Benchmarks.Core/DataGenerator.cs @@ -0,0 +1,29 @@ +namespace Collections.Benchmarks.Core +{ + public static class DataGenerator + { + public static int[] EnumerateTestObjects(int testObjectCount) + { + var result = new int[testObjectCount]; + Console.WriteLine("******* TEST DATA ARRAY CREATED *******"); + + Span resultSpan = result; + for (int i = 0; i < testObjectCount; i++) + { + resultSpan[i] = i; + } + + return result; + } + + public static IEnumerable EnumerateTestObjects(long testObjectCount) + { + Console.WriteLine("******* TEST DATA ARRAY CREATED *******"); + + for (int i = 1; i <= testObjectCount; i++) + { + yield return i; + } + } + } +} \ No newline at end of file diff --git a/Collections.Benchmarks.Core/DoNothing.cs b/Collections.Benchmarks.Core/DoNothing.cs new file mode 100644 index 0000000..a17680e --- /dev/null +++ b/Collections.Benchmarks.Core/DoNothing.cs @@ -0,0 +1,24 @@ +using System.Runtime.CompilerServices; + +#pragma warning disable IDE0060 // Remove unused parameter + +namespace Collections.Benchmarks.Core +{ + public static class DoNothing + { + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] + public static void With(in T item) + { + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] + public static void With(in T item1, in T item2) + { + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] + public static void With(ReadOnlySpan span) + { + } + } +} 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 704659f..971c5ae 100644 --- a/README.md +++ b/README.md @@ -1 +1,8 @@ -# Recyclable.Collections.Benchmarks \ No newline at end of file +# 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/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..078521cf60ab03135993f633a406fc69ba205aab GIT binary patch literal 3100 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