-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Modify segmented list to grow by growth rate. #75756
Conversation
A growth rate of 2x matches the current code behavior. A growth rate of just over 1 matches the growth rate (1 segment) in the other PR (dotnet#75708). All growth rates benchmarked: 1.000001, 1.1, 1.25, 1.5, 2 Obviously, the single segment growth rate is a non-starter without allowing null segments (which is the approach the other PR took). The 2x rate matches current behavior, and is really only measured as a baseline. From my reading of this chart, it looks like 1.1 is the best of these choices.
@@ -42,6 +42,8 @@ internal class SegmentedList<T> : IList<T>, IList, IReadOnlyList<T> | |||
private static readonly SegmentedArray<T> s_emptyArray = new(0); | |||
private static IEnumerator<T>? s_emptyEnumerator; | |||
|
|||
public static double SegmentGrowthRate { get; set; } = 2.0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Note that this check works even when _items.Length overflowed thanks to the (uint) cast | ||
if ((uint)newCapacity > MaxLength) | ||
newCapacity = MaxLength; | ||
} | ||
|
||
// If the computed capacity is still less than specified, set to the original argument. | ||
// Capacities exceeding Array.MaxLength will be surfaced as OutOfMemoryException by Array.Resize. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Need to update the branch following this to ensure the array is still a multiple of the segment size unless it's less than a single segment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not really needed though, right? Seems like we should just respect the size they requested if it's larger than we would be resizing to otherwise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AddRange
can grow by more than one, but we still want to retain page alignment. We only need to allow non-alignment for small collections, and collections where the user set Capacity
directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved the code around, should be resolved
…mantics to indicate the shift amount, not the growth rate
Here's the numbers for shifts of 2 and 3 (1.25 and 1.125 growth rates respectively) with the test modified to specify the exact segment count such that the AddExtraItem takes it from a completely full segment group to the next outer segment allocation. The numbers are fairly close between the two shifts, with 1.25 performing better for full allocations and 1.125 performing better for "worst case" allocations, as expected. From my look over, 1.125 looks preferable, as it's looks like it does better when averaging out the best/worst cases for both wallclock time and allocations. |
…s properly size to execute in other cases.
…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.
…aught by speedometer (#75895) The change in #75756 was incorrect when the existing number of items is between SegmentSize / 2 (inclusive) and SegmentSize (exclusive). 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.
By modifying this growth rate, we can reduce the amount of waste in a SegmentedList in scenarios where it's final capacity isn't known beforehand.
A growth rate of 2x matches the current code behavior. A growth rate of just over 1 matches the growth rate (1 segment) in the other PR (#75708).
All growth rates benchmarked: 1.000001, 1.1, 1.25, 1.5, 2
Obviously, the single segment growth rate is a non-starter without allowing null segments (which is the approach the other PR took).
The 2x rate matches current behavior, and is really only measured as a baseline. From my reading of this chart, it looks like 1.1 is the best of these choices.
*** Long benchmarking results ***