Faster C++ solution load with VS 2017

The Visual C++ product has had projects ever since its inception.  Visual C++ had its own IDE up through Visual Studio 6.  Starting in Visual Studio .NET, C++ moved to a new IDE shared by Visual Basic, C#, C++, and other tools.  This new IDE used COM everywhere and items in the Solution Explorer were based on IVsHierarchy.  Much of the existing project system was kept intact but it was exposed through a new set of COM objects that implemented IVsHierarchy and other interfaces.

In Visual Studio 2010, the underlying project/build system was changedfrom VCBuild to MSBuild.  This is also when the file extension changed from .vcproj to. vcxproj.  At this point, the existing set of (relatively new) COM objects was kept, but forwarded into a new set of managed “shim” objects that then called into the new project system.  This new project system eventually became known as the Visual Studio Project System (VSPS).

As years went by, developers created ever larger solutions and projects.  This created a problem as Visual Studio took longer and longer to load these ever-increasing solutions.  Each project would get parsed and a large graph of objects would be created in memory and kept there.  At least one object per node in the hierarchy would get created.  This approach takes a lot of time and uses a lot of memory.  Visual Studio tried to keep up and has implemented various strategies to support large projects.  For instance, you can explicitly unload projects to prevent them from getting loaded and ASL (Asynchronous Solution Load) was introduced in Visual Studio 2013.  ASL tries to load most projects on a background thread and make the UI immediately responsive for other tasks.

While ASL works to a large extent, we have heard from many developers that they prefer to just wait for the whole solution to finish loading and all other background work to finish before trying to use the IDE.  You know, open your solution and go get coffee.

Even after all of the projects are loaded, there is still other stuff happening that is using CPU and disk.  In the status bar, you are probably used to seeing the following:

initializingprojects

This message means we are doing what we call a “design-time build”.  The VSPS is evaluating projects and figuring out what would be built and how it would be built.  We do this to generate command-lines for every file in every project and then compare them to what we have stored in the browse database.  If they have changed, we write them into the database.  The command lines are used for Intellisense and also to resolve include files that are indexed in the browse database.

After this “initializing” phase you will see that we are checking for out-of-date files (and then updating them if needed).  Finally, the status bar shows ready.  However, if you take a look in Task Manager, you will notice that one CPU is still getting heavily used.  In the background, we are still doing work.  The first task is populating the “external dependencies” node of each project.  This node contains files that aren’t explicitly in the project but are #included directly or indirectly by some file in the project.  After this step, there is one more invisible step which checks the database for orphaned records, such as files we indexed that aren’t used anymore (directly or indirectly) by any project.  All of this happens every time you open a solution even if absolutely nothing has changed since the last time you used it.

Let’s take a look at loading the Chromium solution and see how long each of these steps is taking in Visual Studio 2015 Update 3 vs. Visual Studio 15 Preview 5.  If you follow the instructions on the Chromium website, the solution is generated by the command “gn gen –ide=vs out\Default”.  This results in ~4600 projects of which ~1000 are “Solution Folders” and the others are .vcxproj projects.

picture

The following results are on Windows 10 from my personal machine which is an Intel Core i7-4790 @ 3.6GHz and two SSDs: one as system drive and one for source code (Samsung 850 Pro).  The first set of results is in Visual Studio 2015 Update 3 and the second set is from Preview 5 of Visual Studio 15.

We realize that the “parsing/resolving includes” phase is about 25% slower than in VS2015 Update 3.  We are working on improving this as well and expect to get that resolved soon.

Why is solution load with Visual Studio 2017 so much faster?

Because of our existing layered architecture, it was relatively easy to insert a new caching layer that can answer many questions about projects and configurations without having to actually load the project into the VSPS.  This new layer uses a SQLite database to quickly retrieve information on demand.

When a solution is loaded and we are asked to load a C/C++ project, we get a list of all .vcxproj files this solution will load.  We check the database to see if we already have these projects and if any files have changed.  If we need to update the information about a set of projects, those projects are put into a queue.  That queue is handled by multiple external processes that use MSBuild to load the projects and collect information about them and write it to the database.

As we are asked to load each project, we create a set of small shim objects that can service many requests without needing to fully load a project.  We can provide all of the information that our Intellisense engine needs, as well as provide what Solution Explorer needs through information in the database.  If an API is called that needs a real project (such as modifying project settings), the underlying shim will load the project on the fly and delegate to it.

Because of this change the load time of individual projects went way down, but not as low as we desired.  Profiling revealed some pretty bad algorithms with N^2 time complexity in various places.  Our memory use also dramatically dropped after this change, but we also found some pretty bad memory use inside our own objects.  We were able to trim the size of each object that represents a file in a solution (including external dependencies) from 120 bytes to 44 bytes per instance.  It may not seem like much, but some large solutions end up with millions of these objects.  We are still working on improving the performance of project load and I expect to see some additional improvements before shipping the final version.

This feature is truly experimental, there are still some issues with fast project load we want you to be aware of.

  1. Projects that need upgrading should be upgraded first before trying to use fast project load on them as upgrade will not happen during FPL.
  2. Our story today for being able to build is not complete for this preview release. Very simple solutions with one project might build but that’s pretty much it.
  3. Projects will load through VSPS on demand such as when explicitly editing a project (e.g. adding files or changing properties).  A large
    project may take a few seconds to load.  We want to signal this to the user but we haven’t yet in all cases.
  4. Third party plugins could choose to walk the entire solution hierarchy asking for properties that causes all projects to be fully loaded in VSPS, effectively defeating any benefits of FPL.

The IDE’s Output Window will display a message whenever a project is fully loaded into the VSPS.  Please let us know if you see these messages unexpectedly.

Lightweight Solution Load

There is another experimental effort underway in Visual Studio to improve solution load called “lightweight solution load”. You can enable this feature using the following option.

fpl

This is a completely different approach and you can read about it on the Visual Studio blog here.  Generally, it will avoid loading projects at all and will only load a project when a user explicitly expands a project in Solution Explorer.  The C++ team has been focused on Fast Project Load and so our support for lightweight solution load is currently minimal.  In the RC  release of Visual Studio 2017, we expect to support FPL in conjunction with Lightweight Solution Load.  This combination should provide a great experience.

Wrap Up

As always, we welcome your feedback and we would love to learn from your experiences as you try these features out. Do let us know if you run into any issues trying out faster solution load through report a problem tool.

You can also email us your query or feedback if you choose to interact with us directly! For new feature suggestions, let us know through User Voice.