Performance Improvements in NuGet
With each new release of Visual Studio comes a plethora of performance improvements when restoring NuGet packages, managing project dependencies, and browsing for the next great package to include in your solution.
When you think about NuGet, you probably think about restoring packages in your .NET projects. We have made significant performance optimizations between Visual Studio 16.4 and 16.9 to restore scenarios.
What is NuGet restore?
At its core, restore ensures that all your dependencies, declared in either the project file or packages.config, are available prior to build. This means NuGet ensures that the required packages are available in the global packages folder or the local packages cache. For PackageReference projects, this may involve resolving the dependency graph in addition.
What are the different types of NuGet restores?
Within NuGet, depending on the outcome, we talk about two types of restores, regular and no-op.
- Regular restore – Any restore that involves a package download, package installation, or dependency graph resolution.
- No-op restore – A no-operation restore, no-op for short, that determines your project state is fully up to date. In most cases, you are running a no-op restore.
Improvements to NuGet restore
Here is how we have improved NuGet no-op restore performance over the last 5 releases of Visual Studio for large solutions (50+ projects) using PackageReference. These charts use percentiles to best demonstrate how restore ranks for the large solution population in terms such as p95, p75, and p50. In other words, the higher the percentile, the larger number of projects in a solution.
These improvements are not only for large solutions! They also have an impact on small to medium solutions (1-50 projects) too.
Additionally, we improved the performance of restore for Visual Studio solution load by grouping restores more efficiently. This leads to a drastic change in solution load performance for large solutions such as OrchardCore(over 150 projects).
Finally, we have improved the performance of continuous integration scenarios such as NuGet CLI, dotnet CLI, and MSBuild where no packages have been downloaded. Over the last 5 releases of the NuGet Client tooling, restore has improved over 40% for large solutions (50+ projects).
What impacts NuGet restore performance?
As your solution scales with more projects, you may notice that your NuGet restore takes a bit longer. We will also cover other factors that can affect the performance of NuGet restore.
The larger your solution is, the longer your NuGet restore may take. This is because as your solution grows, so does your dependency graphs. Your project dependency graph is a graph that represents the dependencies of packages towards each other. Depending on your package management format, you may have a flat dependency graph(packages.config) or a transitive dependency graph (PackageReference).
The larger your package dependency graphs become, the more complex the process of correctly evaluating and resolving all your dependencies becomes.
You may not even know how the restore happens but depending on how NuGet restores your packages, it can have an impact to the overall performance. For the sake of classifying restore methods, we will refer to two primary categories:
- Implicit – Using any tool that does a NuGet restore on your behalf.
- Explicit – Using any tool that you manually invoke a NuGet restore with.
For example, when you are using tools like Visual Studio or dotnet build, you are using NuGet implicitly when loading your solutions or building your projects.
There are a few different types of package sources that NuGet uses to restore your packages with. The two main categories are file-based sources and http-based sources. Hierarchical package sources are faster than flat package sources. The performance among http sources can vary as well. NuGet’s package installation process prefers the most responsive sources. The number of sources may affect the performance because NuGet now must check more sources.
Package Management Format
By default, PackageReference is used for .NET Core projects. .NET Framework projects support PackageReference, but currently default to packages.config. It is recommended that you migrate to PackageReference if you can. There are many benefits to using PackageReference such as managing all your dependencies in one place, but most importantly, you get performance improvements due to the nature of how packages are maintained in a global packages folder for performant file operations.
MSBuild Static Graph Evaluation
You can restore your project with a concept known as a MSBuild static graph evaluation, in which NuGet will leverage the MSBuild static graph to evaluate your project graph for restore. If you have a large solution, this may significantly improve your restore performance.
Learn how you can restore with MSBuild static graph evaluation.
How do I improve NuGet restore performance?
We talked about a few separate ways you can improve NuGet restore performance. Let us summarize:
- Ensure you do not have excess package sources in your repositories.
- Reduce the number of projects per solution or filter solutions in Visual Studio.
- Use the latest tooling that supports NuGet implicitly such as Visual Studio, MSBuild, and dotnet CLI.
- Migrate to PackageReference if your project supports it.
- Try MSBuild static graph evaluation within your projects.
Managing Dependencies in Visual Studio
We have heard from you that one of your biggest pain points is the performance of managing your dependencies as your project gets larger such as switching between tabs, installing new packages, updating outdated packages, and uninstalling unneeded packages.
For the following performance improvements, we benchmarked against the OrchardCore project which contains over 150 projects, and the operations are done on a solution-level.
- 16.7 – We made it instantaneous to switch between the installed and update tabs.
- 16.8 – We improved the performance of updating packages (upgrading/downgrading) to up to 14x faster.
- 16.9 – We improved the performance of uninstalling packages to be up to 5x faster.
- 16.10 (Upcoming) – We improved the performance of installing packages to be up to 7x faster.
Try updating to a recent version of Visual Studio and let us know your feedback on these improvements!
Browsing for Packages in Visual Studio
We have also heard that browsing for packages in Visual Studio can be an unreliable experience. One major improvement in Visual Studio 16.9 that we have made is making the loading of package details almost instantaneous.
We hope that you enjoy a more responsive experience and keep providing us your feedback on how we can improve your browsing experience!
You learned a bit about the performance improvements in NuGet since Visual Studio 16.4. These of improving performance in NuGet restore, managing dependencies, and browsing for packages could not have been possible without your feedback and the NuGet team. Thank you for all your help and we are excited to bring you better package management experiences in future releases of Visual Studio. Update to the latest version of Visual Studio and let us know your thoughts in the comments below!
>16.10 (Upcoming) – We improved the performance of installing packages to be up to faster
>up to faster
Fixed now. That should’ve read `up to 7x faster`.
Nikolche, I’m so pleased to hear this is being worked on but, alas, the improvements you describe bear no relationship to the experience I have on the current VS preview (16.10.1) 🙁 It literally took me 2 working days last week to update ~70 packages in a ~130 project solution. Reasons…
– Nuget upgrade frequently gets itself confused when upgrading multiple packages and leaves project files in an inconsistent state so it’s necessary to upgrade only one or two packages at a time.
– The inability to screen by target framework means that each package has to be reviewed lest it turns out to only support .Net5
– The VS UI is unusably unresponsive. It takes many seconds to switch between or refresh the “Updates” tab and many more seconds to refresh the package details when a package is clicked on. Worse, the entire upgrade graph seems to be recalculated after every single interaction so after upgrading a single package I then need to wait 5 minutes for the the UI to repopulate 🙁
I should point out that we’re not trying to build the entirety of Windows here – the code base is a few developer-years of effort, hardly atypical.
Anyway, wishing you luck in further improvements – they are sorely needed.
I can say we habe the same issue…
Upgrading a few Nugets via VS Ui incredible slow.
I mostly use Notepad++, search via regex in the csproj and replace the versions in the package refernce wirh the new one…
Would it not be possible to implement a force update, wich will modify all csproj.
Maybe it would be good if Microsoft also starts to test with some real world customer Solutions, with more then 2-3projects and 2-3nugets
In my case we have LOB repositories with dozens of relatively small solutions. Updating Nuget packages through the UI is prohibitively slow, so I’ve written a simple donet global tool to do this repository-wide with a single iteration, and takes seconds where previously it would have required hours spent in the UI. We use PackageReference exclusively.
Even on v16.9.3, switching from Installed to Updates tab is far from instantaneous.
I don’t understand why the VS Installer is not based on NuGet and why the VS Installer, VS Marketplace (NuGet) and Android SDK Manager are merged. So you have a central point of contact with everything that belongs to VS (with exceptions, of course, where an installer is installed for the actual program such as Unity Hub for Unity) and you don’t have to install from multiple sources (VS Instsller, NuGet, Android SDK Manager, …).
this comment has been deleted.