csharpier icon indicating copy to clipboard operation
csharpier copied to clipboard

perf: optimise `SyntaxNodeComparer`

Open TimothyMakkison opened this issue 2 months ago • 1 comments

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.ToArray instead of OrderBy.ToList
  • Create AllSeparatorsButLast to prevent boxing SeparatedSyntaxList<SyntaxNode> and IEnumerable creation

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

TimothyMakkison avatar Nov 01 '25 22:11 TimothyMakkison

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

TimothyMakkison avatar Nov 03 '25 18:11 TimothyMakkison