Skip to content

Commit

Permalink
Fix an allocation regression due to the recent SegmentedList change c…
Browse files Browse the repository at this point in the history
…aught by speedometer

The change in dotnet#75756 was incorrect when the existing number of items is between SegmentSize /2 and SegmentSize. In this case, the size of the newCapacity would end up as exactly the requested capacity, causing a potentially O(n^2) allocation growth pattern if caller was just increasing the requested capacity by one from it's current size.

The fix is just to handle that case directly, and if the existing size falls into that range, to simply set the desired newCapacity to the SegmentSize.
  • Loading branch information
ToddGrun committed Nov 13, 2024
1 parent 42613df commit 03d86fc
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -140,5 +140,18 @@ public void EnsureCapacity_MatchesSizeWithLargeCapacityRequest(int segmentCount)

Assert.Equal(expectedCapacity, list.Capacity);
}

[Fact]
public void EnsureCapacity_InitialCapacitySlightlyMoreThanHalfSegmentSizeGrowsToFullSegmentSize()
{
var elementCount = SegmentedArray<T>.TestAccessor.SegmentSize / 2 + 1;
var list = new SegmentedList<T>(elementCount);

Assert.Equal(elementCount, list.Capacity);

list.EnsureCapacity(elementCount + 1);

Assert.Equal(SegmentedArray<T>.TestAccessor.SegmentSize, list.Capacity);
}
}
}
5 changes: 5 additions & 0 deletions src/Dependencies/Collections/SegmentedList`1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,11 @@ internal void Grow(int 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 (_items.Length < SegmentedArrayHelper.GetSegmentSize<T>())
{
// There is only a single segment that is over half full. Increase it to a full segment.
newCapacity = SegmentedArrayHelper.GetSegmentSize<T>();
}
else
{
// If the last segment is fully sized, increase the number of segments by the desired growth rate
Expand Down

0 comments on commit 03d86fc

Please sign in to comment.