-
Notifications
You must be signed in to change notification settings - Fork 255
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
Reduce allocations in TokenSegment.TryMatch #12728
Comments
Proposed Solution
Result
Conclusion possible solution
|
@Nigusu-Allehu Can you point me to the dictionary lookup? |
The TryMatch method eventually, most of the time, calls on of the parsers in the ManagedCodeConventions.cs. Here is an example : https://github.com/NuGet/NuGet.Client/blob/b02fdbb36d34947c24496b2c0b505aa785bc657f/src/NuGet.Core/NuGet.Packaging/ContentModel/ManagedCodeConventions.cs#L128 |
Is this code path only used by packages that have |
It is used by all of them. https://github.com/NuGet/NuGet.Client/blob/b02fdbb36d34947c24496b2c0b505aa785bc657f/src/NuGet.Core/NuGet.Packaging/ContentModel/ManagedCodeConventions.cs#L411 in here there is a list of patterns that a package could have for its asset files. TokenSegments in these patterns always use a parser. For example for the pattern "lib/{tfm}/{assembly}", tfm and assembly are TokenSegments and as a result a parser would be used to identify the tfm and assembly. |
Ok. What I'm thinking is that NuGet could "pre-compute" assets lists per TFM on package extraction, and save the results in the values in the This will be a lot more difficult to implement than refactoring a single class for optimizations. But I think it has the potential for significant perf improvements. Other people should definately think about the suggestion, in case I'm way off base. CI restore might not benefit a whole lot, since they'll have to extract packages and do the asset pre-compute on every restore. But on developer boxes where the global packages folder isn't deleted frequently (for example, where customers use VS), I think it could really have a decent (maybe even big?) improvement. I think NuGet's "convention based" approach makes manually creating packages a little easier, but I think it harms restore performance. We can't change the past (and I might be wrong that a more explicit package format would be faster to restore), but we can take advantage of NuGet's "generated on extraction" files. The Unfortunately, for read-only fallback folders, we'll need to keep using the convention based approach, but if whatever creates these fallback folder layouts updates to a newer NuGet, then the higher However, the big question is whether this idea will actually benefit perf. |
@Nigusu-Allehu Is this the table you a referring to? This is currently defined using strings, but doesn't have to be, this could be: private readonly Dictionary<ReadOnlyMemory<Char>, Dictionary<ReadOnlyMemory<Char>, object>> _table = new Dictionary<ReadOnlyMemory<Char>, Dictionary<ReadOnlyMemory<Char>, object>>(ReadOnlyMemoryCharOrdinalComparer.Instance); Where |
I do want to say that @zivkan's idea is a good one, but I don't want to lose sight here that there might be an easier approach we can take to reduce the allocations on this path first. |
I intended to write "if we can't find easy wins" in my previous message. Looks like I forgot to 🤦♂️ |
Thank you, David! I will look into it |
Related PRs:
What I'll look into next is the number of dictionaries we create. |
A couple of more:
The 2nd is not just this codepath, but related as well, and the savings are about ~50MB there. |
A 6th change: After all these changes, we're down from:
to:
40MB of improvement in TryMatch, or about 36% in OrchardCore scenarios. What's leftover is dictionary usage for tokens found. At this point, there are might be more opportunities for improvement in other codepaths rather than this one. I'll timebox some POC tests with removing the dictionary and see if I can get that to improve. |
Great progress @nkolev92, appreciate the updates. |
At this point, I think I've reached my limit. I think the numbers look pretty good. If anyone is curious, my current playground branch is (along with benchmark code) is https://github.com/NuGet/NuGet.Client/compare/nkolev92-benchmarks?expand=1 |
#13712 fixed by NuGet/NuGet.Client#5977, is #2 above. Reduces 100MB in OrchardCore. It affects TryMatch a little bit as well since it gets called less frequently. I'll collect a final trace when both PRs are merged. |
When NuGet/NuGet.Client#5977 is merged, it'll be down to ~34MB.
|
Internal Tracking issue
The text was updated successfully, but these errors were encountered: