In the previous post, we shed some light on the improvements we’ve made around EditorConfig support in ReSharper, including the key obstacles we faced and what we did to overcome them.
Today, we are going to go through performance requests on YouTrack one by one.
In this series (we will update the table of contents as we progress):
- EditorConfig support (ReSharper 2018.2 EAP)
- Performance reports (and fixes) from the ReSharper issue tracker (ReSharper 2018.2 EAP)
Let’s dive right into the performance reports you have been sending us!
Performance reports (and other issues) from the ReSharper issue tracker
RSRP-417284 Non-interruptible StoredTemplesProvider.EnumerateTemplates blocks UI thread of taking a WriteLock
ReSharper uses caches heavily, to help us get some work done quickly if we have already done it at least once before. However, when we need to do something for the first time and the cache is empty, caching might take time.
In this issue, the Live Template cache was generated as soon as the user started typing for the first time. This led to a noticeable delay before the code completion popup appeared.
To fix this, we’ve taught ReSharper to warm up the Live Template cache in a background thread in advance – as soon as the user activates the code editor with a file. Now, the code completion popup appears immediately.
RSRP-470209 IProjectToModelReference resolve result is not cached
As we analyzed several snapshots, we found out that different features/algorithms contributed to processing a reference more than once, even in the same project scope. It looked as though the processing results were not shared between them, despite the fact that some data had already been cached at the lower processing levels.
We’ve added an extra high-level cache to store the entire processing results and share them, so as not to process everything once multiple times. This has led to a savings of about 400 ms on opening some particular files.
RSRP-470136 ‘Add | New From Template’ menu opens with a visible lag
Because of a bug in reading data from cache with file templates applicable to the specific project folder, every time the cache was invalidated and recollected once again. This made it look as though there was no cache at all. We’ve fixed that, and now read/write cache operations work the way they should.
RSRP-469604 [Performance Report] Snapshot for issue #1235046
Actually, ReSharper tried to read project properties for C++ projects on solution loading, even if ReSharper C++ was not installed, so there was no need to collect this data. Starting with version 2018.2, ReSharper no longer reads properties for C++ projects. This has cut down solution loading times for solutions with C++ projects.
RSRP-470230 Web project open may be slow because of WebAssemblyReferenceFactory.ForceAddAssemblyCookie
To execute some operations in ReSharper, a task has to acquire a write lock to stop any other activities. In that particular case, reading an assembly’s parameters (
mvid, etc.) acquired a write lock on a UI thread, but sometimes the reading could take a lot of time because of
I/O operations. As a result, other UI activities would just sit and wait. This produced a noticeable delay on project opening.
To work around this, we’ve applied a slightly different technique. If something needs to read an assembly’s parameters, we read the assembly in a background thread, and collect the requested info. When it’s ready, we return to the UI thread, acquire a write lock, and provide this data to the requester. No noticeable delay so far.
Previously, we used a custom Concurrent Programming pattern with blocking a write lock request, which prevented other activities until the lock was released. When there was a background thread, busy with some read activity, and the IDE was trying to acquire the write lock, the read activity would be stopped and the write lock would be acquired. As a result, the IDE lost all the read activity’s results and needed to restart it after the write lock was released.
We’ve now switched to another pattern for tasks which are not urgent and can wait for some time. If a non-urgent task needs to perform some activity under a write lock but some background thread has already acquired a read lock, then the task will be postponed without interrupting the background activity.
ReSharper has lots of checks to provide you with valuable suggestions and warnings to improve your code. To run any checks, code analysis needs to acquire a read lock. However, since some checks are complicated and need lots of time to complete, this might produce some delay in executing other actions. In this particular case, typing was the victim. Typing requires acquiring a write lock but since the long checks already have a read lock, typing needs to wait for the analysis to be finished. This added a noticeable delay in appearing already typed letters.
To fix this, we’ve implemented CheckForInterrupt pattern for these long-executed inspections. Right now, all of them frequently check if any activity wants to be executed at the same time and if it is, then analysis gets interrupted.
RSRP-468327 [Performance Report] Very slow Intellisense compared to Visual Studio.
In this case the project had lots of extension methods with a type parameter, so collecting all of them for the code completion popup was slow. We’ve reworked the part of code completion that gathers them and managed to speed up code completion appearing for this case.
RSRP-469897 Slow completion of types in Xamarin
Each time you type a dot after an object, ReSharper collects lots of data to present in the code completion popup. Most of the methods that gather this data use caches to store the results for further usages, so that the same work doesn’t have to be performed again. In this case, the method MemberOwner.GetMemberPresenceFlag determined whether a type of the object has, e.g., a constructor or an operator as a type member. It turned out to be a pretty simple fix: we’ve just added a new cache to store the result of first method’s execution.
RSRP-470280 [Performance Report] Slow completion for exception on throw
RSRP-451677 [Performance Report] “Find Code Dependent on Module” is very slow (estimated time > 30 minutes) on a C# project
RSRP-446534 “Find code dependent on module” freezes main VS window
Several ReSharper features, such as code completion, use the Find Inheritors search for compiled elements as a stage while gathering data to present. We analyzed the snapshots to find that this took a lot of time. After some investigation, we figured out that Equals and GetHashCode methods in our comparer were too complicated on adding a new element to HashSet and did more work than needed for this particular case. As a solution, we’ve made Equals and GetHashCode less complex, and now the Find Inheritors search delivers results faster than before.
RSRP-469666 [Performance Report] Performance issues opening large solution + opening CONFIG files + Search Everywhere
As you may know, in the 2018.1 release Search Everywhere started showing additional matches found by Go to Text. In this issue report, when you searched for a two-symbol-long string, you would see an enormous number of Go to Text matches. This wasn’t a problem for the Search Everywhere popup, but if you called Show in Find Results for the results, gathering text search matches and presenting them as a tree took a long time.
To fix this, we’ve stopped forwarding text search matches to the Show in Find Results window in case Go to Text is not visible to a user in the Search Everywhere popup.
RSRP-458584 Remove redundant cast in file takes ages to complete
RSRP-446660 Performance Problems on Fix in Scope
RSRP-463587 Remove redundant argument(s) value very slow when hundreds of files are affected
RSRP-469125 Use type keyword in solution: didn’t finish in 12 hours
RSRP-462828 Quick fix doesn’t work for a large project
There are several performance requests about the same issue: Fix in Scope (when you might select the scope and fix all occurrences of a specific inspection at once) is incredibly slow and can’t finish in acceptable time.
For the ReSharper 2017.3 release, we completed a major refactoring of our codebase under the hood. As a result, quick-fixes are now available in the code editor – even if some of the analyses is incomplete, and even if code analysis is disabled for a file. Also, we switched the engine behind the Fix in scope quick-fixes to the new one, which is faster and more powerful.
Most recently, in the 2018.2 release cycle, some quick-fixes have been moved to the new infrastructure, and we’ve reworked another area to decrease the number of running analyses while executing Fix in scope. All of these improvements have significantly sped up its execution.
RSRP-467524 Formatter is called from daemon, taking 4% of time (a private ticket)
While running some inspections on the code, ReSharper generates a lot of temp code to verify its speculations on what might be done for the current piece of code (e.g., to suggest removing some redundancies). This is to make sure it does not change the existing logic or trigger a compiler error.
Since the temp code is generated only for checks, there is no need to run the code formatter for that code. We’ve stopped invoking the formatter on temporary code and managed to slightly boost its performance.
Whew, that’s all about the specific performance fixes we’ve fixed in ReSharper 2018.2 EAP! We are going to fix a few more before releasing the 2018.2 RTM, like speeding up solution loading a bit. Expect more information about this in next blog post after the 2018.2 release and keep an eye on our blog!
Make sure to also check the Performance guide for Visual Studio (ReSharper 2017.3+) and our KB article Speeding up ReSharper (and Visual Studio) if you are experiencing performance issues. If you have a specific performance issue you can reproduce, we would greatly appreciate it if you could collect a performance snapshot and send it over.