Skip to content
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

Improved performance of calculation of PrivateAssets for transitively pinned centrally managed dependencies #4954

Conversation

marcin-krystianc
Copy link
Contributor

@marcin-krystianc marcin-krystianc commented Nov 29, 2022

Bug

Fixes: NuGet/Home#12269

Regression? Last working version: 6.0.400

Description

Existing code enumerated parent nodes in an inefficient fashion, it could yield the same node many times. Another performance issue was a linear scan to get the dependency node for the given parent node.
It is being fixed by:

  • using a HashSet to avoid traversing the same nodes multiple times
  • using a dictionary lookup instead of a linear scan.

PR Checklist

  • PR has a meaningful title

  • PR has a linked issue.

  • Described changes

  • Tests

    • Automated tests added
    • OR
    • Test exception
    • OR
    • N/A
  • Documentation

    • Documentation PR or issue filled
    • OR
    • N/A

@ghost ghost added the Community PRs created by someone not in the NuGet team label Nov 29, 2022
@ghost ghost added the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label Dec 6, 2022
@ghost
Copy link

ghost commented Dec 6, 2022

This PR has been automatically marked as stale because it has no activity for 7 days. It will be closed if no further activity occurs within another 90 days of this comment. If it is closed, you may reopen it anytime when you're ready again, as long as you don't delete the branch.

@jeffkl
Copy link
Contributor

jeffkl commented Feb 22, 2023

@marcin-krystianc what's the status of this? I'd like to get this in the next release if there's a large performance increase.

@ghost ghost removed the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label Feb 22, 2023
@marcin-krystianc
Copy link
Contributor Author

marcin-krystianc commented Feb 23, 2023

@marcin-krystianc what's the status of this? I'd like to get this in the next release if there's a large performance increase.

I needed previous PRs to be merged. Now I can resolve remaining conflicts and fine tune it. It should be ready to review soon.

…alTransitiveDependencies

# Conflicts:
#	src/NuGet.Core/NuGet.Commands/RestoreCommand/LockFileBuilder.cs
#	test/NuGet.Core.Tests/NuGet.Commands.Test/IncludeTypeTests.cs
#	test/NuGet.Core.Tests/NuGet.Commands.Test/RestoreCommandTests.cs
@marcin-krystianc marcin-krystianc marked this pull request as ready for review February 23, 2023 15:34
@marcin-krystianc marcin-krystianc requested a review from a team as a code owner February 23, 2023 15:34
@marcin-krystianc
Copy link
Contributor Author

@jeffkl It is ready for a review. I've tested the restore time using the test scenario from the NuGet/Home#12269 and now the restore time is more or less the same as it was for 6.0.400.

@@ -517,6 +517,8 @@ private IEnumerable<LibraryDependency> GetLibraryDependenciesForCentralTransitiv
{
foreach (GraphNode<RemoteResolveResult> rootNode in targetGraph.Graphs)
{
var dependencyDictionary = rootNode.Item.Data.Dependencies.ToDictionary(x => x.Name, x => x, StringComparer.OrdinalIgnoreCase);
Copy link
Contributor

Choose a reason for hiding this comment

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

Are there cases where this dictionary won't end up being used? On line 525 nodes can be skipped, so perhaps don't initialize this dictionary until its needed on line 538?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, it is possible that the dictionary will not be eventually used but it is not very likely scenario. I don't have strong opinion here..

}

suppressParent &= parentDependency.SuppressParent;
var dependency = dependencyDictionary[dependencyNode.Key.Name];
Copy link
Contributor

Choose a reason for hiding this comment

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

Use explicit type instead of var please.

{
yield return outerNode;
// It's what we are looking for
Copy link
Contributor

Choose a reason for hiding this comment

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

Please clarify this comment that the node is a parent

@marcin-krystianc marcin-krystianc force-pushed the marcink-20221123-CentralTransitiveDependencies branch from 03dae82 to 6e4e758 Compare February 24, 2023 11:01
jeffkl
jeffkl previously approved these changes Feb 24, 2023
Copy link
Contributor

@jeffkl jeffkl left a comment

Choose a reason for hiding this comment

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

Thanks for the contribution. I ran the changes under the debugger and everything seems to still work. Would it be possible to get some benchmarks just so we know how much faster this is?

Copy link
Member

@nkolev92 nkolev92 left a comment

Choose a reason for hiding this comment

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

Thanks for this fix.

A few suggestions to reduce allocations.

@marcin-krystianc
Copy link
Contributor Author

marcin-krystianc commented Feb 28, 2023

Thanks for the contribution. I ran the changes under the debugger and everything seems to still work. Would it be possible to get some benchmarks just so we know how much faster this is?

Sure, just got some measurements for https://github.com/marcin-krystianc/TestSolutions/tree/master/LargeAppWithPrivatePackagesCentralisedNGBVRemoved on my dev laptop (multiple runs, but posting the best result only):

  • 6.0.400, Time Elapsed 00:00:26.15
  • 7.0.100, Time Elapsed 00:00:57.18
  • 7.0.100 (with this fix), Time Elapsed 00:00:28.28

I see that there is still some noticeable difference when compared to 6.0.400, but I think it is justified by the extra work needed to correctly calculate effective PrivateAssets value for central transitive dependencies.

@jeffkl
Copy link
Contributor

jeffkl commented Mar 1, 2023

@marcin-krystianc something broke with our PR build pipeline but is now fixed, can you please rebase your change onto latest dev branch and force push?

@@ -525,23 +530,21 @@ private IEnumerable<LibraryDependency> GetLibraryDependenciesForCentralTransitiv
continue;
}

if (dependencyDictionary == null)
{
dependencyDictionary = rootNode.Item.Data.Dependencies.ToDictionary(x => x.Name, x => x, StringComparer.OrdinalIgnoreCase);
Copy link
Member

@nkolev92 nkolev92 Mar 2, 2023

Choose a reason for hiding this comment

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

The fact that GraphNode doesn't implement hashcode made me a bit nervous, but I think it's ok in this case, as the data itself will never be duplicated.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, not sure what was your concern really. Was it about HashSet<GraphNode<T>> for visited nodes? I think it is ok that it doesn't have proper equality comparer. We care about not visiting the same graph node twice, but it is ok when there are multiple nodes representing same project or package.

@marcin-krystianc
Copy link
Contributor Author

@marcin-krystianc something broke with our PR build pipeline but is now fixed, can you please rebase your change onto latest dev branch and force push?

Done

@jeffkl jeffkl enabled auto-merge (squash) March 2, 2023 16:52
@jeffkl jeffkl merged commit c911884 into NuGet:dev Mar 5, 2023
@marcin-krystianc marcin-krystianc deleted the marcink-20221123-CentralTransitiveDependencies branch March 6, 2023 07:17
@davkean
Copy link
Contributor

davkean commented May 31, 2023

@marcin-krystianc Nice work on this. Looking our allocation telemetry, I can see this addressed the majority of the issues on this path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Community PRs created by someone not in the NuGet team
Projects
None yet
4 participants