Benchmarks mostly reorganized & cleaned up. Simplified for re-runs.
This commit is contained in:
1 parent
eae5cb4452
commit
2d7e62df72
12 files changed
+2045
-1040
No files matched your search
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user