-
Notifications
You must be signed in to change notification settings - Fork 702
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
Replace JObjects in PackageMetadataResourceV3 with strong type for better performance, lower memory consumption #3462
Replace JObjects in PackageMetadataResourceV3 with strong type for better performance, lower memory consumption #3462
Conversation
src/NuGet.Core/NuGet.Protocol/Resources/PackageMetadataResourceV3.cs
Outdated
Show resolved
Hide resolved
Where are the before and after benchmarks? |
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.
Nice! Few questions/comments..
src/NuGet.Core/NuGet.Protocol/Resources/PackageMetadataResourceV3.cs
Outdated
Show resolved
Hide resolved
.Select(ParseMetadata) | ||
.Select(m => metadataCache.GetObject(m)) | ||
.ToArray(); | ||
var range = VersionRange.All; // This value preset for Consolidate UI. |
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.
Are you sure this method is specific to Consolidate? I think several UI codepaths come here.
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.
plus potentially any customer who has a PackageReference
to NuGet.Protocol
in their own app.
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.
I changed wording. Actually it's from "Update UI" too.
src/NuGet.Core/NuGet.Protocol/Resources/PackageMetadataResourceV3.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Protocol/Resources/PackageMetadataResourceV3.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Protocol/Resources/PackageMetadataResourceV3.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Protocol/Resources/PackageMetadataResourceV3.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Protocol/Resources/PackageMetadataResourceV3.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Protocol/Resources/PackageMetadataResourceV3.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Protocol/Utility/MetadataReferenceCache.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Protocol/Utility/MetadataReferenceCache.cs
Outdated
Show resolved
Hide resolved
@erdembayar Can you provide some benchmarks please. Ensure you profile with different sizes of registration pages. |
I didn't do any benchmarking yet. But finally I was able to see |
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.
If this PR is the only change needed for NuGet/Home#8352, then ok. If that issue is going to need other changes, then please create a new issue specific to the changes in this PR, and link that issue instead.
src/NuGet.Core/NuGet.Protocol/Extensions/VersionRangeExtensions.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Protocol/Extensions/VersionRangeExtensions.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Protocol/Resources/PackageMetadataResourceV3.cs
Outdated
Show resolved
Hide resolved
.Select(ParseMetadata) | ||
.Select(m => metadataCache.GetObject(m)) | ||
.ToArray(); | ||
var range = VersionRange.All; // This value preset for Consolidate UI. |
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.
plus potentially any customer who has a PackageReference
to NuGet.Protocol
in their own app.
src/NuGet.Core/NuGet.Protocol/Resources/PackageMetadataResourceV3.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Protocol/Resources/PackageMetadataResourceV3.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Protocol/Resources/PackageMetadataResourceV3.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Protocol/Resources/PackageMetadataResourceV3.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Protocol/Resources/PackageMetadataResourceV3.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Protocol/Resources/PackageMetadataResourceV3.cs
Outdated
Show resolved
Hide resolved
Also, this PR's title suggests that all JObjects thoughout the entire codebase were replaced. Please change the title so it's obvious that it's specific to the protocol registration resources. |
Ok. I changed PR name. |
{ | ||
catalogEntry.ReportAbuseUrl = _reportAbuseResource?.GetReportAbuseUrl(catalogEntry.PackageId, catalogEntry.Version); | ||
catalogEntry.PackageDetailsUrl = _packageDetailsUriResource?.GetUri(catalogEntry.PackageId, catalogEntry.Version); | ||
metadataCache.GetObject(catalogEntry); |
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.
The return value of GetObject
isn't used. Can we stop calling it and get rid of the changes related to reflection?
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.
Actually I checked it what this thing is doing, it do have benefit. It depopulate same string contents because of string
is true immutable type. All same content strings point to same memory address, I did manually checked it, it works for string type, so I decided to improve it. It's hard to explain here, but I can talk to you on Teams.
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.
I'm not sure how this is helping. This is trying to dedup the full object. I don't see the NuGetVersion and Strings being deduped at all.
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.
This caching method was already here before my change so I don't expect any drastic changes, I only added little improvement on reflection part of cache so it would work little faster otherwise it's same thing. Also it only works string property with both getter and setter. I don't think this one decrease number of NuGetVersions, only decrease overall memory size reserved by string properties of instances passed through this cache.
The moment this one runs we already passed Json part and have end result shouldn't see any changes from previously.
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.
I don't see how the strings are getting deduped.
I ran Get on the nuget.packagemanagement
package and the cache was empty at the end.
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.
@nkolev92 what do you mean by cache exactly? For example for below case nothing happens, because a
and b
are already pointing same object reference:
var a = "aaa";
var b = "aaa";
var res = Object.ReferenceEquals(a, b);
Console.WriteLine(res); //Expect true
But below case you would expect deduplication if you use this cache tool, because they're not same object reference.
var a = "aaa";
var b = "aa";
var c = b+ "a";
var res = Object.ReferenceEquals(a, c);
Console.WriteLine(res); //expect False
…overhead and space.
Type stringPropertyType = typeof(string); | ||
MethodInfo method = _metadataReferenceCacheType.GetTypeInfo() | ||
.DeclaredMethods.FirstOrDefault( | ||
m => | ||
m.Name == CachableTypesMap[stringPropertyType] && | ||
m.GetParameters().Select(p => p.ParameterType).SequenceEqual(new Type[] { stringPropertyType })); | ||
_stringMethodsCache.Add(_metadataReferenceCacheType, method); |
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.
This doesn't look right to me. It's looking for methods defined on the MetadataReferenceCache
class, rather than generic type T
's class. Is that a mistake?
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.
@@ -17,6 +17,8 @@ public class MetadataReferenceCache | |||
private readonly Dictionary<string, string> _stringCache = new Dictionary<string, string>(StringComparer.Ordinal); | |||
private readonly Dictionary<Type, PropertyInfo[]> _propertyCache = new Dictionary<Type, PropertyInfo[]>(); | |||
private readonly Dictionary<string, NuGetVersion> _versionCache = new Dictionary<string, NuGetVersion>(StringComparer.Ordinal); | |||
private readonly Dictionary<Type, MethodInfo> _stringMethodsCache = new Dictionary<Type, MethodInfo>(); | |||
private readonly Type _metadataReferenceCacheType = typeof(MetadataReferenceCache); |
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.
This class is MetadataReferenceCache
. It seems very strange to me why you'd need to use reflection for this class to access itself.
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.
@nkolev92 @zivkan @davidfowl
|
f7632f4
to
44779ac
Compare
Any ideas why the GC stats are not showing? I can't believe that 3MB of allocations didn't trigger any GC. What TFM did you benchmark with? Do different TFMs have different results (I know we care primarily about Visual Studio, but I'm curious if .NET Core has different perf characteristics). edit: And as Nikolche pointed out below, it would be good to benchmark small, medium, large json payload sizes to see if there's much difference. |
Consider using "fake" to benchmark. It contains lots of versions, the true extreme case :) |
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.
A few minor suggestions.
Please do the benchmark with different sizes of registration pages for completeness.
/// <param name="log"></param> | ||
/// <param name="token"></param> | ||
/// <returns>Package meta data.</returns> | ||
/// <remarks>The inlined entries are potentially going away soon</remarks> |
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.
Best way to determine it by following the references that VS shows you.
I'm not surprised that VS only only goes through the "all" codepath.
test/NuGet.Core.Tests/NuGet.Protocol.Tests/Utility/MetadataReferenceCacheTestUtility.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Protocol/Extensions/VersionRangeExtensions.cs
Outdated
Show resolved
Hide resolved
Updated the body of the issue to close NuGet/Home#9719 instead of NuGet/Home#8352 because that one is really an epic. |
First one I am targeting 3.1 Dotnet core. It looks Large = "Fake" has 27 small parts that is making it slow overall, still new implementation is faster than before. Here is same things for Dot.net 4.8, somehow this one shows result in milliseconds instead of microseconds. Also improvement here is more drastic than .net core for large load, but almost nothing on small loads. |
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 would be nice to one day refactor NuGet.Protocol
to make things easier to read, but who knows if we'll ever get around to it.
Perf improvements look fantastic. Great work! 👍
src/NuGet.Core/NuGet.Protocol/Resources/PackageMetadataResourceV3.cs
Outdated
Show resolved
Hide resolved
ea16b21
to
a2e28fd
Compare
Bug
Fixes: NuGet/Home#9719
Regression: No
Fix
Details:
Using VS nuget GUI to consolodate different versions of a package crashes the instance of VS. Occurs in 2017, 2019 and 2019 int preview.
One of main problem was PM UI creates giant Jobject and millions of Nuget related objects and ballooning memory.
I am replacing this strong type instead of JObject.
Testing/Validation
Tests Added: Yes
Reason for not adding tests:
Validation: Private unit test.