Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Reduce linked node size in Enumerable.Append/Prepend/Union #15389

Merged
merged 3 commits into from
Feb 3, 2017

Conversation

jamesqo
Copy link
Contributor

@jamesqo jamesqo commented Jan 22, 2017

  • For Append / Prepend the count of previous iterators isn't needed by this iterator, so we can move that stuff from the nodes to the iterators which can be GC'd on successive linked Appends/Prepends.

  • Similarly, all of the non-readonly state for Union of previous iterators isn't relevant for this iterator, so use SingleLinkedNode to only keep alive what we need (the sources). Additionally, all of the iterators have the same comparer so we only need to store the comparer in the latest node.

  • Introduce Set.UnionWith for brevity and move SingleLinkedNode into its own file.

edit: Was finally able to run perf tests for this. GCs have increased, as expected, because there is a substantial reduction in leaked memory. Concat and Union see 50-60% reductions in leaked memory, Append and Prepend about 20%.

edit 2: Ignore the results for Concat, I hit a few complications there and the changes became pretty big. I'm going to save them for another PR.

/cc @JonHanna @VSadov

@jamesqo jamesqo changed the title Reduce linked node size in Enumerable.Append/Prepend/Union [No merge] Reduce linked node size in Enumerable.Append/Prepend/Union Jan 22, 2017
@karelz karelz added * NO MERGE * The PR is not ready for merge yet (see discussion for detailed reasons) area-System.Linq labels Jan 23, 2017
@jamesqo jamesqo changed the title [No merge] Reduce linked node size in Enumerable.Append/Prepend/Union [No merge] Reduce linked node size in Enumerable.Append/Prepend/Union/Concat Jan 28, 2017
@jamesqo jamesqo changed the title [No merge] Reduce linked node size in Enumerable.Append/Prepend/Union/Concat [WIP] [No merge] Reduce linked node size in Enumerable.Append/Prepend/Union/Concat Jan 29, 2017
@jamesqo jamesqo changed the title [WIP] [No merge] Reduce linked node size in Enumerable.Append/Prepend/Union/Concat Reduce linked node size in Enumerable.Append/Prepend/Union/Concat Jan 29, 2017
@jamesqo
Copy link
Contributor Author

jamesqo commented Jan 29, 2017

OK, I was finally able to post perf tests! 🎉 (see the description)

@JonHanna, @stephentoub, @VSadov PTAL

@karelz This can be un-marked as no merge.

@JonHanna
Copy link
Contributor

I'm honestly not sure whether to consider this an improvement or not.

I'd be inclined to favour what benefits Concat and Union over what favours Append and Prepend and this seems to do the opposite in terms of speed if I'm reading the performance tests correctly. But maybe I shouldn't think that way (how much can we perhaps expect heavy use of Append and Prepend in the future?).

They all benefit in terms of memory, but if the objects produced were short-lived (which is v. common with linq) it would all be collected anyway, no? I'm not sure how to judge the value of something that saves in that regard but increases GC calls.

I does seem to have simplified things a bit, and that's always a good thing.

@jamesqo jamesqo changed the title Reduce linked node size in Enumerable.Append/Prepend/Union/Concat Reduce linked node size in Enumerable.Append/Prepend/Union Jan 30, 2017
@jamesqo
Copy link
Contributor Author

jamesqo commented Jan 30, 2017

I'd be inclined to favour what benefits Concat and Union over what favours Append and Prepend and this seems to do the opposite in terms of speed if I'm reading the performance tests correctly.

The performance tests were only written to illustrate the impact on memory retention, not CPU time. It does not actually iterate any of the enumerables returned from the Linq methods. Iteration would quickly overshadow any impact on the time it takes to create the iterators.

I have encountered significant speed/memory regressions when calling Count/ToArray/ToList on Concat iterators, though, so you are correct in that sense. This is mostly because there are extra virtual calls and unnecessary re-walks of the linked list via GetEnumerable. I'm deciding to save those changes for a later PR because it's a lot more code and I'm no longer sure deleting the iterators is a clear win.

They all benefit in terms of memory, but if the objects produced were short-lived (which is v. common with linq) it would all be collected anyway, no? I'm not sure how to judge the value of something that saves in that regard but increases GC calls.

I actually thought that allowing older iterators to be GC'd was the reason you used SingleLinkedNode in your original Append/Prepend implementation, instead of putting the items directly in the iterator. Was that the case, or did you do it for some other reason?

BTW, there is also a benefit (albeit small) in terms of CPU time here too: since nodes have uniform type we no longer have to typecast when walking the linked list, since the tail is just represented as a null. https://github.com/dotnet/corefx/pull/15389/files#diff-49756bcd3a3a836bf3963f982c6af0f8L253

@JonHanna
Copy link
Contributor

Ah. I see where you're going now.

So all that's left from what I've said above is that it seems to have simplified things, which is good 😄

@JonHanna
Copy link
Contributor

JonHanna commented Jan 30, 2017

Looking at the results at first I thought perhaps it was forcing more GC rather than allowing sooner GC. Seeing it's the latter, this seems like good stuff. I like the unionwith added, too. Nothing seems awry, so it's all down to the perf results really, so LGTM.

@karelz karelz removed the * NO MERGE * The PR is not ready for merge yet (see discussion for detailed reasons) label Jan 30, 2017
@karelz
Copy link
Member

karelz commented Jan 30, 2017

@jamesqo This can be un-marked as no merge.

Done

Copy link
Member

@VSadov VSadov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@VSadov VSadov merged commit d0001fe into dotnet:master Feb 3, 2017
@jamesqo jamesqo deleted the change.linking branch February 3, 2017 02:26
@karelz karelz modified the milestone: 2.0.0 Feb 4, 2017
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
Reduce linked node size in Enumerable.Append/Prepend/Union

Commit migrated from dotnet/corefx@d0001fe
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants