Taking ReSharper out of process – ReSharper performance series

Posted on by

In the previous post of our ReSharper performance series, we looked at Visual Studio and ReSharper complexity and history, and determined that one of the reasons for degraded performance lies in Visual Studio being a 32-bit process. In this post, we will look at what can be done to overcome that limitation.

In this series:

Let’s dive in!

Roslyn and ReSharper

We’ve looked at the 32-bit process limitations, and figured out there is only so much memory that can be consumed. So one might ask: why is JetBrains still building their own language engine with ReSharper? Why not just use Roslyn so that there is only one engine in memory instead of two? And as a bonus, ReSharper development may go quicker?

There are a number of reasons for not doing this, as explained in our ReSharper and Roslyn Q&A. A better question would be the following: is it worth replacing a well-oiled engine just to work around the limited 32-bit working set? And how long would it be before that replacement also runs against the boundaries? Surely there are better ways…

Microservices! (in the form of child processes)

How can one go beyond this hard limitation in available memory? As with everything, the answer is microservices. Visual Studio is gradually transitioning into a process with many child processes, where every one of those child processes can consume the amount of memory it needs.

This architecture has several advantages: each child process has its own thread pool and can be run on different CPU cores by the operating system. Each child process also has its own memory space and its own garbage collector.

We use the same approach in Rider, our cross-platform .NET IDE, where the IDE is just the UI and a host process, and a lot of the heavy lifting happens in the child processes that are spawned, and this works wonders for performance.

Running multiple processes does present some other challenges. How do you keep them in sync? Do all keystrokes in the editor have to be processed? When we type a class name and then remove it again, does it have to exist in both processes? The reactive distributed communication protocol we use in Rider may be the answer to these questions.

JetBrains is investigating making ReSharper a child process of Visual Studio, instead of being part of the main process.

Async loading of the ReSharper VSPackage

Next to making ReSharper run its own process, we can improve loading the bits that have to remain in the Visual Studio process. We’ll still be able to render menu’s, show refactoring dialogs and keep track of what is happening in the editor.

Visual Studio is really just the Visual Studio Shell, which loads a number of VSPackage instances that contain language features and specific tooling to make it an IDE. Pretty much every checkbox in the Visual Studio installer represents such VSPackage, and ReSharper is another VSPackage instance that can be loaded.

Visual Studio performs delay loading when a package is only needed in certain contexts. Newer versions of Visual Studio allow asynchronous loading of a VSPackage as well, moving the work to a background thread.

JetBrains is investigating loading ReSharper as an asynchronous VSPackage.

Conclusion

In this post, we have looked at one of the ways to make ReSharper performance better: taking it out of process. This brings many benefits: both Visual Studio and ReSharper run in their own process, have their own thread pool, and their own memory space. In addition to that, we are investigating async loading of ReSharper in Visual Studio.

None of these solutions will be the silver bullet by themselves, though. There are other areas that can be improved, and we will look at those in our next blog post!

If you are experiencing performance issues, make sure to check the Performance guide for Visual Studio (ReSharper 2017.3+) as well as our KB article Speeding up ReSharper (and Visual Studio). If you have a specific performance issue you can reproduce, we’d appreciate if you could collect a performance snapshot and send it over.