-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Analyzers are slow compared to 2.4 #2594
Comments
First analysis on my computer: 633 Assemblies will investigate further |
….GetRuntimePackAsync, PEFile.Name, PEFile.FullName to improve performance of assembly resolving. This improves performance of the analysis by a factor of 2.
Thanks for looking into this. I see you have a commit to help this issue. Should I attempt to download and build the latest branch, or would it be better to wait? |
You can always try the latest version and see how much it improves. Thanks! |
I did a little informal testing with the latest branch including the commit, 7.1, and 2.4: All tests below are "used by" analysis starting with a type. The timings for 7.1 and 2.4 were done by stopwatch, and may not be exact. The number of results for 2.4 were counted by hand. GAC The performance of the latest is definitely improved, but still not near the IL2.4 performance for my large project. The different number of results for some cases is concerning. My wild, uninformed speculation is that this analysis in 2.4 was O(n) in the number of assemblies (or maybe types? members?) while the latest versions are O(n^2) or worse, somehow. Is there something I can do to help find out where the time is being taken? |
So there's a bunch of things coming together here.
|
… actually have a usage of the analyzed method.
…ve LoadedAssembly.CreateLoadedAssemblyLookupAsync calls
…ve LoadedAssembly.CreateLoadedAssemblyLookupAsync calls
Before I noticed that you had made some commits on this, I had tried making some caching optimizations of my own. See https://github.com/Doug-Becker/ILSpy/tree/issue2594 for the changes. Hopefully these will show some benefit. I had made a global cache (per assembly list) in LoadedAssembly.MyAssemblyResolver for assemblies to address your issue 4. I also added some caching of the method analysis in TypeUsedByAnalyzer. Finally, looking at the ILSpy 2.4 code, it appears to me that ILSpy 2.4 managed to do parallel analysis. I've added PLINQ to TypeUsedByAnalyzer to get some speedup in this way. GAC
ILSpy latest: 6481ms, 1 result
... with LoadedAssembly.MyAssemblyResolver, caching results: 767ms, 306 results
... with LoadedAssembly.MyAssemblyResolver, caching results: 333ms, 857 results (my project with more than 6500 assemblies)
ILSpy latest: 240525ms, 13 results
ILSpy latest: 274690ms, 1252 results
ILSpy latest: 266660ms, 597 results "ILSpy latest" above is before your last set of changes. At some point I will try to measure with your changes instead. |
@Doug-Becker I have pushed some optimizations, which unfortunately are primarily targeted at Could you please try https://github.com/icsharpcode/ILSpy/tree/analyzer-opt with your large assembly list? Thanks! @dgrunwald I just took a look at the old analyzer implementation and it was indeed using PLINQ to speed up the whole process. However, think we first should focus on improving the analyzer code in single-threaded mode. Parallelization can always be added to get a "free" boost. What do you think? |
@Doug-Becker This sounds like our |
@siegfriedpammer: I've tried the https://github.com/icsharpcode/ILSpy/tree/analyzer-opt branch on my project:
An improvement, but not as much as I had hoped. |
Thank you for your feedback, I have pushed some optimizations for I am not sure if you are familiar with the Visual Studio performance profiler. If you want, you can try profiling the one analyzer run on your machine and then send the results to me. For short analysis sessions the size should only be about ~30 MB which I think is manageable. To keep file size down, make sure to start the profiler with data collection disabled and only activate it right before you expand the "Used By" analyzer tree node and stop profiling after it is done. If you could provide profiling data this would be really nice and might help us understand your use-case better. |
I added another set of optimizations. fyi |
Today we looked some more into optimizations with focus on the "Find uses of type" case for
Together these changes bring "Find uses of type" for Ideas for further improvements:
|
… actually have a usage of the analyzed method.
…ve LoadedAssembly.CreateLoadedAssemblyLookupAsync calls
First, let me apologize. I haven't been able to spend much time reacting to your changes. (And I'm sorry to say that's probably going to be true going forward as well. I apologize again.) But the last set of changes... "disappointing" doesn't cover it. And one more apology from me (my last), my project codebase has moved on since I gave the last set of results; the numbers are not entirely apples-to-apples. And for some reason, everything is a bit slower. With my optimized branch that was previously giving me results in 4-6s, I'm now seeing 12-13s. I have no idea why; the project changes should not be so dramatic. Possibly something else is happening on my computer to slow it down, but I can't guess what it could be. But with all those caveats, with the latest changes, I'm seeing:
(The different number of results are also seen on my branch, I don't believe this indicates any problem.) So these results are roughly 50x slower than my optimized branch. And I cannot use the tool with this sort of performance. I have not used the Visual Studio performance profiler before, but I did give it a try. But if there is a way to save the results so I can send to you, it seems to be well hidden. And it doesn't seem to be discoverable with a Google search. (I did find instructions for VS2017 that no longer apply to VS2022.) Maybe there are different types of profiling and I did the wrong one? If you can point me to some documentation, I might be able to send you some results. On the other hand, I did use a profiler (YourKit) to guide my optimizations. Might I suggest that it could be worth looking at them? At least it shows that it's possible to deliver the results much, much faster. Possibly there is something in my changes that is unsuitable for the actual product, but I'd hope that looking at my changes should give a sense of what could be causing over an order of magnitude of unnecessary work for analysis with many interdependent assemblies. And one last minor note, it's not only "type used by" analysis that is very slow; at least "method used by" is as well. But I think you have started to look into that as well. Thank you again for looking into this. |
Can you please send an email to [email protected]. Thanks! |
… actually have a usage of the analyzed method.
…ve LoadedAssembly.CreateLoadedAssemblyLookupAsync calls
With the latest master branch, I have seen a dramatic improvement in performance on my large project with over 6500 assemblies. I consider the analyzers useable on my project. Measurements below: Type “A” Type “B” Type “C” Comparisons are not perfect apples-to-apples since the codebase changed somewhat in between analyses, but should be comparable. While these numbers are 2-4x slower than ILSpy 2.4, I think these are acceptable performance numbers. I think this issue can be closed. |
Discussed in #2592
Originally posted by Doug-Becker December 30, 2021
I had been happily using ILSpy version 2.4 for several years and never felt a strong reason to upgrade. Recently we changed the compiler we use to build our .NET Framework 4.8 assemblies and ILSpy 2.4 throws exceptions and crashes when decompiling certain assemblies.
So I upgraded to ILSpy 7.1, and while it seems to solve the crashing problems, it is incredibly slow when running an analysis. On ILSpy 2.4, analyses (for example, a "used by" analysis on a type or method) would complete in 1-2 seconds. On IlSpy 7.1, they are taking tens of minutes (not an exaggeration). This makes ILSpy effectively useless for me.
CPU usage during this wait is low (<10%) when running an analysis. There seems to be no major disk activity. There seems to be only one thread using CPU during this time (but even if it used all cores, the performance would not be close to the ILSpy 2.4)
Possibly a factor for this is that I have 6500+ assemblies loaded (as our system is huge). But ILSpy 2.4 handled this without a problem.
I haven't found any complaints about this in any web search, so I'm a bit puzzled why I am seeing this. I would have thought that such a dramatic loss of performance would have been noted by somebody. I realize that it's been a very long time since ILSpy 2.4, and the code has changed significantly since then, so I don't have much hope that anyone could easily point to why this has happened. But I'm hoping that someone could suggest a possible mitigation or at least have a theory to what might be causing this.
The text was updated successfully, but these errors were encountered: