Merge branch 'master' of https://github.com/mlemanczyk/Recyclable.Collections.Benchmarks
This commit is contained in:
commit
e1f6e485fb
10 files changed
+359
No files matched your search
@@ -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
|
||||
@@ -0,0 +1,96 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Collections.Benchmarks.Core
|
||||
{
|
||||
public abstract class BaselineVsActualBenchmarkBase<TBenchmarkType>
|
||||
{
|
||||
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>(T benchmarkType) => new InvalidOperationException($"******* UNKNOWN BENCHMARK TYPE {{{benchmarkType}}} *******");
|
||||
protected static Exception CreateMethodNotFoundException(in string methodName, in string? className) => new MethodAccessException($"******* METHOD {{{methodName}}} NOT FOUND IN CLASS {{{className}}} *******");
|
||||
|
||||
protected virtual void PrepareData<T>(T benchmarkType) { }
|
||||
protected virtual Action? GetTestMethod(TBenchmarkType? benchmarkType)
|
||||
=> (Action?)GetType().GetMethod(GetTestMethodName(benchmarkType), BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)?.CreateDelegate(typeof(Action), this) ?? throw CreateMethodNotFoundException(GetTestMethodName(benchmarkType), GetType().FullName);
|
||||
|
||||
protected virtual string GetTestMethodName(TBenchmarkType? benchmarkType) => $"{benchmarkType}";
|
||||
|
||||
[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<TBenchmarkType>.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<TBenchmarkType>.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 *******");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputType>Library</OutputType>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<PackageId>Collections.Benchmarks.Core</PackageId>
|
||||
<PackageVersion>0.0.4</PackageVersion>
|
||||
<Authors>Marek Lemańczyk</Authors>
|
||||
<Company>N/A</Company>
|
||||
<Product>Collections.Benchmarks.Core</Product>
|
||||
<PackageTags>Recyclable Array Pool List Sortable Sort Benchmark Benchmarks Collection Collections</PackageTags>
|
||||
<Description>`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.</Description>
|
||||
<PackageProjectUrl>https://github.com/mlemanczyk/Collections.Benchmarks.Core</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/mlemanczyk/Collections.Benchmarks.Core</RepositoryUrl>
|
||||
<Copyright>Marek Lemańczyk 2023</Copyright>
|
||||
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageReleaseNotes>Recyclable.Collections updated. MethodNameSuffix added to DataDrivenBenchmarksBase to simplify usage.</PackageReleaseNotes>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<PackageIcon>logo.png</PackageIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\logo.png" Pack="true" PackagePath="" Link="logo.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="..\LICENSE.md" Pack="true" PackagePath="" Link="LICENSE.md">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="..\README.md" Pack="true" PackagePath="" Link="README.md">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.9" />
|
||||
<PackageReference Include="Collections.Pooled" Version="1.0.82" />
|
||||
<PackageReference Include="Recyclable.Collections" Version="0.0.6.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Collections.Benchmarks.Core
|
||||
{
|
||||
public enum CollectionsBenchmarkType
|
||||
{
|
||||
Unknown, Add_WithCapacity, Add, AddRange_WithCapacity, AddRangeWhenSourceIsArray, AddRangeWhenSourceIsIEnumerable,
|
||||
AddRangeWhenSourceIsIList, AddRangeWhenSourceIsList, AddRangeWhenSourceIsSameType, BinarySearch_BestAndWorstCases,
|
||||
Contains_FirstItems, Contains_LastItems, ConvertAll, Count, Create_WithCapacity, Create, Exists_BestAndWorstCases,
|
||||
Find_BestAndWorstCases, FindAll_BestAndWorstCases, FindLast_BestAndWorstCases, FindLastIndex_BestAndWorstCases,
|
||||
ForEach, GetItem, IndexOf_BestAndWorstCases,IndexOf_FirstItems, IndexOf_LastItems, LongCount,
|
||||
Remove_FirstItems, Remove_LastItems, RemoveAt_FirstItems, RemoveAt_LastItems,
|
||||
SetItem, VersionedForEach
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Collections.Benchmarks.Core
|
||||
{
|
||||
public enum CollectionsBenchmarksSource
|
||||
{
|
||||
Unknown,
|
||||
Array,
|
||||
List,
|
||||
PooledList,
|
||||
RecyclableList,
|
||||
RecyclableLongList,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Collections.Pooled;
|
||||
using Recyclable.Collections;
|
||||
|
||||
namespace Collections.Benchmarks.Core
|
||||
{
|
||||
[MemoryDiagnoser]
|
||||
public class DataDrivenBenchmarksBase<TTestCase> : BaselineVsActualBenchmarkBase<TTestCase>
|
||||
{
|
||||
protected int[]? _testObjects;
|
||||
protected int[] TestObjects => _testObjects ?? throw new NullReferenceException("Something is wrong and the field is not initialized");
|
||||
protected List<int>? _testObjectsAsList;
|
||||
protected List<int> TestObjectsAsList => _testObjectsAsList ?? throw new NullReferenceException("Something went wrong and the field is not initialized");
|
||||
protected PooledList<int>? _testObjectsAsPooledList;
|
||||
public PooledList<int> TestObjectsAsPooledList => _testObjectsAsPooledList ?? throw new NullReferenceException("Something is wrong and the field is not initialized");
|
||||
protected RecyclableList<int>? _testObjectsAsRecyclableList;
|
||||
protected RecyclableList<int> TestObjectsAsRecyclableList => _testObjectsAsRecyclableList ?? throw new NullReferenceException("Something is wrong and the field is not initialized");
|
||||
protected RecyclableLongList<int>? _testObjectsAsRecyclableLongList;
|
||||
protected RecyclableLongList<int> TestObjectsAsRecyclableLongList => _testObjectsAsRecyclableLongList ?? throw new NullReferenceException("Something is wrong and the field is not initialized");
|
||||
protected IEnumerable<int> TestObjectsAsIEnumerable
|
||||
{
|
||||
get
|
||||
{
|
||||
for (int i = 1; i <= TestObjectCount; i++)
|
||||
{
|
||||
yield return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override string GetTestMethodName(TTestCase? benchmarkType)
|
||||
{
|
||||
return $"{base.GetTestMethodName(benchmarkType)}{MethodNameSuffix}";
|
||||
}
|
||||
|
||||
protected override void PrepareData<T>(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();
|
||||
}
|
||||
|
||||
public virtual string MethodNameSuffix { get; } = string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -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<int> resultSpan = result;
|
||||
for (int i = 0; i < testObjectCount; i++)
|
||||
{
|
||||
resultSpan[i] = i;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static IEnumerable<long> EnumerateTestObjects(long testObjectCount)
|
||||
{
|
||||
Console.WriteLine("******* TEST DATA ARRAY CREATED *******");
|
||||
|
||||
for (int i = 1; i <= testObjectCount; i++)
|
||||
{
|
||||
yield return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<T>(in T item)
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
|
||||
public static void With<T>(in T item1, in T item2)
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
|
||||
public static void With<T>(ReadOnlySpan<T> span)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -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
|
||||
[](https://www.buymeacoffee.com/mlemanczyk)
|
||||
Reference in new issue
Block a user