diff --git a/perf/ListPool.Benchmarks/ListPool.Benchmarks.csproj b/perf/ListPool.Benchmarks/ListPool.Benchmarks.csproj
index edb391f..ec423fc 100644
--- a/perf/ListPool.Benchmarks/ListPool.Benchmarks.csproj
+++ b/perf/ListPool.Benchmarks/ListPool.Benchmarks.csproj
@@ -8,6 +8,7 @@
+
diff --git a/perf/ListPool.Benchmarks/ListPoolAddBenchmarks.cs b/perf/ListPool.Benchmarks/ListPoolAddBenchmarks.cs
index b01f346..144e3bb 100644
--- a/perf/ListPool.Benchmarks/ListPoolAddBenchmarks.cs
+++ b/perf/ListPool.Benchmarks/ListPoolAddBenchmarks.cs
@@ -11,53 +11,43 @@ namespace ListPool.Benchmarks
[GcConcurrent]
public class ListPoolAddBenchmarks
{
- private List _list;
- private ListPool _listPool;
- private ValueListPool _valueListPool;
-
- [Params(1000)]
+ [Params(100, 1000, 10000)]
public int N { get; set; }
- [IterationSetup]
- public void IterationSetup()
- {
- _list = new List(N);
- _listPool = new ListPool(N);
- _valueListPool = new ValueListPool(N);
- }
-
- [IterationCleanup]
- public void IterationCleanup()
- {
- _listPool.Dispose();
- _valueListPool.Dispose();
- }
-
[Benchmark(Baseline = true)]
- public void List()
+ public int List()
{
- for (int i = 0; i < N - 1; i++)
+ List list = new List(N);
+ for (int i = 0; i < N; i++)
{
- _list.Add(i);
+ list.Add(i);
}
+
+ return list.Count;
}
[Benchmark]
- public void ListPool()
+ public int ListPool()
{
- for (int i = 0; i < N - 1; i++)
+ using ListPool list = new ListPool(N);
+ for (int i = 0; i < N; i++)
{
- _listPool.Add(i);
+ list.Add(i);
}
+
+ return list.Count;
}
[Benchmark]
- public void ValueListPool()
+ public int ValueListPool()
{
- for (int i = 0; i < N - 1; i++)
+ using ValueListPool list = new ValueListPool(N);
+ for (int i = 0; i < N; i++)
{
- _valueListPool.Add(i);
+ list.Add(i);
}
+
+ return list.Count;
}
}
}
diff --git a/perf/ListPool.Benchmarks/Program.cs b/perf/ListPool.Benchmarks/Program.cs
index 13503d0..98b5970 100644
--- a/perf/ListPool.Benchmarks/Program.cs
+++ b/perf/ListPool.Benchmarks/Program.cs
@@ -1,5 +1,4 @@
-using System;
-using BenchmarkDotNet.Running;
+using BenchmarkDotNet.Running;
namespace ListPool.Benchmarks
{
@@ -7,8 +6,10 @@ internal class Program
{
private static void Main(string[] args)
{
- BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
- Console.ReadLine();
+ while (true)
+ {
+ BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
+ }
}
}
}
diff --git a/perf/ListPool.Benchmarks/Utf8JsonDeserializeListOfIntBenchmarks.cs b/perf/ListPool.Benchmarks/Utf8JsonDeserializeListOfIntBenchmarks.cs
index e73b1e2..860d513 100644
--- a/perf/ListPool.Benchmarks/Utf8JsonDeserializeListOfIntBenchmarks.cs
+++ b/perf/ListPool.Benchmarks/Utf8JsonDeserializeListOfIntBenchmarks.cs
@@ -14,7 +14,7 @@ public class Utf8JsonDeserializeListOfIntBenchmarks
{
private byte[] _serializedList;
- [Params(100, 1000, 1000)]
+ [Params(100, 1_000, 10_000)]
public int N { get; set; }
[GlobalSetup]
@@ -36,5 +36,18 @@ public int ListPool()
using ListPool list = Utf8Json.JsonSerializer.Deserialize>(_serializedList);
return list.Count;
}
+ [Benchmark]
+ public int ListPool_Spreads()
+ {
+ using ListPool list = Spreads.Serialization.Utf8Json.JsonSerializer.Deserialize>(_serializedList);
+ return list.Count;
+ }
+
+ [Benchmark]
+ public int List_Spreads()
+ {
+ List list = Spreads.Serialization.Utf8Json.JsonSerializer.Deserialize>(_serializedList);
+ return list.Count;
+ }
}
}
diff --git a/perf/ListPool.Benchmarks/Utf8JsonSerializeListOfIntBenchmarks.cs b/perf/ListPool.Benchmarks/Utf8JsonSerializeListOfIntBenchmarks.cs
new file mode 100644
index 0000000..0ec302e
--- /dev/null
+++ b/perf/ListPool.Benchmarks/Utf8JsonSerializeListOfIntBenchmarks.cs
@@ -0,0 +1,59 @@
+using System.Collections.Generic;
+using System.Linq;
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Order;
+using Utf8Json;
+
+namespace ListPool.Benchmarks
+{
+ [RPlotExporter]
+ [RankColumn]
+ [Orderer(SummaryOrderPolicy.FastestToSlowest)]
+ [MemoryDiagnoser]
+ [GcServer(true)]
+ [GcConcurrent]
+ public class Utf8JsonSerializeListOfIntBenchmarks
+ {
+ private List _list;
+ private ListPool _listPool;
+
+ [Params(100, 1_000, 10_000)]
+ public int N { get; set; }
+
+ [GlobalSetup]
+ public void GlobalSetup()
+ {
+ var items = Enumerable.Range(0, N).ToArray();
+ _listPool = items.ToListPool();
+ _list = items.ToList();
+ }
+
+ [GlobalCleanup]
+ public void GlobalCleanup()
+ {
+ _listPool.Dispose();
+ }
+
+ [Benchmark(Baseline = true)]
+ public int List()
+ {
+ byte[] serializedItems = JsonSerializer.Serialize(_list);
+ return serializedItems.Length;
+ }
+
+ [Benchmark]
+ public int ListPool()
+ {
+ byte[] serializedItems = JsonSerializer.Serialize(_listPool);
+ return serializedItems.Length;
+ }
+
+
+ [Benchmark]
+ public int ListPool_Spreads()
+ {
+ byte[] serializedItems = Spreads.Serialization.Utf8Json.JsonSerializer.Serialize(_listPool);
+ return serializedItems.Length;
+ }
+ }
+}
diff --git a/src/ListPool/IValueEnumerable.cs b/src/ListPool/IValueEnumerable.cs
deleted file mode 100644
index 898e628..0000000
--- a/src/ListPool/IValueEnumerable.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Collections.Generic;
-
-namespace ListPool
-{
- internal interface IValueEnumerable : IEnumerable
- {
- new ValueEnumerator GetEnumerator();
- }
-}
diff --git a/src/ListPool/ListPool.cs b/src/ListPool/ListPool.cs
index 4a2b748..9e1d41f 100644
--- a/src/ListPool/ListPool.cs
+++ b/src/ListPool/ListPool.cs
@@ -3,7 +3,6 @@
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
-using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Threading;
@@ -14,11 +13,10 @@ namespace ListPool
/// With overhead being the class itself regardless the size of the underlying array.
///
///
- public sealed class ListPool : IList, IList, IReadOnlyList, IDisposable,
- IValueEnumerable
-
+ [Serializable]
+ public sealed class ListPool : IList, IList, IReadOnlyList, IDisposable
{
- private const int MinimumCapacity = 128;
+ private const int MinimumCapacity = 64;
private T[] _buffer;
[NonSerialized]
@@ -53,20 +51,37 @@ public ListPool(IEnumerable source)
{
if (source is ICollection collection)
{
- _buffer = ArrayPool.Shared.Rent(collection.Count);
+ T[] buffer = ArrayPool.Shared.Rent(collection.Count > MinimumCapacity ? collection.Count : MinimumCapacity);
+
+ collection.CopyTo(buffer, 0);
- collection.CopyTo(_buffer, 0);
- _count = collection.Count;
+ _buffer = buffer;
+ Count = collection.Count;
}
else
{
_buffer = ArrayPool.Shared.Rent(MinimumCapacity);
-
+ T[] buffer = _buffer;
+ Count = 0;
+ int count = 0;
using IEnumerator enumerator = source.GetEnumerator();
while (enumerator.MoveNext())
{
- Add(enumerator.Current);
+ if (count < buffer.Length)
+ {
+ buffer[count] = enumerator.Current;
+ count++;
+ }
+ else
+ {
+ Count = count;
+ count++;
+ AddWithResize(enumerator.Current);
+ buffer = _buffer;
+ }
}
+
+ Count = count;
}
}
@@ -80,9 +95,10 @@ public ListPool(IEnumerable source)
///
public void Dispose()
{
- _count = 0;
- if (_buffer != null)
- ArrayPool.Shared.Return(_buffer);
+ Count = 0;
+ T[] buffer = _buffer;
+ if (buffer != null)
+ ArrayPool.Shared.Return(buffer);
}
int ICollection.Count => Count;
@@ -103,6 +119,7 @@ object ICollection.SyncRoot
}
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
int IList.Add(object item)
{
if (item is T itemAsTSource)
@@ -140,29 +157,28 @@ int IList.IndexOf(object item)
void IList.Remove(object item)
{
- if (item is null)
+ if (item is T itemAsTSource)
{
- return;
+ Remove(itemAsTSource);
}
-
- if (!(item is T itemAsTSource))
+ else if (item != null)
{
throw new ArgumentException($"Wrong value type. Expected {typeof(T)}, got: '{item}'.",
nameof(item));
}
-
- Remove(itemAsTSource);
}
void IList.Insert(int index, object item)
{
- if (!(item is T itemAsTSource))
+ if (item is T itemAsTSource)
+ {
+ Insert(index, itemAsTSource);
+ }
+ else
{
throw new ArgumentException($"Wrong value type. Expected {typeof(T)}, got: '{item}'.",
nameof(item));
}
-
- Insert(index, itemAsTSource);
}
void ICollection.CopyTo(Array array, int arrayIndex)
@@ -173,11 +189,10 @@ void ICollection.CopyTo(Array array, int arrayIndex)
[MaybeNull]
object IList.this[int index]
{
- [Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
- if (index >= _count)
+ if (index >= Count)
throw new IndexOutOfRangeException(nameof(index));
return _buffer[index];
@@ -185,7 +200,7 @@ object IList.this[int index]
set
{
- if (index >= _count)
+ if (index >= Count)
throw new IndexOutOfRangeException(nameof(index));
if (value is T valueAsTSource)
@@ -203,27 +218,34 @@ object IList.this[int index]
///
/// Count of items added.
///
- public int Count => _count;
-
- private int _count;
+ public int Count { get; private set; }
public bool IsReadOnly => false;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(T item)
{
- if (_count >= _buffer.Length) GrowBufferDoubleSize();
+ T[] buffer = _buffer;
+ int count = Count;
- _buffer[_count++] = item;
+ if (count < buffer.Length)
+ {
+ buffer[count] = item;
+ Count = count + 1;
+ }
+ else
+ {
+ AddWithResize(item);
+ }
}
- public void Clear() => _count = 0;
+ public void Clear() => Count = 0;
public bool Contains(T item) => IndexOf(item) > -1;
- public int IndexOf(T item) => Array.IndexOf(_buffer, item, 0, _count);
+ public int IndexOf(T item) => Array.IndexOf(_buffer, item, 0, Count);
public void CopyTo(T[] array, int arrayIndex) =>
- Array.Copy(_buffer, 0, array, arrayIndex, _count);
+ Array.Copy(_buffer, 0, array, arrayIndex, Count);
public bool Remove(T item)
{
@@ -241,21 +263,42 @@ public bool Remove(T item)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Insert(int index, T item)
{
- if (index > _count) throw new IndexOutOfRangeException(nameof(index));
- if (index >= _buffer.Length) GrowBufferDoubleSize();
- if (index < _count)
- Array.Copy(_buffer, index, _buffer, index + 1, _count - index);
+ int count = Count;
+ T[] buffer = _buffer;
- _buffer[index] = item;
- _count++;
+ if (buffer.Length == count)
+ {
+ count *= 2;
+ GrowBuffer(count);
+ buffer = _buffer;
+ }
+
+ if (index < Count)
+ {
+ if (index < count)
+ Array.Copy(buffer, index, buffer, index + 1, count - index);
+
+ buffer[index] = item;
+ Count++;
+ }
+ else if (index == Count)
+ {
+ buffer[index] = item;
+ Count++;
+ }
+ else if (index > count) throw new IndexOutOfRangeException(nameof(index));
}
public void RemoveAt(int index)
{
- if (index >= _count) throw new IndexOutOfRangeException(nameof(index));
+ int count = Count;
+ T[] buffer = _buffer;
- _count--;
- Array.Copy(_buffer, index + 1, _buffer, index, _count - index);
+ if (index >= count) throw new IndexOutOfRangeException(nameof(index));
+
+ count--;
+ Array.Copy(buffer, index + 1, buffer, index, count - index);
+ Count = count;
}
[MaybeNull]
@@ -264,7 +307,7 @@ public T this[int index]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
- if (index >= _count)
+ if (index >= Count)
throw new IndexOutOfRangeException(nameof(index));
return _buffer[index];
@@ -272,93 +315,177 @@ public T this[int index]
set
{
- if (index >= _count)
+ if (index >= Count)
throw new IndexOutOfRangeException(nameof(index));
_buffer[index] = value;
}
}
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
-
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ValueEnumerator GetEnumerator() =>
- new ValueEnumerator(_buffer, _count);
-
public void AddRange(Span items)
{
- bool isCapacityEnough = _buffer.Length - items.Length - _count > 0;
+ int count = Count;
+ T[] buffer = _buffer;
+
+ bool isCapacityEnough = buffer.Length - items.Length - count > 0;
if (!isCapacityEnough)
- GrowBuffer(_buffer.Length + items.Length);
+ {
+ GrowBuffer(buffer.Length + items.Length);
+ buffer = _buffer;
+ }
- items.CopyTo(_buffer.AsSpan().Slice(Count));
- _count += items.Length;
+ items.CopyTo(buffer.AsSpan().Slice(count));
+ Count += items.Length;
}
public void AddRange(ReadOnlySpan items)
{
- bool isCapacityEnough = _buffer.Length - items.Length - _count > 0;
+ int count = Count;
+ T[] buffer = _buffer;
+
+ bool isCapacityEnough = buffer.Length - items.Length - count > 0;
if (!isCapacityEnough)
- GrowBuffer(_buffer.Length + items.Length);
+ {
+ GrowBuffer(buffer.Length + items.Length);
+ buffer = _buffer;
+ }
- items.CopyTo(_buffer.AsSpan().Slice(Count));
- _count += items.Length;
+ items.CopyTo(buffer.AsSpan().Slice(count));
+ Count += items.Length;
}
public void AddRange(T[] array) => AddRange(array.AsSpan());
public void AddRange(IEnumerable items)
{
+ int count = Count;
+ T[] buffer = _buffer;
+
if (items is ICollection collection)
{
- bool isCapacityEnough = _buffer.Length - collection.Count - _count > 0;
+ bool isCapacityEnough = buffer.Length - collection.Count - count > 0;
if (!isCapacityEnough)
- GrowBuffer(_buffer.Length + collection.Count);
+ {
+ GrowBuffer(buffer.Length + collection.Count);
+ buffer = _buffer;
+ }
- collection.CopyTo(_buffer, _count);
- _count += collection.Count;
+ collection.CopyTo(buffer, count);
+ Count += collection.Count;
}
else
{
foreach (T item in items)
{
- if (Count >= _buffer.Length) GrowBufferDoubleSize();
- _buffer[_count++] = item;
+ if (count < buffer.Length)
+ {
+ buffer[count] = item;
+ count++;
+ }
+ else
+ {
+ Count = count;
+ AddWithResize(item);
+ count++;
+ buffer = _buffer;
+ }
}
+
+ Count = count;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Span AsSpan() => _buffer.AsSpan(0, _count);
+ public Span AsSpan() => _buffer.AsSpan(0, Count);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Memory AsMemory() => _buffer.AsMemory(0, _count);
+ public Memory AsMemory() => _buffer.AsMemory(0, Count);
[MethodImpl(MethodImplOptions.NoInlining)]
- public void GrowBufferDoubleSize()
+ private void AddWithResize(T item)
{
- int newLength = _buffer.Length * 2;
- var newBuffer = ArrayPool.Shared.Rent(newLength);
- var oldBuffer = _buffer;
+ ArrayPool arrayPool = ArrayPool.Shared;
+ T[] oldBuffer = _buffer;
+ T[] newBuffer = arrayPool.Rent(oldBuffer.Length * 2);
+ int count = oldBuffer.Length;
- Array.Copy(oldBuffer, 0, newBuffer, 0, _buffer.Length);
+ Array.Copy(oldBuffer, 0, newBuffer, 0, count);
+ newBuffer[count] = item;
_buffer = newBuffer;
- ArrayPool.Shared.Return(oldBuffer);
+ Count = count + 1;
+ arrayPool.Return(oldBuffer);
}
[MethodImpl(MethodImplOptions.NoInlining)]
- public void GrowBuffer(int capacity)
+ private void GrowBuffer(int capacity)
{
- var newBuffer = ArrayPool.Shared.Rent(capacity);
- var oldBuffer = _buffer;
+ ArrayPool arrayPool = ArrayPool.Shared;
+ T[] newBuffer = arrayPool.Rent(capacity);
+ T[] oldBuffer = _buffer;
- Array.Copy(oldBuffer, 0, newBuffer, 0, _buffer.Length);
+ Array.Copy(oldBuffer, 0, newBuffer, 0, oldBuffer.Length);
_buffer = newBuffer;
- ArrayPool.Shared.Return(oldBuffer);
+ arrayPool.Return(oldBuffer);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_buffer, Count);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_buffer, Count);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Enumerator GetEnumerator() => new Enumerator(_buffer, Count);
+
+ public struct Enumerator : IEnumerator
+ {
+ private readonly T[] _source;
+ private readonly int _itemsCount;
+ private int _index;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Enumerator(T[] source, int itemsCount)
+ {
+ _source = source;
+ _itemsCount = itemsCount;
+ _index = -1;
+ }
+
+ [MaybeNull]
+ public readonly ref T Current
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => ref _source[_index];
+ }
+
+ [MaybeNull]
+ readonly T IEnumerator.Current
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _source[_index];
+ }
+
+ [MaybeNull]
+ readonly object? IEnumerator.Current
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _source[_index];
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool MoveNext() => unchecked(++_index < _itemsCount);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Reset()
+ {
+ _index = -1;
+ }
+
+ public readonly void Dispose()
+ {
+ }
}
}
}
diff --git a/src/ListPool/ListPool.csproj b/src/ListPool/ListPool.csproj
index f43aa26..5fb9337 100644
--- a/src/ListPool/ListPool.csproj
+++ b/src/ListPool/ListPool.csproj
@@ -9,16 +9,14 @@
ListPool
ListPool
ListPool and ValueListPool are an optimized allocation free implementations of IList using ArrayPool.
- ASP.NET;List;System.Buffers;ArrayPool;ListPool;Performance
+ ASP.NET;List;System.Buffers;ArrayPool;ListPool;Performance;Span
https://github.com/faustodavid/ListPool
true
LICENSE
- 2.2.0
+ 2.2.1
Changelog:
- * Improve performance of ValueListPool by removing buffer checks.Now is required to avoid parametless constructor for ValueListPool.
- * Add support for span and memory
- * Add method AddRange
+ * Improve overall performance of ListPool and ValueListPool
ListPool is the general use of the implementation. ValueListPool is the zero heap allocations implementation. Note, because it is a struct it is passed by value, not by reference.
diff --git a/src/ListPool/ValueEnumerator.cs b/src/ListPool/ValueEnumerator.cs
deleted file mode 100644
index 2650b6b..0000000
--- a/src/ListPool/ValueEnumerator.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using System.Collections;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
-
-namespace ListPool
-{
- public struct ValueEnumerator : IEnumerator
- {
- private readonly T[] _source;
- private readonly int _itemsCount;
- private int _index;
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ValueEnumerator(T[] source, int itemsCount)
- {
- _source = source;
- _itemsCount = itemsCount;
- _index = -1;
- }
-
- [MaybeNull]
- public readonly ref T Current
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => ref _source[_index];
- }
-
- [MaybeNull]
- readonly T IEnumerator.Current
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get { return _source[_index]; }
- }
-
- [MaybeNull]
- readonly object? IEnumerator.Current
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get { return _source[_index]; }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool MoveNext() => ++_index < _itemsCount;
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Reset()
- {
- _index = -1;
- }
-
- public readonly void Dispose()
- {
- }
- }
-}
diff --git a/src/ListPool/ValueListPool.cs b/src/ListPool/ValueListPool.cs
index 76ce3e0..85c06f5 100644
--- a/src/ListPool/ValueListPool.cs
+++ b/src/ListPool/ValueListPool.cs
@@ -3,7 +3,6 @@
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
-using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Threading;
@@ -15,215 +14,244 @@ namespace ListPool
/// Otherwise it wont work.
///
///
- public struct ValueListPool : IList, IList, IReadOnlyList, IDisposable,
- IValueEnumerable
+ public struct ValueListPool : IList, IList, IReadOnlyList, IDisposable
- {
- ///
- /// Capacity of the underlying array.
- ///
- public readonly int Capacity => _buffer.Length;
-
- ///
- /// Count of items added.
- ///
- public int Count { get; private set; }
-
- public readonly bool IsReadOnly => false;
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public readonly Span AsSpan() => _buffer.AsSpan(0, Count);
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public readonly Memory AsMemory() => _buffer.AsMemory(0, Count);
-
- int ICollection.Count => Count;
- readonly bool IList.IsFixedSize => false;
- bool ICollection.IsSynchronized => false;
- readonly bool IList.IsReadOnly => false;
+ {
+ private const int MinimumCapacity = 64;
private T[] _buffer;
- private const int MinimumCapacity = 128;
[NonSerialized]
private object? _syncRoot;
- object ICollection.SyncRoot
- {
- get
- {
- if (_syncRoot is null)
- {
- _ = Interlocked.CompareExchange