-
Notifications
You must be signed in to change notification settings - Fork 5k
Avoid sort allocations #116109
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
base: main
Are you sure you want to change the base?
Avoid sort allocations #116109
Conversation
@EgorBot -intel using BenchmarkDotNet.Attributes;
public class Bench
{
int[] a = new int[10000];
Comparison<int> c = (int x, int y) => x - y;
[IterationSetup]
public void IterationSetup() { for (int i = 0; i < a.Length; i++) a[i] = i; }
[Benchmark]
public void Sort() => Array.Sort(a, c);
} |
Tagging subscribers to this area: @dotnet/area-system-memory |
Could you please share perf numbers that demonstrate the performance improvements? The one micro-benchmark that I have kicked off above shows ~10% regression: EgorBot/runtime-utils#370 (comment) . |
No, I haven't done any performance testing. |
@2A5F You can use the benchmarks from the performance repo (we use them for ensuring there are no regressions): You can both trace files and disassembler output: https://github.com/dotnet/performance/tree/main/src/benchmarks/micro#quick-start |
c46122b
to
1b22a69
Compare
@adamsitnik Thanks for the notice Test Code[Benchmark]
public void Span_ComparerStruct() => _arrays[_iterationIndex++].AsSpan(0, Size).Sort(new ComparableComparerStruct());
private readonly struct ComparableComparerStruct : IComparer<T>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Compare(T x, T y) => x.CompareTo(y);
} All test on
Int32
BigStruct
IntClass
IntStruct
String
|
Ideas and todo:
|
To avoid duplicate code, could you do something like this: struct DelegateWrappedComparer<T> : IComparer<T>
{
private Comparer<T> _comparer;
public int Compare(T? x, T? y) => _comparer.Compare(x, y);
} And point the "old" version of the method to the new one, with this wrapper? Hope this is clear. |
@andrewjsaid For Delegate is This test has expired/is invalid. I will retest the new code later when I have time. (benchmarks are really slow) |
My reasoning was that by using the struct wrapper, methods like the below would be specialized for that struct and the JIT would be able to inline the call to private static void SwapIfGreater<TComparer>(Span<T> keys, TComparer comparer, int i, int j)
where TComparer : IComparer<T>, allows ref struct However on further reflection I note that currently since |
|
Oops already suggested 🤦 Thanks for the link |
https://github.com/2A5F/dotnet-runtime/tree/try-uni-path Test results show that it is impossible to unify the path, therefore we should stick to only using the generic path on Int32
IntStruct
|
note, the old impl branch: https://github.com/2A5F/dotnet-runtime/tree/avoid-sort-allocations-1 |
Here are the benchmark results for the current pr branch Int32
IntStruct
|
Modified
ArraySortHelper
and addedArraySortHelper<T, TComparer>
to allow generic struct TComparer without memory allocationSpan.Sort<T, TComparer>
will not box now#39466
#39543