-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Avoid extra Array.Copy
in List<T>.Insert(Range)
when the list is full
#90089
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
Conversation
Originally there are 3 `Array.Copy` during `List<T>.Insert` when `Capacity` is maxed out. Avoid this extra copy with a modified `Grow`. Fix dotnet#89915
Tagging subscribers to this area: @dotnet/area-system-collections Issue DetailsOriginally there are 3 Microbenchmark at the issue page. Fix #89915
|
List
at capacityList
at capacity
List
at capacityArray.Copy
when inserting to List<T>
at capacity
If I understand it correctly, this gives us up to 3.5% imprvoement for the best case - insertion at the 0 index in a large array, right? |
The gain is 20%-ish for insertion at 0 into 10000-long list.
(InsertRange: 100->100 or 10000->10000) The correct benchmark should utilize |
Array.Copy
when inserting to List<T>
at capacityArray.Copy
when inserting into a full List<T>
src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs
Outdated
Show resolved
Hide resolved
Array.Copy
when inserting into a full List<T>
Array.Copy
in List<T>.Insert(Range)
when the list is full
Maybe. It would be a tradeoff between steady state speed vs. and startup time and memory consumption. This PR is a similar tradeoff at smaller scale. It makes each |
I just did a microbenchmark and it seems replacing
Yes you are right. |
The bigger impact here would be with smaller collections, since |
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.
@karajas Thank you for your contribution!
PTAL at my comments, apply the changes and share updated perf numbers. Thanks!
src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs
Show resolved
Hide resolved
code formatting Co-authored-by: Adam Sitnik <[email protected]>
Shared logics of getting new capacities in Grow & GrowForInsertion are moved to a separated method.
code
code
|
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.
LGTM, thank you for your contribution @karakasa !
@adamsitnik btw do you have thoughts on how to benchmark this, which requires a new Currently I added a baseline function and subtract results, but it can be unstable and troublesome.
|
@karakasa this is tricky. You can combine |
It doesn't necessarily require a new List. For a small amount of overhead, it could do: CollectionsMarshal.SetCount(list, 0); or similar to put it back to the desired configuration. It could even use UnsafeAccessor to poke at list internals directly. |
Original issue: #89915 (by me) |
Originally there are 3
Array.Copy
duringList<T>.Insert
whenCapacity
is maxed out. Avoid one extra copy with a modifiedGrow
.Microbenchmark in following comments.
List<T>.InsertRange
also benefits from similar patterns. Performance is ~20% better for best cases.Fix #89915