martinothamar / Mediator

A high performance implementation of Mediator pattern in .NET using source generators.
MIT License
2.16k stars 71 forks source link

feat: add incremental caching #113

Closed TimothyMakkison closed 9 months ago

TimothyMakkison commented 1 year ago

Modify CompilationAnalyzer to return an equatable model, skipping the final build and emit stage, improving performance and making the IDE more responsive.

Note that I had to add Mediator.SourceGenerator.Roslyn41 to use WithTrackingName for the tests. This is pretty fragile as changes to Mediator.SourceGenerator.Roslyn40 will not be detected by the tests. This could be solved with compile constant preproccessor sections, although I'm not confident I can add this.

martinothamar commented 1 year ago

Awesome work! In my experimental branch I think I removed the Roslyn38 project, was trying to simplify. Do you know what version of the SDK is needed for 4.1?

TimothyMakkison commented 11 months ago

Thanks for the review, I'll resolve the issues when I have more time over the weekend 👍

I must admit I don't understand all these Roslyn APIs related to incremental compilation

TLDR, each step in a incremental generator (Select, Where, SelectMany), caches the input and output of the previous runs step. By using a custom comparer or object implementing IEquality we can use this behaviour to skip a section of the pipeline if the input object is the same as the last run.

In roslyn equivalent syntax (ie SyntaxNode, InvocationExpressionSyntax) is the same between runs whereas each compilation object will always be different between runs (ITypeSymbol, ISymbol, Compilation) and should not be cached for both performance and logic reasons.

In our case I created a parse step that produces a cacheable object, this lets us skip the output stage.

martinothamar commented 11 months ago

Thanks! Makes sense

TimothyMakkison commented 11 months ago

Happy new years and thanks for the review, I think I've made all the relevant changes 👍

Edit: just ran the benchmarks and got drastically different results to yours. Moving TryParseConfiguration doesn't cause this as I get the same results when they are moved back. Method Mean Error StdDev Median Gen 0 Gen 1 Allocated
Cached 5.296 ms 0.0548 ms 0.0512 ms 5.299 ms 507.8125 156.2500 5 MB
Compile 7.333 ms 0.3460 ms 0.9982 ms 6.686 ms 593.7500 203.1250 6 MB
LargeCached 70.923 ms 0.4671 ms 0.4141 ms 70.743 ms 3875.0000 1500.0000 36 MB
LargeCompile 134.006 ms 0.8707 ms 0.8144 ms 134.052 ms 7500.0000 2500.0000 83 MB