csharpier
csharpier copied to clipboard
perf: optimise `SyntaxNodeComparer`
It appears that SyntaxNodeComparer is run after a file is formated, optimisation here may be impactful
- Prevent null
Func<T, T, Result>allocations (still seeing some in the profiler, no idea how to sotp this) - Prevent boxing of
SyntaxTokenList,SyntaxNodeOrTokenList - Use
OrderBy.ToArrayinstead ofOrderBy.ToList - Create
AllSeparatorsButLastto prevent boxingSeparatedSyntaxList<SyntaxNode>andIEnumerablecreation
I have some other idea, but I'm focusing on the unhappy path right now.
Benchmark (comparing complexCode)
Before
| Method | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated |
|---|---|---|---|---|---|---|---|
| Default_SyntaxNodeComparer | 229.1 ms | 4.55 ms | 7.60 ms | 5000.0000 | 3000.0000 | 1000.0000 | 40.53 MB |
After
| Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated |
|---|---|---|---|---|---|---|
| Default_SyntaxNodeComparer | 187.4 ms | 3.51 ms | 7.64 ms | 3000.0000 | 1000.0000 | 31.39 MB |
Every time Compare was passed as an argument .NET would allocate Func<SyntaxToken, SyntaxToken, CompareResult>. I think this is due to a method group conversion. I prevented this by adding the property Func<SyntaxToken, SyntaxToken, CompareResult> CompareFunc { get; }
This either saves 1.4MB or 1.9MB of memory according to the benchmarks. I'm not sure whats causing this variance.
Timing is inaccurate
| Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated |
|---|---|---|---|---|---|---|
| Default_SyntaxNodeComparer | 184.4 ms | 2.68 ms | 2.97 ms | 3000.0000 | 1000.0000 | 29.5 MB |
| Method | Mean | Error | StdDev | Median | Gen0 | Gen1 | Allocated |
|---|---|---|---|---|---|---|---|
| Default_SyntaxNodeComparer | 190.2 ms | 3.68 ms | 6.14 ms | 187.3 ms | 3000.0000 | 1000.0000 | 30.05 MB |