roslyn: Roslyn Produces Abnormally High CPU Utilization When Many Files Are Open

Version Used: This is seen as of Visual Studio 2022 Preview 7

Steps to Reproduce:

  1. Open this solution
  2. Expand the DragonSpark.Application project so that the DragonSpark.Application/Security/Identity/Profile namespace is visible.
  3. Open all the visible files under the project with Right-click -> Open (this is about 60 of them):
  4. Open this file, pressing Enter and Backspace several times at the end of this line
  5. Observe CPU usage and time (it should be nominal and expected)
  6. Now repeat step 3 <-- Triggering Event
  7. Repeat step 4
  8. Repeat step 5 – CPU utilization should be abnormally utilized in both amount and time

Expected Behavior: The expected behavior is seen with steps 1-5. It is expected that steps 6-8 produce the same behavior as well, but they do not.

Actual Behavior: While steps 1-5 take several seconds to complete at ~6% CPU utilization (nominal and expected), steps 6-8 take up to nearly 30 seconds to complete, with double the amount of CPU utilization during this time.

Additional notes This is (thankfully) a very easy reproduction to produce the issue. Until I was able to reproduce it using this method, I was only encountering it in impossibly complex situations where massive refactoring occurred and many files were opened. This is still the case today and I actively encounter this problem when significant refactoring is involved. When paired with issues such this one it becomes quite the challenge to keep Visual Studio open.

Note that this is also captured in developercommunity here with provided ETL/DMPs. An issue was never made here in the Roslyn repository to properly track so doing that now.

Please do let me know if you have any further questions and I will do my best to assist you. Thank you for any consideration towards addressing this issue, and for all your excellent work over there. 🙏

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 3
  • Comments: 104 (53 by maintainers)

Most upvoted comments

@Mike-E-angelo we’ve followed up internally and moved a few issues around to re-purpose your VS feedback issue to be the public facing issue for this particular problem (CodeLens querying non-visible files).

We’ve also raised the alarm bells with the team involved and ensured the public facing issue can be used to communicate with the team! The fix for this most likely wont make it into 17.3-P1 but we’re pushing for 17.3-P2

For your review next week, @CyrusNajmabadi: I have recorded the entire process on a brand new VM install and have posted it with chapters (found in the video description) to fast forward to the interesting parts:

https://www.youtube.com/watch?v=N4onyXKlWRo

You can see that:

  1. The first opening of documents takes only 27 seconds
  2. The first area/stage takes only 5 seconds to process the keystrokes
  3. The second opening of documents then takes 52 seconds (~2x of point 1)
  4. The second area/stage then takes 23 seconds to process the keystrokes (~4x of point 2)

Note that this was performed with a new VM install with only the Web workload to further eliminate any possible external causes. No settings, extensions, or user sign-in were applied or installed. Everything is standard and a default installation from the following location: https://visualstudio.microsoft.com/vs/community/

It took about 20 minutes to do this.

Terrific. Closing out. Thanks @Mike-E-angelo

But you are saying that this is not the case, correct? So this is not the repository for this issue then, correct?

It’s some of column A and some of column B. for example, this issue helped identify that we had a problem with Inheritance margin. We think that we’ve fixed that in 17.3. However, it also covers things outside of roslyn.

I don’t want to close this out until both the stuff we’ve fixed, and the stuff in sister teams has made it out so we can validate if the end experience is now ok. As mentioned before, OOP is enormously complex. Thinks about it as a server where dozens of microservices run, and where a single errant service can cause a bad experience. So we want to make sure that we’re actually at a good state before closing out.

I would feel so much better here if I had an issue to view/track/subscribe.

All the issues are tracked with VS’ internal bug tracker. These projects are not public so there are no public links to them.

Which teams?

VS - CodeLens 😃 We tend to call this also Platform/Editor.

Part (if not all) of my confusion here is that I https://github.com/dotnet/roslyn/issues/57525#issuecomment-967270667 for this issue, and https://github.com/dotnet/roslyn/issues/57525#issuecomment-967520949. It is now sounding that indeed this is not a Roslyn issue and belongs in another repository (and/or closed altogether here).

It’s the correct repository if roslyn is consuming excessive CPU based on reasonable requests of us.

For analogy, say that List<string> used 1GB of ram itself when someone added the string "" to it. That would be an issue with List<T>. However, if someone added 1 billion strings to List<T> then it’s not an issue with List<T> if its internal array now has to hold those billion strings, it’s an issue with teh caller who is adding all those strings. Does that make sense? 😃

I can probably try that next week. 😃

Looking now.

I didn’t have those issues with 2019, but can’t go back because 2019 don’t support .NET 6.

Note: there are known issues with .net 6 in that it adds source generators to every project. This deeply exacerbates some perf issues as we are unable to share as much as before due to our system not knowing what the effect will be of all the source generators. Projects that use .net 6 are not comparable currently wrt performance with projects that don’t in IDE scenarios. This continues to be something we work on, but (as per above) it’s non-trivial and will likely take a lot of time as we continue to assess how to best have a system that can run arbitrary code that affects semantics on every keystroke.

Can confirm, i have the same bug with VS 2022 Enterprise.

@sharwell we could. However while that would get us better true-positive matches. It wouldn’t help with the false-positive case. Effectively (and need to discuss with @jcouv to make sure of this) practically any downstream new(...) could be ina context where it is referring to the type in question. e.g. Foo(new(...)). We’d have to still check this to see if it possibly was a reference to the constructor in question.

The only ways around this are:

  1. store some more syntactic information to hope to get less false positives. for example, storing the argument-count at the use site. this will only be beneficial though if the file the new(...) is in doesn’t have other new(...) calls that run the gamut of the common arg counts (e.g. 0-4 args).
  2. do an initial pass first to see where the containing type is used e.g. Bar in void Foo(Bar bar), then use that information and syntax to then try to filter down to documents that have both a usage of Foo and new(...) in it. However, this sort of ‘combined knowledge’ approach is something we have no precedence for, and it would require us to enumerate all possible contextual locations and ensure that all of them have something else we can definitively search for. I actually think this is promising, but it’s def a large departure from how Find-Refs works today.

@jcouv could you actually enumerate all the scenarios in teh compiler hwere new(...) is allowed (e.g. where hte compiler can infer the type for it?). If you can, i can start assessing if all those scenarios would have a secondary indicator that we can use to drive this.

Along those lines, can you please verify that you are using this same solution to attempt to reproduce this issue on your side or are you using a different one altogether?

I’m using yours, following the instructions you gave in the OP about which files to open and what to repeat 😃

This is the correct repository for the parts of codelens related to roslyn (like ‘reference count’ which is the part you’re hitting). So this issue should stay here. Other parts of codelens (like the source control information) would live outside of here.

Thanks for the additional info. I’ll try repro’ing this next week with codelens on.

Following the above (OP) instructions on:

image

produces no strange behavior for me.

I’ll look at your traces again today @Mike-E-angelo however, they an showed work being done in find refs. So something is calling that. The likely subjects are code lens and inheritance margin. But it’s possible it’s some other feature or extension (as find refs is a public api we expose)

Based on those traces. It is likely InheritanceMargin or CodeLens. If you have those two running, can you disable and see if it clears things up for you?