Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modify segmented list to grow by growth rate. #75756

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,11 @@ public void EnsureCapacity_MatchesSizeWithLargeCapacityRequest(int segmentCount)

var requestedCapacity = 2 * elementCount + 10;
list.EnsureCapacity(requestedCapacity);
Assert.Equal(requestedCapacity, list.Capacity);

var lastSegmentLength = requestedCapacity % SegmentedArray<T>.TestAccessor.SegmentSize;
var expectedCapacity = (requestedCapacity - lastSegmentLength) + SegmentedArray<T>.TestAccessor.SegmentSize;

Assert.Equal(expectedCapacity, list.Capacity);
}
}
}
43 changes: 37 additions & 6 deletions src/Dependencies/Collections/SegmentedList`1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -512,18 +512,49 @@ internal void Grow(int capacity)
{
Debug.Assert(_items.Length < capacity);

var newCapacity = _items.Length == 0 ? DefaultCapacity : 2 * _items.Length;
var newCapacity = 0;

// Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow.
// Note that this check works even when _items.Length overflowed thanks to the (uint) cast
if ((uint)newCapacity > MaxLength)
newCapacity = MaxLength;
if (_items.Length < SegmentedArrayHelper.GetSegmentSize<T>() / 2)
{
// The array isn't near the maximum segment size. If the array is empty, the new capacity
// should be DefaultCapacity. Otherwise, the new capacity should be double the current array size.
newCapacity = _items.Length == 0 ? DefaultCapacity : _items.Length * 2;
}
else
{
// If the last segment is fully sized, increase the number of segments by the desired growth rate
if (0 == (_items.Length & SegmentedArrayHelper.GetOffsetMask<T>()))
{
// This value determines the growth rate of the number of segments to use.
// For a value of 3, this means the segment count will grow at a rate of
// 1 + (1 >> 3) or 12.5%
const int segmentGrowthShiftValue = 3;

var oldSegmentCount = (_items.Length + SegmentedArrayHelper.GetSegmentSize<T>() - 1) >> SegmentedArrayHelper.GetSegmentShift<T>();
var newSegmentCount = oldSegmentCount + Math.Max(1, oldSegmentCount >> segmentGrowthShiftValue);

// If the computed capacity is still less than specified, set to the original argument.
newCapacity = SegmentedArrayHelper.GetSegmentSize<T>() * newSegmentCount;
}
}

// If the computed capacity is less than specified, set to the original argument.
// Capacities exceeding Array.MaxLength will be surfaced as OutOfMemoryException by Array.Resize.
if (newCapacity < capacity)
newCapacity = capacity;

if (newCapacity > SegmentedArrayHelper.GetSegmentSize<T>())
{
// If the last segment isn't fully sized, increase the new capacity such that it will be.
var lastSegmentLength = newCapacity & SegmentedArrayHelper.GetOffsetMask<T>();
if (lastSegmentLength > 0)
newCapacity = (newCapacity - lastSegmentLength) + SegmentedArrayHelper.GetSegmentSize<T>();

// Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow.
// Note that this check works even when _items.Length overflowed thanks to the (uint) cast
if ((uint)newCapacity > MaxLength)
newCapacity = MaxLength;
}

Capacity = newCapacity;
}

Expand Down
25 changes: 9 additions & 16 deletions src/Tools/IdeCoreBenchmarks/SegmentedListBenchmarks_Add.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,28 +38,21 @@ private void AddToList<T>(T item)
array.Add(item);
}

private struct LargeStruct
private struct MediumStruct
{
public int i1 { get; set; }
public int i2 { get; set; }
public int i3 { get; set; }
public int i4 { get; set; }
public int i5 { get; set; }
public int i6 { get; set; }
public int i7 { get; set; }
public int i8 { get; set; }
public int i9 { get; set; }
public int i10 { get; set; }
public int i11 { get; set; }
public int i12 { get; set; }
public int i13 { get; set; }
public int i14 { get; set; }
public int i15 { get; set; }
public int i16 { get; set; }
public int i17 { get; set; }
public int i18 { get; set; }
public int i19 { get; set; }
public int i20 { get; set; }
}

private struct LargeStruct
{
public MediumStruct s1 { get; set; }
public MediumStruct s2 { get; set; }
public MediumStruct s3 { get; set; }
public MediumStruct s4 { get; set; }
}

private struct EnormousStruct
Expand Down