Address more issue numbers in the code by stephentoub · Pull Request #1888 · dotnet/runtime (original) (raw)

I hadn't planned on this being a perf-related PR, but as a happy accident, one of the issue numbers I was fixing up referred to a fix that could be made in LINQ's sorting implementation when a new Array.Sort overload was added. We ended up rejecting the proposal for that overload due to compatibility concerns, but the issue can be addressed similarly using the new span-based sorting methods we recently added.

The core sorting routine had been doing:

Array.Sort(keys, lo, hi - lo + 1, Comparer.Create(CompareAnyKeys));

We have the CompareAnyKeys method, and we want to use it as the comparer for Array.Sort, but there's no overload that takes a Comparison<T>, so it's wrapped into a new IComparer<T>. There are a few issues with this. First, we're not only allocating a delegate for the CompareAnyKeys, we're also allocating an IComparer<T> to wrap it. Second, though, the Array.Sort<T> implementation actually works internally in terms of a Comparison<T>, so when it's given an IComparer<T>, it has to wrap its Compare method in a Comparison<T>. That means it ends up with a Comparison<T> wrapping the Compare method of an IComparer<T> that in turn invokes a Comparison<T>. Ugh. When we switch to using span:

new Span(keys, lo, hi - lo + 1).Sort(CompareAnyKeys);

we pay for just one rather than two allocations, and we use the Comparison<T> directly rather than two additional levels of indirection.

using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; using System; using System.Linq;

public class Program { static void Main(string[] args) => BenchmarkSwitcher.FromAssemblies(new[] { typeof(Program).Assembly }).Run(args);

public Program()
{
    var r = new Random(42);
    _array = new int[1_000];
    for (int i = 0; i < _array.Length; i++) _array[i] = r.Next();
}

private readonly int[] _array;

[Benchmark]
public void Sort()
{
    foreach (int i in _array.OrderBy(i => i)) { }
}

}

Method Toolchain Mean Error StdDev Ratio
Sort \master\corerun.exe 97.85 us 0.809 us 0.757 us 1.00
Sort \pr\corerun.exe 89.71 us 0.230 us 0.192 us 0.92