RecyclableArrayList.AddRange(IEnumerable<T>) performance improvements. ...WhenSourceIsIEnumerable benchmarks reworked to use real IEnumerable, which can't be typecasted to anything.
This commit is contained in:
1 parent
bf2d966f28
commit
f500f846ac
7 files changed
+121
-33
No files matched your search
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
private RecyclableArrayList<object>? _testObjectsAsRecyclableArrayList;
|
private RecyclableArrayList<object>? _testObjectsAsRecyclableArrayList;
|
||||||
protected RecyclableArrayList<object> TestObjectsAsRecyclableArrayList => _testObjectsAsRecyclableArrayList ??= TestObjects.ToRecyclableArrayList();
|
protected RecyclableArrayList<object> TestObjectsAsRecyclableArrayList => _testObjectsAsRecyclableArrayList ??= TestObjects.ToRecyclableArrayList();
|
||||||
|
protected IEnumerable<object> TestObjectsAsIEnumerable = Enumerable.Range(1, TestObjectCount).Select(static x => new object());
|
||||||
|
|
||||||
protected static void DoNothing<T>(T obj)
|
protected static void DoNothing<T>(T obj)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
//[Benchmark]
|
||||||
public void Array_GetItem()
|
public void Array_GetItem()
|
||||||
{
|
{
|
||||||
var data = TestObjects;
|
var data = TestObjects;
|
||||||
@@ -36,14 +36,14 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
//[Benchmark]
|
||||||
public void Array_Count()
|
public void Array_Count()
|
||||||
{
|
{
|
||||||
var data = _testArray;
|
var data = _testArray;
|
||||||
DoNothing(data.Length);
|
DoNothing(data.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
//[Benchmark]
|
||||||
public void Array_LongCount()
|
public void Array_LongCount()
|
||||||
{
|
{
|
||||||
var data = _testArray;
|
var data = _testArray;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
DoNothing(list);
|
DoNothing(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
//[Benchmark]
|
||||||
public void List_Add()
|
public void List_Add()
|
||||||
{
|
{
|
||||||
var data = TestObjects;
|
var data = TestObjects;
|
||||||
@@ -39,7 +39,7 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark(Baseline = false)]
|
//[Benchmark(Baseline = false)]
|
||||||
public void List_Add_WithCapacity()
|
public void List_Add_WithCapacity()
|
||||||
{
|
{
|
||||||
var data = TestObjects;
|
var data = TestObjects;
|
||||||
@@ -51,7 +51,7 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
//[Benchmark]
|
||||||
public void List_AddRangeWhenSourceIsArray()
|
public void List_AddRangeWhenSourceIsArray()
|
||||||
{
|
{
|
||||||
var data = TestObjects;
|
var data = TestObjects;
|
||||||
@@ -59,7 +59,7 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
list.AddRange(data);
|
list.AddRange(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
//[Benchmark]
|
||||||
public void List_AddRangeWhenSourceIsList()
|
public void List_AddRangeWhenSourceIsList()
|
||||||
{
|
{
|
||||||
var data = TestObjectsAsList;
|
var data = TestObjectsAsList;
|
||||||
@@ -67,7 +67,7 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
list.AddRange(data);
|
list.AddRange(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
//[Benchmark]
|
||||||
public void List_AddRangeWhenSourceIsIList()
|
public void List_AddRangeWhenSourceIsIList()
|
||||||
{
|
{
|
||||||
var data = (IList<object>)TestObjectsAsList;
|
var data = (IList<object>)TestObjectsAsList;
|
||||||
@@ -78,12 +78,12 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
[Benchmark]
|
[Benchmark]
|
||||||
public void List_AddRangeWhenSourceIsIEnumerable()
|
public void List_AddRangeWhenSourceIsIEnumerable()
|
||||||
{
|
{
|
||||||
var data = (IEnumerable<object>)TestObjects;
|
var data = TestObjectsAsIEnumerable;
|
||||||
var list = new List<object>();
|
var list = new List<object>();
|
||||||
list.AddRange(data);
|
list.AddRange(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark(Baseline = false)]
|
//[Benchmark(Baseline = false)]
|
||||||
public void List_AddRange_WithCapacity()
|
public void List_AddRange_WithCapacity()
|
||||||
{
|
{
|
||||||
var data = TestObjects;
|
var data = TestObjects;
|
||||||
@@ -92,7 +92,7 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
list.AddRange(data);
|
list.AddRange(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
//[Benchmark]
|
||||||
public void List_SetItem()
|
public void List_SetItem()
|
||||||
{
|
{
|
||||||
var data = TestObjectsAsList;
|
var data = TestObjectsAsList;
|
||||||
@@ -103,7 +103,7 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
//[Benchmark]
|
||||||
public void List_GetItem()
|
public void List_GetItem()
|
||||||
{
|
{
|
||||||
var data = TestObjectsAsList;
|
var data = TestObjectsAsList;
|
||||||
@@ -114,7 +114,7 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
//[Benchmark]
|
||||||
public void List_Count()
|
public void List_Count()
|
||||||
{
|
{
|
||||||
var data = _testList;
|
var data = _testList;
|
||||||
|
|||||||
+11
-11
@@ -44,7 +44,7 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
DoNothing(list);
|
DoNothing(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
//[Benchmark]
|
||||||
public void RecyclableArrayList_Add()
|
public void RecyclableArrayList_Add()
|
||||||
{
|
{
|
||||||
var data = TestObjects;
|
var data = TestObjects;
|
||||||
@@ -56,7 +56,7 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
//[Benchmark]
|
||||||
public void RecyclableArrayList_Add_WithCapacity()
|
public void RecyclableArrayList_Add_WithCapacity()
|
||||||
{
|
{
|
||||||
var data = TestObjects;
|
var data = TestObjects;
|
||||||
@@ -68,7 +68,7 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
//[Benchmark]
|
||||||
public void RecyclableArrayList_AddRangeWhenSourceIsArray()
|
public void RecyclableArrayList_AddRangeWhenSourceIsArray()
|
||||||
{
|
{
|
||||||
var data = TestObjects;
|
var data = TestObjects;
|
||||||
@@ -76,7 +76,7 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
list.AddRange(data);
|
list.AddRange(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
//[Benchmark]
|
||||||
public void RecyclableArrayList_AddRangeWhenSourceIsList()
|
public void RecyclableArrayList_AddRangeWhenSourceIsList()
|
||||||
{
|
{
|
||||||
var data = TestObjectsAsList;
|
var data = TestObjectsAsList;
|
||||||
@@ -84,7 +84,7 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
list.AddRange(data);
|
list.AddRange(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
//[Benchmark]
|
||||||
public void RecyclableArrayList_AddRangeWhenSourceIsIList()
|
public void RecyclableArrayList_AddRangeWhenSourceIsIList()
|
||||||
{
|
{
|
||||||
var data = (IList<object>)TestObjectsAsList;
|
var data = (IList<object>)TestObjectsAsList;
|
||||||
@@ -95,12 +95,12 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
[Benchmark]
|
[Benchmark]
|
||||||
public void RecyclableArrayList_AddRangeWhenSourceIsIEnumerable()
|
public void RecyclableArrayList_AddRangeWhenSourceIsIEnumerable()
|
||||||
{
|
{
|
||||||
var data = (IEnumerable<object>)TestObjects;
|
var data = TestObjectsAsIEnumerable;
|
||||||
using var list = new RecyclableArrayList<object>();
|
using var list = new RecyclableArrayList<object>();
|
||||||
list.AddRange(data);
|
list.AddRange(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
//[Benchmark]
|
||||||
public void RecyclableArrayList_AddRangeWhenSourceIsRecyclableArrayList()
|
public void RecyclableArrayList_AddRangeWhenSourceIsRecyclableArrayList()
|
||||||
{
|
{
|
||||||
var data = TestObjectsAsRecyclableArrayList;
|
var data = TestObjectsAsRecyclableArrayList;
|
||||||
@@ -108,7 +108,7 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
list.AddRange(data);
|
list.AddRange(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
//[Benchmark]
|
||||||
public void RecyclableArrayList_AddRange_WithCapacity()
|
public void RecyclableArrayList_AddRange_WithCapacity()
|
||||||
{
|
{
|
||||||
var data = TestObjects;
|
var data = TestObjects;
|
||||||
@@ -117,7 +117,7 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
list.AddRange(data);
|
list.AddRange(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
//[Benchmark]
|
||||||
public void RecyclableArrayList_SetItem()
|
public void RecyclableArrayList_SetItem()
|
||||||
{
|
{
|
||||||
var data = TestObjectsAsRecyclableArrayList;
|
var data = TestObjectsAsRecyclableArrayList;
|
||||||
@@ -128,7 +128,7 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
//[Benchmark]
|
||||||
public void RecyclableArrayList_GetItem()
|
public void RecyclableArrayList_GetItem()
|
||||||
{
|
{
|
||||||
var data = TestObjectsAsRecyclableArrayList;
|
var data = TestObjectsAsRecyclableArrayList;
|
||||||
@@ -139,7 +139,7 @@ namespace Recyclable.Collections.Benchmarks
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
//[Benchmark]
|
||||||
public void RecyclableArrayList_Count()
|
public void RecyclableArrayList_Count()
|
||||||
{
|
{
|
||||||
var data = _testRecyclableArrayList;
|
var data = _testRecyclableArrayList;
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
public bool IsReadOnly { get; } = false;
|
public bool IsReadOnly { get; } = false;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Add(T item)
|
public void Add(T item)
|
||||||
{
|
{
|
||||||
int requestedCapacity = _count + 1;
|
int requestedCapacity = _count + 1;
|
||||||
@@ -221,19 +221,90 @@ namespace Recyclable.Collections
|
|||||||
_count = targetCapacity;
|
_count = targetCapacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddRange(IEnumerable<T> source, int growByCount = 100)
|
public void AddRange(IEnumerable<T> source, int growByCount = RecyclableDefaults.MinPooledArrayLength)
|
||||||
{
|
{
|
||||||
ref int capacity = ref _capacity;
|
if (source is T[] sourceArray)
|
||||||
ref int count = ref _count;
|
|
||||||
foreach (var item in source)
|
|
||||||
{
|
{
|
||||||
if (capacity == count)
|
AddRange(sourceArray);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source is List<T> sourceList)
|
||||||
|
{
|
||||||
|
AddRange(sourceList);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source is RecyclableArrayList<T> sourceRecyclableArrayList)
|
||||||
|
{
|
||||||
|
AddRange(sourceRecyclableArrayList);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source is IList<T> sourceIList)
|
||||||
|
{
|
||||||
|
AddRange(sourceIList);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int capacity = _capacity;
|
||||||
|
int targetIndex = _count;
|
||||||
|
Span<T> memorySpan;
|
||||||
|
if (source.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity))
|
||||||
|
{
|
||||||
|
_ = EnsureCapacity(targetIndex + requiredAdditionalCapacity);
|
||||||
|
memorySpan = new(_memory);
|
||||||
|
foreach (var item in source)
|
||||||
{
|
{
|
||||||
_ = EnsureCapacity(capacity * growByCount);
|
memorySpan[targetIndex++] = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
Add(item);
|
_count = targetIndex;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
using var enumerator = source.GetEnumerator();
|
||||||
|
|
||||||
|
memorySpan = new(_memory);
|
||||||
|
if (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
int available = capacity - targetIndex;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (targetIndex + growByCount > capacity)
|
||||||
|
{
|
||||||
|
capacity = EnsureCapacity(capacity + growByCount);
|
||||||
|
memorySpan = new(_memory);
|
||||||
|
available = capacity - targetIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < available; i++)
|
||||||
|
{
|
||||||
|
memorySpan[targetIndex++] = enumerator.Current;
|
||||||
|
if (!enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < available)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_count = targetIndex;
|
||||||
|
|
||||||
|
//foreach (var item in source)
|
||||||
|
//{
|
||||||
|
// if (capacity == count)
|
||||||
|
// {
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Add(item);
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
public static class RecyclableListExtensions
|
public static class RecyclableListExtensions
|
||||||
{
|
{
|
||||||
private const int _minPooledArraySize = 100;
|
private const int _minPooledArraySize = RecyclableDefaults.MinPooledArrayLength;
|
||||||
|
|
||||||
public static void CopyItems<T>(this T[] memory, long startingIndex, long count, ref T[] destArray, long offset = 1)
|
public static void CopyItems<T>(this T[] memory, long startingIndex, long count, ref T[] destArray, long offset = 1)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -62,6 +62,22 @@ namespace Recyclable.CollectionsTests
|
|||||||
.And.BeEquivalentTo(testData);
|
.And.BeEquivalentTo(testData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ConstructorShouldAddItemsInCorrectOrderWhenSourceIsIEnumerable()
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
var testData = _testData;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var list = new RecyclableArrayList<int>(testData);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
_ = list.Capacity.Should().Be(32, "when we succeed in taking count without enumerating, then we allocate as much memory as needed, only");
|
||||||
|
_ = list.Should().HaveCount(testData.Count())
|
||||||
|
.And.ContainInConsecutiveOrder(testData)
|
||||||
|
.And.BeEquivalentTo(testData);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void InsertAtTheBeginningShouldMoveItems()
|
public void InsertAtTheBeginningShouldMoveItems()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in new issue
Block a user