Benchmarks mostly reorganized & cleaned up. Simplified for re-runs.

This commit is contained in:
mlemanczyk committed 2023-04-11 16:42:06 +02:00
1 parent eae5cb4452
commit 2d7e62df72
12 files changed
+2045 -1040

No files matched your search

@@ -1,24 +1,9 @@
using BenchmarkDotNet.Attributes;
using Recyclable.Collections.Benchmarks.POC;
namespace Recyclable.Collections.Benchmarks
{
public class BenchmarkBase
public class BenchmarkBase : PocBenchmarkBase
{
//[Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, 50, 60, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 1_000_000)]
//[Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, 50, 60, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 1_000_000)]
public long TestObjectCount = 1_000_000;
//[Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200)]
//[Params(10, 20, 50, 100)]
//[Params(1, 10)]
public int Divider = 10;
public int BlockSize => TestObjectCount switch
{
0 => 1,
> 0 and <= 10 => (int)TestObjectCount,
_ => TestObjectCount / Divider > 0 ? checked((int)(TestObjectCount / Divider)) : checked((int)TestObjectCount)
};
protected object[]? _testObjects;
protected object[] TestObjects => _testObjects ?? throw new NullReferenceException("Something is wrong and the field is not initialized");
@@ -1,252 +0,0 @@
using BenchmarkDotNet.Attributes;
using MiscUtil;
namespace Recyclable.Collections.Benchmarks
{
public delegate T Compare<T>(T x, T y);
public readonly struct MyComparerReadOnlyStruct<T> : IComparer<T>
{
public int Compare(T? x, T? y) => Operator.Subtract(x, y) switch
{
> 0 => 1,
< 0 => -1,
_ => 0,
};
}
public struct MyComparerStruct<T> : IComparer<T>
{
public int Compare(T? x, T? y) => Operator.Subtract(x, y) switch
{
> 0 => 1,
< 0 => -1,
_ => 0,
};
}
public class MyComparer : IComparer<long>
{
public int Compare(long x, long y) => (x - y) switch
{
> 0 => 1,
< 0 => -1,
_ => 0,
};
}
[MemoryDiagnoser]
public class DelegateVsComparerBenchmark
{
private static readonly Comparer<long> _comparer = Comparer<long>.Default;
private const int _objectCount = 1_000_000;
private static readonly IEnumerable<long> _testObjects = Enumerable.Range(1, _objectCount).Select(x => (long)x);
private static readonly IEnumerable<Func<long, long, int>> _testListOfFunctions = _testObjects.Select<long, Func<long, long, int>>(static x => new MyComparer().Compare);
private static readonly IEnumerable<IComparer<long>> _testListOfComparers = _testObjects.Select(x => (IComparer<long>)new MyComparer());
private static readonly IEnumerable<Compare<long>> _testListOfDelegates = _testObjects.Select(x => new Compare<long>((x, y) => new MyComparer().Compare(x, y)));
private static readonly IEnumerable<Func<long, long, int>> _testListOfLocalFunctions = GetLocalFunctions(_testObjects);
private static readonly IEnumerable<MyComparer> _testListOfClasses = _testObjects.Select(x => new MyComparer());
private static readonly IEnumerable<MyComparerStruct<long>> _testListOfStructs = _testObjects.Select(x => new MyComparerStruct<long>());
private static readonly IEnumerable<IComparer<long>> _testListOfBoxedStructs = _testObjects.Select(x => (IComparer<long>)new MyComparerStruct<long>());
private static IEnumerable<TComparer> _testListOfUnboxedStructs<TComparer, TValue>() where TComparer : IComparer<TValue>, new()
{
foreach (var item in _testObjects)
{
yield return new TComparer();
}
}
private static IEnumerable<Func<long, long, int>> GetLocalFunctions(IEnumerable<long> testObjects)
{
static int compareFunc(long x, long y)
{
return (x - y) switch
{
> 0 => 1,
< 0 => -1,
_ => 0
};
}
foreach (var item in testObjects)
{
yield return compareFunc;
}
}
private static void TestWithComparer(IComparer<long> comparer)
{
var testData = _testObjects.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length - 1; itemIdx++)
{
_ = comparer.Compare(testData[itemIdx], testData[itemIdx + 1]);
}
}
//[Benchmark(Baseline = true)]
public void Comparer()
{
TestWithComparer(_comparer);
}
private static void TestWithFunc(Func<long, long, int> compareFunc)
{
var testData = _testObjects.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length - 1; itemIdx++)
{
_ = compareFunc(testData[itemIdx], testData[itemIdx + 1]);
}
}
//[Benchmark]
public void Func()
{
TestWithFunc((x, y) => (x - y) switch
{
> 0 => 1,
< 0 => -1,
_ => 0
});
}
//[Benchmark]
public void StaticFunc()
{
TestWithFunc(static (x, y) => (x - y) switch
{
> 0 => 1,
< 0 => -1,
_ => 0
});
}
private static void TestWithDelegate<T>(Compare<long> compareFunc)
{
var testData = _testObjects.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length - 1; itemIdx++)
{
_ = compareFunc(testData[itemIdx], testData[itemIdx + 1]);
}
}
//[Benchmark]
public void Delegate()
{
TestWithDelegate<long>((x, y) => (x - y) switch
{
> 0 => 1,
< 0 => -1,
_ => 0
});
}
//[Benchmark]
public void ListOfFuncs()
{
const long x = 2, y = 5;
var testData = _testListOfFunctions.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var compareFunc = testData[itemIdx];
_ = compareFunc(x, y);
}
}
//[Benchmark]
public void ListOfLocalFuncs()
{
const long x = 2, y = 5;
var testData = _testListOfLocalFunctions.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var compareFunc = testData[itemIdx];
_ = compareFunc(x, y);
}
}
//[Benchmark]
public void ListOfComparers()
{
const long x = 2, y = 5;
var testData = _testListOfComparers.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var comparer = testData[itemIdx];
_ = comparer.Compare(x, y);
}
}
//[Benchmark]
public void ListOfClasses()
{
const long x = 2, y = 5;
var testData = _testListOfClasses.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var comparer = testData[itemIdx];
_ = comparer.Compare(x, y);
}
}
//[Benchmark]
public void ListOfStructs()
{
const long x = 2, y = 5;
var testData = _testListOfStructs.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var comparer = testData[itemIdx];
_ = comparer.Compare(x, y);
}
}
//[Benchmark]
public void ListOfBoxedStructs()
{
const long x = 2, y = 5;
var testData = _testListOfBoxedStructs.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var comparer = testData[itemIdx];
_ = comparer.Compare(x, y);
}
}
//[Benchmark]
public void ListOfUnboxedStructs()
{
const long x = 2, y = 5;
var testData = _testListOfUnboxedStructs<MyComparerStruct<long>, long>().ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var comparer = testData[itemIdx];
_ = comparer.Compare(x, y);
}
}
//[Benchmark]
public void ListOfUnboxedReadOnlyStructs()
{
const long x = 2, y = 5;
var testData = _testListOfUnboxedStructs<MyComparerReadOnlyStruct<long>, long>().ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var comparer = testData[itemIdx];
_ = comparer.Compare(x, y);
}
}
//[Benchmark]
public void ListOfDelegates()
{
const long x = 2, y = 5;
var testData = _testListOfDelegates.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var compareDelegate = testData[itemIdx];
_ = compareDelegate(x, y);
}
}
}
}
@@ -1,355 +0,0 @@
using BenchmarkDotNet.Attributes;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
namespace Recyclable.Collections.Benchmarks
{
[MemoryDiagnoser]
public class ModuloBenchmarks
{
private long _cutoff;
private Vector128<long> _increment;
private long _lastCaseIndex;
[GlobalSetup]
public void Setup()
{
_cutoff = BlockSize << 4;
_increment = Vector128.Create(-BlockSize, 1L);
_lastCaseIndex = 0;
}
private static void DoNothing<T>(T result, T remainder, [CallerMemberName] string? callerName = null)
{
//Console.WriteLine($"{callerName}: result = {result}, remainder = {remainder}");
}
//[Params(123, 10, 2, int.MaxValue, int.MaxValue / 2, int.MaxValue / 3, int.MaxValue / 4, int.MaxValue / 6, int.MaxValue / 10, int.MaxValue / 15, int.MaxValue / 20)]
[Params(int.MaxValue, int.MaxValue / 2, int.MaxValue / 3, int.MaxValue / 4, int.MaxValue / 6, int.MaxValue / 8, int.MaxValue / 10, int.MaxValue / 100)]
//[Params(int.MaxValue / 10, int.MaxValue / 20, int.MaxValue / 100)]
public int BlockSize { get; set; } = int.MaxValue;
[Params(int.MaxValue, int.MaxValue / 1000, int.MaxValue / 10000)]
//[Params(int.MaxValue)]
//[Params(int.MaxValue * 100L)]
public long TestNumber { get; set; } = int.MaxValue;
private void CalculateDividerAndModulusWithOperator(long i)
{
DoNothing(i / BlockSize, i % BlockSize);
}
private void CalculateWithMathDivRem(long i)
{
DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder);
}
private void CalculateWithAddWithFor(long i)
{
int result = 0;
int remainder = 0;
int blockSize = BlockSize;
for (long iteration = 0; iteration < i; iteration++)
{
if (remainder > blockSize - 1)
{
remainder = 0;
result++;
}
remainder++;
}
DoNothing(result, remainder);
}
private void CalculateWithAddWithForOptimized(long i)
{
long total = 0;
int result = 0;
int blockSize = BlockSize;
while (total + blockSize <= i)
{
total += blockSize;
result++;
}
DoNothing(result, i - total);
}
private void CalculateWithXDivYMulX(long i)
{
DoNothing(i / BlockSize, i - (i / BlockSize * BlockSize));
}
private void CalculateWithXDivYMulXWithLocalVar(long i)
{
int blockSize = BlockSize;
int result = (int)(i / blockSize);
DoNothing(result, i - (result * blockSize));
}
private void CalculateWithSubtractWithWhile(long i)
{
int result = 0;
long remainder = i;
int blockSize = BlockSize;
while (remainder >= blockSize)
{
remainder -= blockSize;
result++;
}
DoNothing(result, remainder);
}
private void CalculateWithSubractWithWhileWithSse2(long i)
{
var state = Vector128.Create(i, 0);
int blockSize = BlockSize;
var inc = _increment;
while (state.GetElement(0) >= blockSize)
{
state = Sse2.Add(state, inc);
}
DoNothing(state.GetElement(1), state.GetElement(0));
}
private void CalculateWithMixedApproachWithSubractWithWhile(long i)
{
long remainder;
if (i >= _cutoff)
{
DoNothing(Math.DivRem(i, BlockSize, out remainder), remainder);
}
else
{
int result = 0;
int blockSize = BlockSize;
remainder = i;
while (remainder >= blockSize)
{
remainder -= blockSize;
result++;
}
DoNothing(result, remainder);
}
}
private void CalculateWithMixedApproachWithSubtractWithWhileAsVector(long i)
{
if (i >= _cutoff)
{
DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder);
}
else
{
var state = new Vector<long>(new[] { 0L, i, 0, 0 });
var increment = new Vector<long>(new[] { 1L, -BlockSize, 0, 0 });
int blockSize = BlockSize;
while (state[1] >= blockSize)
{
state += increment;
}
DoNothing(state[0], state[1]);
}
}
private void CalculateWithMixedApproachWithSubtractWithWhileWithSse2(long i)
{
if (i >= _cutoff)
{
DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder);
}
else
{
var increment = _increment;
var state = Vector128.Create(i, 0);
int blockSize = BlockSize;
while (state.GetElement(0) >= blockSize)
{
state = Sse2.Add(state, increment);
}
DoNothing(state.GetElement(1), state.GetElement(0));
}
}
private void CalculateWithMixedApproachWithSubtractWithStackalloc(long i)
{
if (i >= _cutoff)
{
DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder);
}
else
{
Span<long> state = stackalloc long[2] { i, 0 };// Vector128.Create(BlockSize, 0);
int blockSize = BlockSize;
while (state[0] >= blockSize)
{
state[0] -= blockSize;
state[1]++;
}
DoNothing(state[1], state[0]);
}
}
private void CalculateWithMixedApproachWithForLoopOptimized(long i)
{
long total;
if (i >= _cutoff)
{
DoNothing(Math.DivRem(i, BlockSize, out total), total);
}
else
{
total = 0;
int result = 0;
int blockSize = BlockSize;
while (total + blockSize <= i)
{
total += blockSize;
result++;
}
DoNothing(result, i - total);
}
}
[Benchmark(Baseline = true)]
public void DividerAndModulusWithOperator()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateDividerAndModulusWithOperator(i);
}
}
[Benchmark]
public void MathDivRem()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithMathDivRem(i);
}
}
[Benchmark]
public void AddWithFor()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithAddWithFor(i);
}
}
[Benchmark(Baseline = true)]
public void AddWithForOptimized()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithAddWithForOptimized(i);
}
}
[Benchmark]
public void XDivYMulX()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithXDivYMulX(i);
}
}
[Benchmark]
public void XDivYMulXWithLocalVar()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithXDivYMulXWithLocalVar(i);
}
}
[Benchmark]
public void SubtractWithWhile()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithSubtractWithWhile(i);
}
}
[Benchmark]
public void SubtractWithWhileWithSse2()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithSubractWithWhileWithSse2(i);
}
}
[Benchmark(Baseline = false)]
public void MixedApproachWithSubtractWithWhile()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithMixedApproachWithSubractWithWhile(i);
}
}
[Benchmark(Baseline = false)]
public void MixedApproachWithSubtractWithWhileAsVector()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithMixedApproachWithSubtractWithWhileAsVector(i);
}
}
[Benchmark(Baseline = false)]
public void MixedApproachWithSubtractWithWhileWithSse2()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithMixedApproachWithSubtractWithWhileWithSse2(i);
}
}
[Benchmark(Baseline = false)]
public void MixedApproachWithSubtractWithStackalloc()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithMixedApproachWithSubtractWithStackalloc(i);
}
}
[Benchmark(Baseline = false)]
public void MixedApproachWithForLoopOptimized()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithMixedApproachWithForLoopOptimized(i);
}
}
}
}
@@ -0,0 +1,55 @@
using BenchmarkDotNet.Attributes;
using System.Buffers;
namespace Recyclable.Collections.Benchmarks.POC
{
public class ArraySizeLimitPocBenchmarks : PocBenchmarkBase
{
public static void Run()
{
var benchmark = new ArraySizeLimitPocBenchmarks();
benchmark.FindArraySizeLimit();
}
[Benchmark]
public void FindArraySizeLimit()
{
var pool = ArrayPool<object>.Shared;
var bufferSize = int.MaxValue;
var oldValue = 0;
while (true)
{
try
{
Console.WriteLine($"Allocating {bufferSize}");
var arr = pool.Rent((int)bufferSize);
var nextValue = (oldValue - bufferSize) >> 1;
if (nextValue == 0)
{
break;
}
oldValue = bufferSize;
bufferSize += nextValue;
pool.Return(arr);
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
}
catch (Exception)
{
Console.WriteLine($"Error during array creation when {nameof(bufferSize)} = {bufferSize}");
var nextValue = (oldValue - bufferSize) >> 1;
if (nextValue == 0)
{
break;
}
oldValue = bufferSize;
bufferSize -= nextValue;
}
}
Console.WriteLine("All done");
}
}
}
@@ -0,0 +1,274 @@
using BenchmarkDotNet.Attributes;
using MiscUtil;
namespace Recyclable.Collections.Benchmarks.POC
{
[MemoryDiagnoser]
public class DelegateVsComparerPocBenchmarks : PocBenchmarkBase
{
#region Dummy classes
private delegate T Compare<T>(T x, T y);
public readonly struct MyComparerReadOnlyStruct<T> : IComparer<T>
{
public int Compare(T? x, T? y) => Operator.Subtract(x, y) switch
{
> 0 => 1,
< 0 => -1,
_ => 0,
};
}
private struct MyComparerStruct<T> : IComparer<T>
{
public int Compare(T? x, T? y) => Operator.Subtract(x, y) switch
{
> 0 => 1,
< 0 => -1,
_ => 0,
};
}
private class MyComparer : IComparer<long>
{
public int Compare(long x, long y) => (x - y) switch
{
> 0 => 1,
< 0 => -1,
_ => 0,
};
}
#endregion
public static void Run()
{
var benchmark = new DelegateVsComparerPocBenchmarks();
benchmark.Comparer();
benchmark.Delegate();
benchmark.Func();
benchmark.ListOfBoxedStructs();
benchmark.ListOfClasses();
benchmark.ListOfComparers();
benchmark.ListOfDelegates();
benchmark.ListOfFuncs();
benchmark.ListOfLocalFuncs();
benchmark.ListOfStructs();
benchmark.ListOfUnboxedReadOnlyStructs();
benchmark.ListOfUnboxedStructs();
benchmark.StaticFunc();
}
private static readonly Comparer<long> _comparer = Comparer<long>.Default;
private const int _objectCount = 1_000_000;
private static readonly IEnumerable<long> _testObjects = Enumerable.Range(1, _objectCount).Select(x => (long)x);
private static readonly IEnumerable<Func<long, long, int>> _testListOfFunctions = _testObjects.Select<long, Func<long, long, int>>(static x => new MyComparer().Compare);
private static readonly IEnumerable<IComparer<long>> _testListOfComparers = _testObjects.Select(x => (IComparer<long>)new MyComparer());
private static readonly IEnumerable<Compare<long>> _testListOfDelegates = _testObjects.Select(x => new Compare<long>((x, y) => new MyComparer().Compare(x, y)));
private static readonly IEnumerable<Func<long, long, int>> _testListOfLocalFunctions = GetLocalFunctions(_testObjects);
private static readonly IEnumerable<MyComparer> _testListOfClasses = _testObjects.Select(x => new MyComparer());
private static readonly IEnumerable<MyComparerStruct<long>> _testListOfStructs = _testObjects.Select(x => new MyComparerStruct<long>());
private static readonly IEnumerable<IComparer<long>> _testListOfBoxedStructs = _testObjects.Select(x => (IComparer<long>)new MyComparerStruct<long>());
private static IEnumerable<TComparer> _testListOfUnboxedStructs<TComparer, TValue>() where TComparer : IComparer<TValue>, new()
{
foreach (var item in _testObjects)
{
yield return new TComparer();
}
}
private static IEnumerable<Func<long, long, int>> GetLocalFunctions(IEnumerable<long> testObjects)
{
static int compareFunc(long x, long y)
{
return (x - y) switch
{
> 0 => 1,
< 0 => -1,
_ => 0
};
}
foreach (var item in testObjects)
{
yield return compareFunc;
}
}
private static void TestWithComparer(IComparer<long> comparer)
{
var testData = _testObjects.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length - 1; itemIdx++)
{
_ = comparer.Compare(testData[itemIdx], testData[itemIdx + 1]);
}
}
[Benchmark(Baseline = true)]
public void Comparer()
{
TestWithComparer(_comparer);
}
private static void TestWithFunc(Func<long, long, int> compareFunc)
{
var testData = _testObjects.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length - 1; itemIdx++)
{
_ = compareFunc(testData[itemIdx], testData[itemIdx + 1]);
}
}
[Benchmark]
public void Func()
{
TestWithFunc((x, y) => (x - y) switch
{
> 0 => 1,
< 0 => -1,
_ => 0
});
}
[Benchmark]
public void StaticFunc()
{
TestWithFunc(static (x, y) => (x - y) switch
{
> 0 => 1,
< 0 => -1,
_ => 0
});
}
private static void TestWithDelegate<T>(Compare<long> compareFunc)
{
var testData = _testObjects.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length - 1; itemIdx++)
{
_ = compareFunc(testData[itemIdx], testData[itemIdx + 1]);
}
}
[Benchmark]
public void Delegate()
{
TestWithDelegate<long>((x, y) => (x - y) switch
{
> 0 => 1,
< 0 => -1,
_ => 0
});
}
[Benchmark]
public void ListOfFuncs()
{
const long x = 2, y = 5;
var testData = _testListOfFunctions.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var compareFunc = testData[itemIdx];
_ = compareFunc(x, y);
}
}
[Benchmark]
public void ListOfLocalFuncs()
{
const long x = 2, y = 5;
var testData = _testListOfLocalFunctions.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var compareFunc = testData[itemIdx];
_ = compareFunc(x, y);
}
}
[Benchmark]
public void ListOfComparers()
{
const long x = 2, y = 5;
var testData = _testListOfComparers.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var comparer = testData[itemIdx];
_ = comparer.Compare(x, y);
}
}
[Benchmark]
public void ListOfClasses()
{
const long x = 2, y = 5;
var testData = _testListOfClasses.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var comparer = testData[itemIdx];
_ = comparer.Compare(x, y);
}
}
[Benchmark]
public void ListOfStructs()
{
const long x = 2, y = 5;
var testData = _testListOfStructs.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var comparer = testData[itemIdx];
_ = comparer.Compare(x, y);
}
}
[Benchmark]
public void ListOfBoxedStructs()
{
const long x = 2, y = 5;
var testData = _testListOfBoxedStructs.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var comparer = testData[itemIdx];
_ = comparer.Compare(x, y);
}
}
[Benchmark]
public void ListOfUnboxedStructs()
{
const long x = 2, y = 5;
var testData = _testListOfUnboxedStructs<MyComparerStruct<long>, long>().ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var comparer = testData[itemIdx];
_ = comparer.Compare(x, y);
}
}
[Benchmark]
public void ListOfUnboxedReadOnlyStructs()
{
const long x = 2, y = 5;
var testData = _testListOfUnboxedStructs<MyComparerReadOnlyStruct<long>, long>().ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var comparer = testData[itemIdx];
_ = comparer.Compare(x, y);
}
}
[Benchmark]
public void ListOfDelegates()
{
const long x = 2, y = 5;
var testData = _testListOfDelegates.ToArray();
for (var itemIdx = 0; itemIdx < testData.Length; itemIdx++)
{
var compareDelegate = testData[itemIdx];
_ = compareDelegate(x, y);
}
}
}
}
@@ -0,0 +1,374 @@
using BenchmarkDotNet.Attributes;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
namespace Recyclable.Collections.Benchmarks.POC
{
[MemoryDiagnoser]
public class ModuloPocBenchmarks
{
public static void Run()
{
var benchmark = new ModuloPocBenchmarks();
benchmark.Setup();
benchmark.AddWithFor();
benchmark.AddWithForOptimized();
benchmark.DividerAndModulusWithOperator();
benchmark.MathDivRem();
benchmark.MixedApproachWithForLoopOptimized();
benchmark.MixedApproachWithSubtractWithStackalloc();
benchmark.MixedApproachWithSubtractWithWhile();
benchmark.MixedApproachWithSubtractWithWhileAsVector();
benchmark.MixedApproachWithSubtractWithWhileWithSse2();
benchmark.SubtractWithWhile();
benchmark.SubtractWithWhileWithSse2();
benchmark.XDivYMulX();
benchmark.XDivYMulXWithLocalVar();
}
private long _cutoff;
private Vector128<long> _increment;
private long _lastCaseIndex;
[GlobalSetup]
public void Setup()
{
_cutoff = BlockSize << 4;
_increment = Vector128.Create(-BlockSize, 1L);
_lastCaseIndex = 0;
}
private static void DoNothing<T>(T result, T remainder, [CallerMemberName] string? callerName = null)
{
//Console.WriteLine($"{callerName}: result = {result}, remainder = {remainder}");
}
//[Params(123, 10, 2, int.MaxValue, int.MaxValue / 2, int.MaxValue / 3, int.MaxValue / 4, int.MaxValue / 6, int.MaxValue / 10, int.MaxValue / 15, int.MaxValue / 20)]
[Params(int.MaxValue, int.MaxValue / 2, int.MaxValue / 3, int.MaxValue / 4, int.MaxValue / 6, int.MaxValue / 8, int.MaxValue / 10, int.MaxValue / 100)]
//[Params(int.MaxValue / 10, int.MaxValue / 20, int.MaxValue / 100)]
public int BlockSize { get; set; } = int.MaxValue;
[Params(int.MaxValue, int.MaxValue / 1000, int.MaxValue / 10000)]
//[Params(int.MaxValue)]
//[Params(int.MaxValue * 100L)]
public long TestNumber { get; set; } = int.MaxValue;
private void CalculateDividerAndModulusWithOperator(long i)
{
DoNothing(i / BlockSize, i % BlockSize);
}
private void CalculateWithMathDivRem(long i)
{
DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder);
}
private void CalculateWithAddWithFor(long i)
{
int result = 0;
int remainder = 0;
int blockSize = BlockSize;
for (long iteration = 0; iteration < i; iteration++)
{
if (remainder > blockSize - 1)
{
remainder = 0;
result++;
}
remainder++;
}
DoNothing(result, remainder);
}
private void CalculateWithAddWithForOptimized(long i)
{
long total = 0;
int result = 0;
int blockSize = BlockSize;
while (total + blockSize <= i)
{
total += blockSize;
result++;
}
DoNothing(result, i - total);
}
private void CalculateWithXDivYMulX(long i)
{
DoNothing(i / BlockSize, i - (i / BlockSize * BlockSize));
}
private void CalculateWithXDivYMulXWithLocalVar(long i)
{
int blockSize = BlockSize;
int result = (int)(i / blockSize);
DoNothing(result, i - (result * blockSize));
}
private void CalculateWithSubtractWithWhile(long i)
{
int result = 0;
long remainder = i;
int blockSize = BlockSize;
while (remainder >= blockSize)
{
remainder -= blockSize;
result++;
}
DoNothing(result, remainder);
}
private void CalculateWithSubractWithWhileWithSse2(long i)
{
var state = Vector128.Create(i, 0);
int blockSize = BlockSize;
var inc = _increment;
while (state.GetElement(0) >= blockSize)
{
state = Sse2.Add(state, inc);
}
DoNothing(state.GetElement(1), state.GetElement(0));
}
private void CalculateWithMixedApproachWithSubractWithWhile(long i)
{
long remainder;
if (i >= _cutoff)
{
DoNothing(Math.DivRem(i, BlockSize, out remainder), remainder);
}
else
{
int result = 0;
int blockSize = BlockSize;
remainder = i;
while (remainder >= blockSize)
{
remainder -= blockSize;
result++;
}
DoNothing(result, remainder);
}
}
private void CalculateWithMixedApproachWithSubtractWithWhileAsVector(long i)
{
if (i >= _cutoff)
{
DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder);
}
else
{
var state = new Vector<long>(new[] { 0L, i, 0, 0 });
var increment = new Vector<long>(new[] { 1L, -BlockSize, 0, 0 });
int blockSize = BlockSize;
while (state[1] >= blockSize)
{
state += increment;
}
DoNothing(state[0], state[1]);
}
}
private void CalculateWithMixedApproachWithSubtractWithWhileWithSse2(long i)
{
if (i >= _cutoff)
{
DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder);
}
else
{
var increment = _increment;
var state = Vector128.Create(i, 0);
int blockSize = BlockSize;
while (state.GetElement(0) >= blockSize)
{
state = Sse2.Add(state, increment);
}
DoNothing(state.GetElement(1), state.GetElement(0));
}
}
private void CalculateWithMixedApproachWithSubtractWithStackalloc(long i)
{
if (i >= _cutoff)
{
DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder);
}
else
{
Span<long> state = stackalloc long[2] { i, 0 };// Vector128.Create(BlockSize, 0);
int blockSize = BlockSize;
while (state[0] >= blockSize)
{
state[0] -= blockSize;
state[1]++;
}
DoNothing(state[1], state[0]);
}
}
private void CalculateWithMixedApproachWithForLoopOptimized(long i)
{
long total;
if (i >= _cutoff)
{
DoNothing(Math.DivRem(i, BlockSize, out total), total);
}
else
{
total = 0;
int result = 0;
int blockSize = BlockSize;
while (total + blockSize <= i)
{
total += blockSize;
result++;
}
DoNothing(result, i - total);
}
}
[Benchmark(Baseline = true)]
public void DividerAndModulusWithOperator()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateDividerAndModulusWithOperator(i);
}
}
[Benchmark]
public void MathDivRem()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithMathDivRem(i);
}
}
[Benchmark]
public void AddWithFor()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithAddWithFor(i);
}
}
[Benchmark(Baseline = true)]
public void AddWithForOptimized()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithAddWithForOptimized(i);
}
}
[Benchmark]
public void XDivYMulX()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithXDivYMulX(i);
}
}
[Benchmark]
public void XDivYMulXWithLocalVar()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithXDivYMulXWithLocalVar(i);
}
}
[Benchmark]
public void SubtractWithWhile()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithSubtractWithWhile(i);
}
}
[Benchmark]
public void SubtractWithWhileWithSse2()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithSubractWithWhileWithSse2(i);
}
}
[Benchmark(Baseline = false)]
public void MixedApproachWithSubtractWithWhile()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithMixedApproachWithSubractWithWhile(i);
}
}
[Benchmark(Baseline = false)]
public void MixedApproachWithSubtractWithWhileAsVector()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithMixedApproachWithSubtractWithWhileAsVector(i);
}
}
[Benchmark(Baseline = false)]
public void MixedApproachWithSubtractWithWhileWithSse2()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithMixedApproachWithSubtractWithWhileWithSse2(i);
}
}
[Benchmark(Baseline = false)]
public void MixedApproachWithSubtractWithStackalloc()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithMixedApproachWithSubtractWithStackalloc(i);
}
}
[Benchmark(Baseline = false)]
public void MixedApproachWithForLoopOptimized()
{
long testNumber = TestNumber;
for (long i = testNumber; i >= _lastCaseIndex; i--)
{
CalculateWithMixedApproachWithForLoopOptimized(i);
}
}
}
}
@@ -0,0 +1,25 @@
namespace Recyclable.Collections.Benchmarks.POC
{
public class PocBenchmarkBase
{
// 0 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 524288
// 1048576 2097152 4194304 8388608 16777216 33554432 67108864 134217728 268435456 536870912
// 1073741824 2147483648
//[Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, 50, 60, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 1_000_000)]
//[Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 32, 40, 50, 60, 80, 90, 100, 128, 256, 512, 1024, 2048, 4096, 8192, 1_000_000)]
public long TestObjectCount = 1_000_000;
//[Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200)]
//[Params(10, 20, 50, 100)]
//[Params(1, 10)]
public int Divider = 10;
//public int BlockSize = 1_048_576;//int.MaxValue - 1;
public int BlockSize => TestObjectCount switch
{
0 => 1,
> 0 and <= 10 => (int)TestObjectCount,
_ => TestObjectCount / Divider > 0 ? checked((int)(TestObjectCount / Divider)) : checked((int)TestObjectCount)
};
}
}
@@ -0,0 +1,908 @@
using BenchmarkDotNet.Attributes;
using System.Buffers;
using System.Collections;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace Recyclable.Collections.Benchmarks.POC
{
public partial class RecyclableListPocBenchmarks
{
#region Old class
private class OldRecyclableList<T> : IDisposable, IList<T>
{
private const int ItemNotFoundIndex = -1;
private static readonly ArrayPool<T[]> _defaultMemoryBlocksPool = ArrayPool<T[]>.Create();
private static readonly ArrayPool<T> _defaultBlockArrayPool = ArrayPool<T>.Create();
private static readonly T[][] _emptyMemoryBlocksArray = new T[0][];
private static readonly T[] _emptyBlockArray = new T[0];
private int _blockSize;
private int _blockSizePow2Shift;
private int _nextItemBlockIndex;
private int _nextItemIndex;
protected static readonly bool NeedsClearing = !typeof(T).IsValueType;
protected ArrayPool<T[]> _memoryBlocksPool;
protected ArrayPool<T> _blockArrayPool;
protected T[][] _memoryBlocks;
private long _capacity;
public long Capacity
{
get => _capacity;
protected set => _capacity = value;
}
public int Count => checked((int)_longCount);
public bool IsReadOnly { get; }
public int LastTakenBlockIndex => _nextItemBlockIndex - (_nextItemIndex > 0 ? 0 : 1);
protected long _longCount;
public long LongCount
{
get => _longCount;
set => _longCount = value;
}
public int ReservedBlockCount => (int)(_capacity >> _blockSizePow2Shift) + ((_capacity & (_blockSize - 1)) > 0 ? 1 : 0);
public int BlockSize => _blockSize;
public int NextItemBlockIndex => _nextItemBlockIndex;
public int NextItemIndex => _nextItemIndex;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private void DoRemoveAt(long index)
{
int blockSize = _blockSize;
int nextItemIndex = _nextItemIndex;
T[][] memoryBlocks = _memoryBlocks;
_longCount--;
if (index < _longCount)
{
int sourceBlockIndex = (int)((index + 1) >> _blockSizePow2Shift);
int sourceItemIndex = (int)((index + 1) & (blockSize - 1));
int targetBlockIndex = (int)(index >> _blockSizePow2Shift);
int targetItemIndex = (int)(index & (blockSize - 1));
int lastTakenBlockIndex = LastTakenBlockIndex;
while (sourceBlockIndex < lastTakenBlockIndex || (sourceBlockIndex == lastTakenBlockIndex && (sourceItemIndex < nextItemIndex || sourceBlockIndex != _nextItemBlockIndex)))
{
int toCopy = sourceBlockIndex < lastTakenBlockIndex || nextItemIndex == 0
? Math.Min(blockSize - sourceItemIndex, blockSize - targetItemIndex)
: Math.Min(nextItemIndex, blockSize - targetItemIndex);
Array.Copy(memoryBlocks[sourceBlockIndex], sourceItemIndex, memoryBlocks[targetBlockIndex], targetItemIndex, toCopy);
// We didn't have enough room in the target array block. There are still items in the source array block to copy.
if (sourceItemIndex + toCopy < blockSize)
{
sourceItemIndex += toCopy;
targetBlockIndex++;
targetItemIndex = 0;
}
// We copied all the source items in the current array block. But have we filled the target?
else
{
sourceItemIndex = 0;
sourceBlockIndex++;
if (targetItemIndex + toCopy < blockSize)
{
targetItemIndex += toCopy;
}
else
{
targetBlockIndex++;
targetItemIndex = 0;
}
}
}
}
if (nextItemIndex > 0)
{
_nextItemIndex--;
}
else
{
_nextItemIndex = blockSize - 1;
_nextItemBlockIndex--;
}
if (NeedsClearing)
{
#pragma warning disable CS8601 // In real use cases we'll never access it
new Span<T>(memoryBlocks[_nextItemBlockIndex], _nextItemIndex, 1)[0] = default;
#pragma warning restore CS8601
}
}
private static void ThrowArgumentOutOfRangeException(in string message) => throw new ArgumentOutOfRangeException("index", message);
protected void AddRangeEnumerated(IEnumerable<T> source, int growByCount)
{
long i, copied = _longCount;
int blockSize = _blockSize, targetItemIdx = _nextItemIndex, targetBlockIdx = _nextItemBlockIndex;
using IEnumerator<T> enumerator = source.GetEnumerator();
if (!enumerator.MoveNext())
{
return;
}
long capacity = _capacity;
long available = capacity - copied;
Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, ReservedBlockCount);
Span<T> blockArraySpan;
var memoryBlocksCount = memoryBlocksSpan.Length;
while (true)
{
if (copied + growByCount > capacity)
{
capacity = EnsureCapacity(capacity + growByCount);
memoryBlocksCount = _memoryBlocks.Length;
if (blockSize != _blockSize)
{
blockSize = _blockSize;
}
memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlocksCount);
available = capacity - copied;
}
blockArraySpan = new(memoryBlocksSpan[targetBlockIdx], 0, blockSize);
for (i = 1; i <= available; i++)
{
blockArraySpan[targetItemIdx++] = enumerator.Current;
if (!enumerator.MoveNext())
{
if (targetItemIdx < blockSize)
{
_nextItemBlockIndex = targetBlockIdx;
_nextItemIndex = targetItemIdx;
}
else
{
_nextItemBlockIndex = targetBlockIdx + 1;
_nextItemIndex = 0;
}
_longCount += copied + i - _longCount;
return;
}
if (targetItemIdx == blockSize)
{
targetBlockIdx++;
targetItemIdx = 0;
if (targetBlockIdx == memoryBlocksCount)
{
break;
}
blockArraySpan = new(memoryBlocksSpan[targetBlockIdx], 0, blockSize);
}
}
copied += available;
}
}
protected void AddRangeWithKnownCount(IEnumerable<T> source, int requiredAdditionalCapacity)
{
long copied = _longCount;
int blockSize = _blockSize;
int targetItemIdx = _nextItemIndex;
int targetBlockIdx = _nextItemBlockIndex;
Span<T[]> memoryBlocksSpan;
Span<T> blockArraySpan;
long requiredCapacity = copied + requiredAdditionalCapacity;
if (_capacity < requiredCapacity)
{
_ = EnsureCapacity(requiredCapacity);
blockSize = _blockSize;
}
var memoryBlocksCount = ReservedBlockCount;
memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlocksCount);
blockArraySpan = new Span<T>(memoryBlocksSpan[targetBlockIdx], 0, blockSize);
foreach (var item in source)
{
blockArraySpan[targetItemIdx++] = item;
if (targetItemIdx == blockSize)
{
targetBlockIdx++;
targetItemIdx = 0;
if (targetBlockIdx == memoryBlocksCount)
{
break;
}
blockArraySpan = new Span<T>(memoryBlocksSpan[targetBlockIdx], 0, blockSize);
}
}
_longCount = requiredCapacity;
_nextItemBlockIndex = targetBlockIdx;
_nextItemIndex = targetItemIdx;
return;
}
protected static long DoIndexOf(T item, in T[][] memoryBlocks, int lastBlockIndex, int blockSize, int nextItemIndex)
{
Span<T[]> memoryBlocksSpan = new(memoryBlocks, 0, lastBlockIndex + 1);
int itemIndex, blockIndex;
for (blockIndex = 0; blockIndex < lastBlockIndex; blockIndex++)
{
itemIndex = Array.IndexOf(memoryBlocksSpan[blockIndex], item, 0, blockSize);
if (itemIndex >= 0)
{
return itemIndex + (blockIndex << (31 - BitOperations.LeadingZeroCount((uint)blockSize)));
}
}
if (blockIndex == lastBlockIndex)
{
itemIndex = nextItemIndex != 0
? Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, nextItemIndex)
: Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, blockSize);
}
else
{
return ItemNotFoundIndex;
}
return itemIndex >= 0 ? itemIndex + (lastBlockIndex << (31 - BitOperations.LeadingZeroCount((uint)blockSize))) : ItemNotFoundIndex;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected static long Resize(ref T[][]? source, int minBlockSize, long oldCapacity, long newCapacity, ArrayPool<T[]> memoryBlocksPool, ArrayPool<T> blockArrayPool)
{
int minBlockSizePowerToShift = 31 - BitOperations.LeadingZeroCount((uint)minBlockSize);
int sourceBlockCount = (int)(oldCapacity >> minBlockSizePowerToShift);
int requiredBlockCount = (int)(newCapacity >> minBlockSizePowerToShift) + ((newCapacity & (minBlockSize - 1)) > 0 ? 1 : 0);
int blockIndex;
T[][]? newMemoryBlocks;
Span<T[]> memoryBlocksSpan;
// Release excessive blocks if we're downsizing
if (requiredBlockCount < sourceBlockCount && (minBlockSize >= RecyclableDefaults.MinPooledArrayLength))
{
memoryBlocksSpan = new(source, requiredBlockCount, sourceBlockCount - requiredBlockCount);
blockIndex = 0;
while (blockIndex < memoryBlocksSpan.Length)
{
blockArrayPool.Return(memoryBlocksSpan[blockIndex++]!, NeedsClearing);
}
}
// Allocate new memory block for all arrays
newMemoryBlocks = requiredBlockCount < RecyclableDefaults.MinPooledArrayLength
? (new T[requiredBlockCount][])
: memoryBlocksPool.Rent(requiredBlockCount);
// Copy arrays from the old memory block for all arrays
if (sourceBlockCount > 0)
{
Array.Copy(source!, newMemoryBlocks, sourceBlockCount);
// We can now return the old memory block for all arrays itself
if (sourceBlockCount >= RecyclableDefaults.MinPooledArrayLength)
{
memoryBlocksPool.Return(source!, true);
}
}
source = newMemoryBlocks;
// Allocate arrays for any new blocks
if (requiredBlockCount > sourceBlockCount)
{
memoryBlocksSpan = new(newMemoryBlocks, sourceBlockCount, newMemoryBlocks.Length - sourceBlockCount);
if (minBlockSize >= RecyclableDefaults.MinPooledArrayLength)
{
if (sourceBlockCount == 0)
{
memoryBlocksSpan[0] = blockArrayPool.Rent(minBlockSize);
minBlockSize = memoryBlocksSpan[0].Length;
blockIndex = 1;
}
else
{
blockIndex = 0;
}
while (blockIndex < memoryBlocksSpan.Length)
{
memoryBlocksSpan[blockIndex++] = blockArrayPool.Rent(minBlockSize);
}
}
else
{
blockIndex = 0;
while (blockIndex < memoryBlocksSpan.Length)
{
memoryBlocksSpan[blockIndex++] = new T[minBlockSize];
}
}
}
return newMemoryBlocks.Length << minBlockSizePowerToShift;
}
internal long EnsureCapacity(long requestedCapacity)
{
long newCapacity = _capacity > 0
? checked((long)BitOperations.RoundUpToPowerOf2((ulong)requestedCapacity))
: requestedCapacity;
newCapacity = Resize(ref _memoryBlocks!, _blockSize, _capacity, newCapacity, _memoryBlocksPool, _blockArrayPool);
if (newCapacity > 0 && _capacity == 0)
{
_blockSize = _memoryBlocks[0].Length;
_blockSizePow2Shift = MathUtils.GetPow2Shift(_blockSize);
}
_capacity = newCapacity;
return newCapacity;
}
public OldRecyclableList(int minBlockSize = RecyclableDefaults.BlockSize, long? expectedItemsCount = default, ArrayPool<T[]>? memoryBlocksPool = default, ArrayPool<T>? blockArrayPool = default)
{
memoryBlocksPool ??= _defaultMemoryBlocksPool;
blockArrayPool ??= _defaultBlockArrayPool;
_memoryBlocksPool = memoryBlocksPool;
_blockArrayPool = blockArrayPool;
if (expectedItemsCount > 0)
{
_capacity = Resize(ref _memoryBlocks!, (int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize), 0, expectedItemsCount.Value, memoryBlocksPool, blockArrayPool);
if (_memoryBlocks.Length > 0)
{
minBlockSize = _memoryBlocks[0].Length;
}
_blockSize = minBlockSize;
_blockSizePow2Shift = MathUtils.GetPow2Shift(minBlockSize);
}
else
{
_blockSize = (int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize);
_blockSizePow2Shift = MathUtils.GetPow2Shift(minBlockSize);
_memoryBlocks = _emptyMemoryBlocksArray;
}
}
public OldRecyclableList(IEnumerable<T> source, int minBlockSize = RecyclableDefaults.BlockSize, long? expectedItemsCount = default, ArrayPool<T[]>? memoryBlocksPool = default, ArrayPool<T>? blockArrayPool = default)
{
memoryBlocksPool ??= _defaultMemoryBlocksPool;
blockArrayPool ??= _defaultBlockArrayPool;
_memoryBlocksPool = memoryBlocksPool;
_blockArrayPool = blockArrayPool;
if (expectedItemsCount > 0)
{
_capacity = Resize(ref _memoryBlocks!, (int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize), 0, expectedItemsCount.Value, memoryBlocksPool, blockArrayPool);
if (_memoryBlocks.Length > 0)
{
minBlockSize = _memoryBlocks[0].Length;
}
_blockSize = minBlockSize;
_blockSizePow2Shift = MathUtils.GetPow2Shift(minBlockSize);
}
else
{
_blockSize = (int)BitOperations.RoundUpToPowerOf2((uint)minBlockSize);
_blockSizePow2Shift = MathUtils.GetPow2Shift(minBlockSize);
_memoryBlocks = _emptyMemoryBlocksArray;
}
AddRange(source);
}
public T this[long index]
{
get => _memoryBlocks[index >> _blockSizePow2Shift][index & (_blockSize - 1)];
set => new Span<T>(_memoryBlocks[(int)(index >> _blockSizePow2Shift)])[(int)(index & (_blockSize - 1))] = value;
}
public T this[int index]
{
get => _memoryBlocks[index >> _blockSizePow2Shift][index & (_blockSize - 1)];
set => new Span<T>(_memoryBlocks[index >> _blockSizePow2Shift])[index & (_blockSize - 1)] = value;
}
public T[][] MemoryBlocks { get => _memoryBlocks; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(T item)
{
if (_capacity < _longCount + 1)
{
_ = EnsureCapacity(_longCount + 1);
}
_memoryBlocks[_nextItemBlockIndex][_nextItemIndex++] = item;
if (_nextItemIndex == _blockSize)
{
_nextItemBlockIndex++;
_nextItemIndex = 0;
}
_longCount++;
}
public void AddRange(in T[] items)
{
if (items.LongLength == 0)
{
return;
}
long targetCapacity = _longCount + items.LongLength;
if (_capacity < targetCapacity)
{
_ = EnsureCapacity(targetCapacity);
}
int blockSize = _blockSize,
targetBlockIndex = _nextItemBlockIndex,
memoryBlockCount = _memoryBlocks.Length;
Span<T> itemsSpan = new(items);
Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlockCount);
Span<T> targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], _nextItemIndex, blockSize - _nextItemIndex);
while (targetBlockArraySpan.Length <= itemsSpan.Length)
{
itemsSpan[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan);
itemsSpan = itemsSpan[targetBlockArraySpan.Length..];
targetBlockIndex++;
if (targetBlockIndex == memoryBlockCount)
{
break;
}
targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], 0, blockSize);
}
if (itemsSpan.Length > 0)
{
itemsSpan.CopyTo(targetBlockArraySpan);
}
_nextItemIndex = itemsSpan.Length;
_longCount = targetCapacity;
_nextItemBlockIndex = targetBlockIndex;
}
public void AddRange(RecyclableArrayList<T> items)
{
if (items.Count == 0)
{
return;
}
long targetCapacity = _longCount + items.Count;
if (_capacity < targetCapacity)
{
_ = EnsureCapacity(targetCapacity);
}
int blockSize = _blockSize,
targetBlockIndex = _nextItemBlockIndex,
memoryBlockCount = _memoryBlocks.Length;
Span<T> itemsSpan = new(items.AsArray(), 0, items.Count);
Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlockCount);
Span<T> targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], _nextItemIndex, blockSize - _nextItemIndex);
while (targetBlockArraySpan.Length <= itemsSpan.Length)
{
itemsSpan[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan);
itemsSpan = itemsSpan[targetBlockArraySpan.Length..];
targetBlockIndex++;
if (targetBlockIndex == memoryBlockCount)
{
break;
}
targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], 0, blockSize);
}
_nextItemIndex = itemsSpan.Length;
itemsSpan.CopyTo(targetBlockArraySpan);
_longCount = targetCapacity;
_nextItemBlockIndex = targetBlockIndex;
}
public void AddRange(OldRecyclableList<T> items)
{
if (items.LongCount == 0)
{
return;
}
long itemsCount = items.LongCount,
targetCapacity = _longCount + itemsCount;
if (_capacity < targetCapacity)
{
_ = EnsureCapacity(targetCapacity);
}
int blockSize = _blockSize,
sourceBlockIndex = 0,
targetBlockIndex = _nextItemBlockIndex,
toCopy;
Span<T[]> sourceMemoryBlocksSpan = new(items._memoryBlocks, 0, items._memoryBlocks.Length);
Span<T> itemsSpan = new(sourceMemoryBlocksSpan[sourceBlockIndex], 0, items._blockSize);
Span<T[]> targetMemoryBlocksSpan = new(_memoryBlocks, 0, _memoryBlocks.Length);
Span<T> targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex], _nextItemIndex, blockSize - _nextItemIndex);
long copiedCount = 0;
while (copiedCount < itemsCount)
{
toCopy = (int)Math.Min(itemsCount - copiedCount, itemsSpan.Length);
if (targetBlockArraySpan.Length < toCopy)
{
toCopy = targetBlockArraySpan.Length;
itemsSpan[..toCopy].CopyTo(targetBlockArraySpan);
targetBlockIndex++;
targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex], 0, blockSize);
itemsSpan = itemsSpan[toCopy..];
copiedCount += toCopy;
}
else
{
itemsSpan[..toCopy].CopyTo(targetBlockArraySpan);
targetBlockArraySpan = targetBlockArraySpan[toCopy..];
itemsSpan = itemsSpan[toCopy..];
if (itemsSpan.IsEmpty && sourceBlockIndex + 1 < sourceMemoryBlocksSpan.Length)
{
sourceBlockIndex++;
itemsSpan = new(sourceMemoryBlocksSpan[sourceBlockIndex], 0, items._blockSize);
}
copiedCount += toCopy;
if (targetBlockArraySpan.IsEmpty && copiedCount < itemsCount)
{
targetBlockIndex++;
targetBlockArraySpan = new(targetMemoryBlocksSpan[targetBlockIndex], 0, blockSize);
}
}
}
if ((targetCapacity & (blockSize - 1)) == 0)
{
_nextItemIndex = 0;
_nextItemBlockIndex = targetBlockIndex + 1;
}
else
{
_nextItemIndex = (int)(targetCapacity & (blockSize - 1));
_nextItemBlockIndex = targetBlockIndex;
}
_longCount = targetCapacity;
}
public void AddRange(List<T> items)
{
if (items.Count == 0)
{
return;
}
long targetCapacity = _longCount + items.Count;
if (_capacity < targetCapacity)
{
_ = EnsureCapacity(targetCapacity);
}
int blockSize = _blockSize,
targetBlockIndex = _nextItemBlockIndex,
itemsCount = items.Count,
copied = Math.Min(blockSize - _nextItemIndex, itemsCount);
Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, _memoryBlocks.Length);
items.CopyTo(0, memoryBlocksSpan[targetBlockIndex], _nextItemIndex, copied);
if (_nextItemIndex + copied == blockSize)
{
targetBlockIndex++;
_nextItemIndex = 0;
}
else
{
_nextItemIndex += copied;
}
while (blockSize <= itemsCount - copied)
{
items.CopyTo(copied, memoryBlocksSpan[targetBlockIndex++], 0, blockSize);
copied += blockSize;
}
if ((itemsCount - copied < blockSize) && (itemsCount - copied != 0))
{
_nextItemIndex = itemsCount - copied;
items.CopyTo(copied, memoryBlocksSpan[targetBlockIndex], 0, _nextItemIndex);
}
_nextItemBlockIndex = targetBlockIndex;
_longCount = targetCapacity;
}
public void AddRange(IList<T> items)
{
if (items.Count == 0)
{
return;
}
long targetCapacity = _longCount + items.Count;
if (_capacity < targetCapacity)
{
_ = EnsureCapacity(targetCapacity);
}
int blockSize = _blockSize,
targetBlockIndex = _nextItemBlockIndex,
memoryBlockCount = _memoryBlocks.Length;
T[] itemsBuffer = items.Count >= RecyclableDefaults.MinPooledArrayLength ? _blockArrayPool.Rent(items.Count) : new T[items.Count];
try
{
items.CopyTo(itemsBuffer, 0);
Span<T> itemsSpan = new(itemsBuffer, 0, items.Count);
Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, memoryBlockCount);
Span<T> targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], _nextItemIndex, blockSize - _nextItemIndex);
while (targetBlockArraySpan.Length <= itemsSpan.Length)
{
itemsSpan[..targetBlockArraySpan.Length].CopyTo(targetBlockArraySpan);
itemsSpan = itemsSpan[targetBlockArraySpan.Length..];
targetBlockIndex++;
if (targetBlockIndex == memoryBlockCount)
{
break;
}
targetBlockArraySpan = new(memoryBlocksSpan[targetBlockIndex], 0, blockSize);
}
itemsSpan.CopyTo(targetBlockArraySpan);
_longCount = targetCapacity;
_nextItemBlockIndex = targetBlockIndex;
_nextItemIndex = itemsSpan.Length;
}
finally
{
if (items.Count >= RecyclableDefaults.MinPooledArrayLength)
{
_blockArrayPool.Return(itemsBuffer, NeedsClearing);
}
}
}
public void AddRange(IEnumerable<T> source, int growByCount = RecyclableDefaults.MinPooledArrayLength)
{
if (source is OldRecyclableList<T> sourceRecyclableList)
{
AddRange(sourceRecyclableList);
return;
}
if (source is RecyclableArrayList<T> sourceRecyclableArrayList)
{
AddRange(sourceRecyclableArrayList);
return;
}
if (source is T[] sourceArray)
{
AddRange(sourceArray);
return;
}
if (source is List<T> sourceList)
{
AddRange(sourceList);
return;
}
if (source is IList<T> sourceIList)
{
AddRange(sourceIList);
return;
}
if (source.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity))
{
AddRangeWithKnownCount(source, requiredAdditionalCapacity);
return;
}
AddRangeEnumerated(source, growByCount);
}
public void Clear()
{
if (_blockSize >= RecyclableDefaults.MinPooledArrayLength)
{
Span<T[]> memoryBlocksSpan = new(_memoryBlocks, 0, ReservedBlockCount);
int memoryBlocksCount = memoryBlocksSpan.Length;
ArrayPool<T> blockArrayPool = _blockArrayPool;
for (int toRemoveIdx = 0; toRemoveIdx < memoryBlocksCount; toRemoveIdx++)
{
blockArrayPool.Return(memoryBlocksSpan[toRemoveIdx], NeedsClearing);
}
}
_capacity = 0;
_nextItemBlockIndex = 0;
_nextItemIndex = 0;
_longCount = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(T item)
{
if (_longCount == 0)
{
return false;
}
if (_nextItemBlockIndex == 0 || (_nextItemBlockIndex == 1 && _nextItemIndex == 0))
{
return Array.IndexOf(_memoryBlocks[0], item, 0, (int)_longCount) >= 0;
}
Span<T[]> memoryBlocksSpan = new(_memoryBlocks);
int lastBlockIndex = _nextItemBlockIndex;
for (int blockIndex = 0; blockIndex < lastBlockIndex; blockIndex++)
{
if (Array.IndexOf(memoryBlocksSpan[blockIndex], item, 0, _blockSize) >= 0)
{
return true;
}
}
return lastBlockIndex < memoryBlocksSpan.Length && Array.IndexOf(memoryBlocksSpan[lastBlockIndex], item, 0, _nextItemIndex) >= 0;
}
public void CopyTo(T[] array, int arrayIndex) => _memoryBlocks.CopyTo(0, _blockSize, _longCount, array, arrayIndex);
public IEnumerator<T> GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int IndexOf(T item) => _longCount == 0
? ItemNotFoundIndex
: _nextItemBlockIndex == 0 || (_nextItemBlockIndex == 1 && _nextItemIndex == 0)
? Array.IndexOf(_memoryBlocks[0], item, 0, (int)_longCount)
: (int)DoIndexOf(item, _memoryBlocks, LastTakenBlockIndex, _blockSize, _nextItemIndex);
public void Insert(int index, T item) => throw new NotSupportedException();
public long LongIndexOf(T item) => _longCount == 0
? ItemNotFoundIndex
: _nextItemBlockIndex == 0 || (_nextItemBlockIndex == 1 && _nextItemIndex == 0)
? Array.IndexOf(_memoryBlocks[0], item, 0, (int)_longCount)
: DoIndexOf(item, _memoryBlocks, LastTakenBlockIndex, _blockSize, _nextItemIndex);
public bool Remove(T item)
{
var itemIndex = LongIndexOf(item);
if (itemIndex >= 0)
{
DoRemoveAt(itemIndex);
return true;
}
return false;
}
public void RemoveBlock(int index)
{
if (_blockSize < RecyclableDefaults.MinPooledArrayLength)
{
_memoryBlocks[index] = _emptyBlockArray;
}
else
{
_blockArrayPool.Return(_memoryBlocks[index], NeedsClearing);
}
_capacity -= _blockSize;
if (_nextItemBlockIndex == index)
{
_nextItemIndex = 0;
}
else if (_nextItemBlockIndex > index)
{
_nextItemBlockIndex--;
}
}
public void RemoveAt(int index)
{
if (index > _longCount - 1 || index < 0)
{
ThrowArgumentOutOfRangeException($"Argument \"{nameof(index)}\" = {index} is out of range. Expected value between 0 and {_longCount - 1}");
}
DoRemoveAt(index);
}
public void RemoveAt(long index)
{
if (index > _longCount - 1 || index < 0)
{
ThrowArgumentOutOfRangeException($"Argument \"{nameof(index)}\" = {index} is out of range. Expected value between 0 and {_longCount - 1}");
}
DoRemoveAt(index);
}
IEnumerator IEnumerable.GetEnumerator() => _memoryBlocks.Enumerate(_blockSize, LongCount).GetEnumerator();
~OldRecyclableList()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose();
}
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
if (_capacity > 0)
{
Clear();
if (_memoryBlocks.Length >= RecyclableDefaults.MinPooledArrayLength)
{
_memoryBlocksPool.Return(_memoryBlocks, NeedsClearing);
}
GC.SuppressFinalize(this);
}
}
}
#endregion
[Benchmark(Baseline = true)]
public void RecyclableList_EnsureCapacity_Old_ByPowOf2()
{
using var list = new OldRecyclableList<object>(minBlockSize: BlockSize, expectedItemsCount: TestObjectCount);
_ = list.EnsureCapacity(TestObjectCount);
}
[Benchmark(Baseline = false)]
public void RecyclableList_EnsureCapacity_ByPowOf2()
{
using var list = new RecyclableList<object>(minBlockSize: BlockSize, expectedItemsCount: TestObjectCount);
_ = RecyclableList<object>.EnsureCapacity(list, TestObjectCount);
}
[Benchmark(Baseline = false)]
public void RecyclableList_EnsureCapacity_Old_ByBlockSize()
{
using var list = new OldRecyclableList<object>();
_ = list.EnsureCapacity(TestObjectCount);
}
[Benchmark(Baseline = false)]
public void RecyclableList_EnsureCapacity_ByBlockSize()
{
using var list = new RecyclableList<object>();
_ = RecyclableList<object>.EnsureCapacity(list, TestObjectCount);
}
}
}
@@ -0,0 +1,14 @@
namespace Recyclable.Collections.Benchmarks.POC
{
public partial class RecyclableListPocBenchmarks : PocBenchmarkBase
{
public static void Run()
{
var benchmark = new RecyclableListPocBenchmarks();
benchmark.RecyclableList_EnsureCapacity_Old_ByPowOf2();
benchmark.RecyclableList_EnsureCapacity_ByPowOf2();
benchmark.RecyclableList_EnsureCapacity_Old_ByBlockSize();
benchmark.RecyclableList_EnsureCapacity_ByBlockSize();
}
}
}
@@ -0,0 +1,362 @@
using BenchmarkDotNet.Attributes;
namespace Recyclable.Collections.Benchmarks.POC
{
public class RefVsInstanceMemberPocBenchmarks
{
public static void Run()
{
var benchmark = new RefVsInstanceMemberPocBenchmarks();
benchmark.ArrayInstanceMember2();
benchmark.ArrayInstanceMember3();
benchmark.ArrayInstanceMember4();
benchmark.ArrayInstanceMember5();
benchmark.ArrayInstanceMember6();
benchmark.ArrayInstanceMemberLocalVar5();
benchmark.ArrayInstanceMemberLocalVar6();
benchmark.IntInstanceMember2();
benchmark.IntInstanceMember3();
benchmark.IntInstanceMember4();
benchmark.IntInstanceMember5();
benchmark.RefToArray2();
benchmark.RefToArray3();
benchmark.RefToArray4();
benchmark.RefToArray5();
benchmark.RefToArray6();
benchmark.RefToInt2();
benchmark.RefToInt3();
}
private readonly TestCase _testCase = new();
private static void DoNothing(int count)
{
_ = count;
}
private class TestCase
{
private int[] _items = new int[123];
private int _count = 123;
private static int Add(int count)
{
return count;
}
public int Count
{
get => _count;
set => _count = value;
}
public ref int GetCountAsRefCount2()
{
ref int count = ref _count;
count = 234;
return ref count;
}
public ref int GetCountAsRefCount3()
{
ref int count = ref _count;
count = 234;
_ = TestCase.Add(count);
return ref count;
}
public ref int GetCountAsRefCount4()
{
ref int count = ref _count;
count = 234;
_ = TestCase.Add(count);
_ = TestCase.Add(count);
return ref count;
}
public ref int GetCountAsRefCount5()
{
ref int count = ref _count;
count = 234;
_ = TestCase.Add(count);
_ = TestCase.Add(count);
_ = TestCase.Add(count);
return ref count;
}
public int GetCountAsInstanceMember2()
{
_count = 234;
return _count;
}
public int GetCountAsInstanceMember3()
{
_count = 234;
_ = TestCase.Add(_count);
return _count;
}
public int GetCountAsInstanceMember4()
{
_count = 234;
_ = TestCase.Add(_count);
_ = TestCase.Add(_count);
return _count;
}
public int GetCountAsInstanceMember5()
{
_count = 234;
_ = TestCase.Add(_count);
_ = TestCase.Add(_count);
_ = TestCase.Add(_count);
return _count;
}
public int GetLengthAsArray2()
{
_ = TestCase.Add(_items.Length);
return _items.Length;
}
public int GetLengthAsArray3()
{
_ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length);
return _items.Length;
}
public int GetLengthAsArray4()
{
_ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length);
return _items.Length;
}
public int GetLengthAsArray5()
{
_ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length);
return _items.Length;
}
public int GetLengthAsArray6()
{
_ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length);
_ = TestCase.Add(_items.Length);
return _items.Length;
}
public int GetLengthAsArrayLocalVar5()
{
var items = _items;
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
return items.Length;
}
public int GetLengthAsArrayLocalVar6()
{
var items = _items;
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
return items.Length;
}
public int GetLengthAsRefArray2()
{
ref int[] items = ref _items;
_ = TestCase.Add(items.Length);
return items.Length;
}
public int GetLengthAsRefArray3()
{
ref int[] items = ref _items;
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
return items.Length;
}
public int GetLengthAsRefArray4()
{
ref int[] items = ref _items;
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
return items.Length;
}
public int GetLengthAsRefArray5()
{
ref int[] items = ref _items;
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
return items.Length;
}
public int GetLengthAsRefArray6()
{
ref int[] items = ref _items;
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
_ = TestCase.Add(items.Length);
return items.Length;
}
}
[Benchmark]
public void RefToInt2()
{
var count = _testCase.GetCountAsRefCount2();
DoNothing(count);
}
[Benchmark]
public void RefToInt3()
{
var count = _testCase.GetCountAsRefCount3();
DoNothing(count);
}
[Benchmark]
public void RefToInt4()
{
var count = _testCase.GetCountAsRefCount4();
DoNothing(count);
}
[Benchmark]
public void RefToInt5()
{
var count = _testCase.GetCountAsRefCount5();
DoNothing(count);
}
[Benchmark(Baseline = true)]
public void IntInstanceMember2()
{
var count = _testCase.GetCountAsInstanceMember2();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void IntInstanceMember3()
{
var count = _testCase.GetCountAsInstanceMember3();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void IntInstanceMember4()
{
var count = _testCase.GetCountAsInstanceMember4();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void IntInstanceMember5()
{
var count = _testCase.GetCountAsInstanceMember5();
DoNothing(count);
}
[Benchmark]
public void RefToArray2()
{
var count = _testCase.GetLengthAsRefArray2();
DoNothing(count);
}
[Benchmark]
public void RefToArray3()
{
var count = _testCase.GetLengthAsRefArray3();
DoNothing(count);
}
[Benchmark]
public void RefToArray4()
{
var count = _testCase.GetLengthAsRefArray4();
DoNothing(count);
}
[Benchmark]
public void RefToArray5()
{
var count = _testCase.GetLengthAsRefArray5();
DoNothing(count);
}
[Benchmark]
public void RefToArray6()
{
var count = _testCase.GetLengthAsRefArray6();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void ArrayInstanceMember2()
{
var count = _testCase.GetLengthAsArray2();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void ArrayInstanceMember3()
{
var count = _testCase.GetLengthAsArray3();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void ArrayInstanceMember4()
{
var count = _testCase.GetLengthAsArray4();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void ArrayInstanceMember5()
{
var count = _testCase.GetLengthAsArray5();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void ArrayInstanceMember6()
{
var count = _testCase.GetLengthAsArray6();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void ArrayInstanceMemberLocalVar5()
{
var count = _testCase.GetLengthAsArrayLocalVar5();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void ArrayInstanceMemberLocalVar6()
{
var count = _testCase.GetLengthAsArrayLocalVar6();
DoNothing(count);
}
}
}
+31 -77
View File
@@ -1,100 +1,54 @@
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Running;
using Recyclable.Collections.Benchmarks;
//BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(new[] { "-f*" });
//BenchmarkRunner.Run<RefVsInstanceMemberBenchmarks>();
using Recyclable.Collections.Benchmarks.POC;
internal class Program
{
static void RunModuloBenchmarks()
static IConfig BenchmarkConfig { get; } = ManualConfig.Create(DefaultConfig.Instance).WithOptions(ConfigOptions.DisableOptimizationsValidator | ConfigOptions.JoinSummary);
static void RunAssemblyBenchmarks()
{
var benchmark = new ModuloBenchmarks();
//benchmark.AddWithFor();
//benchmark.DividerAndModulusOperator();
//benchmark.MathDivRem();
//benchmark.SubtracWithWhile();
//benchmark.XDivYMulX();
//benchmark.XDivYMulXWithLocalVar();
//benchmark.AddWithForOptimized();
//benchmark.MixedApproachWithForLoopOptimized();
//benchmark.MixedApproachWithSubtractWithWhile();
benchmark.MixedApproachWithSubtractWithWhileAsVector();
_ = BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(new[] { "-f*" });
}
static void RunBenchmark()
private static void RunRecyclableCollectionsBenchmarks()
{
var benchmark = new RecyclableCollectionsBenchmarks();
benchmark.Setup();
_ = BenchmarkRunner.Run<RecyclableCollectionsBenchmarks>(BenchmarkConfig);
}
static void RunSelectedBenchmarks()
{
// ****************
// *** Template ***
// ****************
//var benchmark = new RecyclableCollectionsBenchmarks();
//benchmark.Setup();
//foreach (var _ in Enumerable.Range(1, 1000))
//{
//benchmark.RecyclableList_Contains();
//benchmark.RecyclableArrayList_AddRange_WithCapacity();
//benchmark.RecyclableArrayList_Add_WithCapacity();
//benchmark.RecyclableList_Create();
//benchmark.RecyclableList_Create_WithCapacity();
//benchmark.RecyclableList_Add();
//benchmark.RecyclableList_Add_WithCapacity();
//benchmark.RecyclableList_Contains_LastItems();
//benchmark.RecyclableList_IndexOf_FirstItems();
//benchmark.RecyclableList_AddRangeWhenSourceIsList();
//benchmark.RecyclableList_Remove_FirstItems();
//benchmark.RecyclableList_RemoveAt_LastItems();
//benchmark.RecyclableList_EnsureCapacity_New_ByBlockSize();
benchmark.RecyclableList_EnsureCapacity_ByPowOf2();
//benchmark.List_RemoveAt_LastItems();
//}
benchmark.Cleanup();
//benchmark.Cleanup();
}
static void RunPocBenchmarks()
{
_ = BenchmarkRunner.Run<RecyclableListPocBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<DelegateVsComparerPocBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<ModuloPocBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<RefVsInstanceMemberPocBenchmarks>(BenchmarkConfig);
_ = BenchmarkRunner.Run<ArraySizeLimitPocBenchmarks>(BenchmarkConfig);
}
private static void Main(string[] args)
{
BenchmarkRunner.Run<RecyclableCollectionsBenchmarks>(
ManualConfig.Create(DefaultConfig.Instance).WithOptions(ConfigOptions.DisableOptimizationsValidator | ConfigOptions.JoinSummary)
);
//var pool = ArrayPool<object>.Shared;
//for (long bufferSize = int.MaxValue / 2; bufferSize <= int.MaxValue; bufferSize += 100_000_000)
//{
// try
// {
// Console.WriteLine($"Allocating {bufferSize}");
// var arr = pool.Rent((int)bufferSize);
// pool.Return(arr);
// GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
// }
// catch (Exception)
// {
// Console.WriteLine($"Error during array creation. {nameof(bufferSize)} = {bufferSize}");
// throw;
// }
//}
//RunBenchmark();
//BenchmarkRunner.Run<ModuloBenchmarks>(
// ManualConfig.Create(DefaultConfig.Instance).WithOptions(ConfigOptions.DisableOptimizationsValidator | ConfigOptions.JoinSummary)
//);
//RunModuloBenchmarks();
RunRecyclableCollectionsBenchmarks();
//RunPocBenchmarks();
//RunSelectedBenchmarks();
//RunAssemblyBenchmarks();
}
}
// benchmark.Setup();
// benchmark.Cleanup();
//benchmark.Setup();
//benchmark.Cleanup();
//}
//BenchmarkRunner.Run<DelegateVsComparerBenchmark>();
//var tests = new ListVsLinkedListBenchmark();
//tests.RecyclableList();
@@ -1,339 +0,0 @@
using BenchmarkDotNet.Attributes;
namespace Recyclable.Collections.Benchmarks
{
public class RefVsInstanceMemberBenchmarks
{
private readonly TestCase _testCase = new();
private void DoNothing(int count)
{
_ = count;
}
private class TestCase
{
private int[] _items = new int[123];
private int _count = 123;
private int Add(int count)
{
return count;
}
public int Count
{
get => _count;
set => _count = value;
}
public ref int GetCountAsRefCount2()
{
ref int count = ref _count;
count = 234;
return ref count;
}
public ref int GetCountAsRefCount3()
{
ref int count = ref _count;
count = 234;
_ = Add(count);
return ref count;
}
public ref int GetCountAsRefCount4()
{
ref int count = ref _count;
count = 234;
_ = Add(count);
_ = Add(count);
return ref count;
}
public ref int GetCountAsRefCount5()
{
ref int count = ref _count;
count = 234;
_ = Add(count);
_ = Add(count);
_ = Add(count);
return ref count;
}
public int GetCountAsInstanceMember2()
{
_count = 234;
return _count;
}
public int GetCountAsInstanceMember3()
{
_count = 234;
_ = Add(_count);
return _count;
}
public int GetCountAsInstanceMember4()
{
_count = 234;
_ = Add(_count);
_ = Add(_count);
return _count;
}
public int GetCountAsInstanceMember5()
{
_count = 234;
_ = Add(_count);
_ = Add(_count);
_ = Add(_count);
return _count;
}
public int GetLengthAsArray2()
{
_ = Add(_items.Length);
return _items.Length;
}
public int GetLengthAsArray3()
{
_ = Add(_items.Length);
_ = Add(_items.Length);
return _items.Length;
}
public int GetLengthAsArray4()
{
_ = Add(_items.Length);
_ = Add(_items.Length);
_ = Add(_items.Length);
return _items.Length;
}
public int GetLengthAsArray5()
{
_ = Add(_items.Length);
_ = Add(_items.Length);
_ = Add(_items.Length);
_ = Add(_items.Length);
return _items.Length;
}
public int GetLengthAsArray6()
{
_ = Add(_items.Length);
_ = Add(_items.Length);
_ = Add(_items.Length);
_ = Add(_items.Length);
_ = Add(_items.Length);
return _items.Length;
}
public int GetLengthAsArrayLocalVar5()
{
var items = _items;
_ = Add(items.Length);
_ = Add(items.Length);
_ = Add(items.Length);
_ = Add(items.Length);
return items.Length;
}
public int GetLengthAsArrayLocalVar6()
{
var items = _items;
_ = Add(items.Length);
_ = Add(items.Length);
_ = Add(items.Length);
_ = Add(items.Length);
_ = Add(items.Length);
return items.Length;
}
public int GetLengthAsRefArray2()
{
ref int[] items = ref _items;
_ = Add(items.Length);
return items.Length;
}
public int GetLengthAsRefArray3()
{
ref int[] items = ref _items;
_ = Add(items.Length);
_ = Add(items.Length);
return items.Length;
}
public int GetLengthAsRefArray4()
{
ref int[] items = ref _items;
_ = Add(items.Length);
_ = Add(items.Length);
_ = Add(items.Length);
return items.Length;
}
public int GetLengthAsRefArray5()
{
ref int[] items = ref _items;
_ = Add(items.Length);
_ = Add(items.Length);
_ = Add(items.Length);
_ = Add(items.Length);
return items.Length;
}
public int GetLengthAsRefArray6()
{
ref int[] items = ref _items;
_ = Add(items.Length);
_ = Add(items.Length);
_ = Add(items.Length);
_ = Add(items.Length);
_ = Add(items.Length);
return items.Length;
}
}
[Benchmark]
public void RefToInt2()
{
var count = _testCase.GetCountAsRefCount2();
DoNothing(count);
}
[Benchmark]
public void RefToInt3()
{
var count = _testCase.GetCountAsRefCount3();
DoNothing(count);
}
[Benchmark]
public void RefToInt4()
{
var count = _testCase.GetCountAsRefCount4();
DoNothing(count);
}
[Benchmark]
public void RefToInt5()
{
var count = _testCase.GetCountAsRefCount5();
DoNothing(count);
}
[Benchmark(Baseline = true)]
public void IntInstanceMember2()
{
var count = _testCase.GetCountAsInstanceMember2();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void IntInstanceMember3()
{
var count = _testCase.GetCountAsInstanceMember3();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void IntInstanceMember4()
{
var count = _testCase.GetCountAsInstanceMember4();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void IntInstanceMember5()
{
var count = _testCase.GetCountAsInstanceMember5();
DoNothing(count);
}
[Benchmark]
public void RefToArray2()
{
var count = _testCase.GetLengthAsRefArray2();
DoNothing(count);
}
[Benchmark]
public void RefToArray3()
{
var count = _testCase.GetLengthAsRefArray3();
DoNothing(count);
}
[Benchmark]
public void RefToArray4()
{
var count = _testCase.GetLengthAsRefArray4();
DoNothing(count);
}
[Benchmark]
public void RefToArray5()
{
var count = _testCase.GetLengthAsRefArray5();
DoNothing(count);
}
[Benchmark]
public void RefToArray6()
{
var count = _testCase.GetLengthAsRefArray6();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void ArrayInstanceMember2()
{
var count = _testCase.GetLengthAsArray2();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void ArrayInstanceMember3()
{
var count = _testCase.GetLengthAsArray3();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void ArrayInstanceMember4()
{
var count = _testCase.GetLengthAsArray4();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void ArrayInstanceMember5()
{
var count = _testCase.GetLengthAsArray5();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void ArrayInstanceMember6()
{
var count = _testCase.GetLengthAsArray6();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void ArrayInstanceMemberLocalVar5()
{
var count = _testCase.GetLengthAsArrayLocalVar5();
DoNothing(count);
}
[Benchmark(Baseline = false)]
public void ArrayInstanceMemberLocalVar6()
{
var count = _testCase.GetLengthAsArrayLocalVar6();
DoNothing(count);
}
}
}