We introduced the new IComparisonOperators<,,> interface as part of the generic math APIs work in .NET 7, but for general-purpose APIs that need to compare and aren't constrained to numerical types, it's preferable from a design perspective to use the longer-standing IComparable<T>. Unfortunately, the codegen that's produced with IComparable<T> can end up being worse than that for IComparisonOperators<,,>, forcing a hard choice. Can we improve the JIT here?
Example:
SharpLab
#nullable disable
using System;
using System.Numerics;
using SharpLab.Runtime;
public class C
{
[JitGeneric(typeof(int))]
public static bool M1<T>(T value, T other) where T : IComparable<T>, IComparisonOperators<T, T, bool> =>
value.CompareTo(other) > 0;
[JitGeneric(typeof(int))]
public static bool M2<T>(T value, T other) where T : IComparable<T>, IComparisonOperators<T, T, bool> =>
value > other;
public static bool M3(int value, int other) =>
value.CompareTo(other) > 0;
public static bool M4(int value, int other) =>
value > other;
}
Both M1 (generic) and M3 (non-generic) that use CompareTo end up producing:
L0000: cmp ecx, edx
L0002: jl short L0013
L0004: cmp ecx, edx
L0006: jg short L001a
L0008: xor eax, eax
L000a: test eax, eax
L000c: setg al
L000f: movzx eax, al
L0012: ret
L0013: mov eax, 0xffffffff
L0018: jmp short L000a
L001a: mov eax, 1
L001f: jmp short L000a
whereas both M2 (generic) and M4 (non-generic) that use IComparisonOperators<T,T,bool> end up producing:
L0000: xor eax, eax
L0002: cmp ecx, edx
L0004: setg al
L0007: ret
Related to #78222
cc: @EgorBo, @tannergooding
We introduced the new
IComparisonOperators<,,>interface as part of the generic math APIs work in .NET 7, but for general-purpose APIs that need to compare and aren't constrained to numerical types, it's preferable from a design perspective to use the longer-standingIComparable<T>. Unfortunately, the codegen that's produced withIComparable<T>can end up being worse than that forIComparisonOperators<,,>, forcing a hard choice. Can we improve the JIT here?Example:
SharpLab
Both M1 (generic) and M3 (non-generic) that use
CompareToend up producing:whereas both M2 (generic) and M4 (non-generic) that use
IComparisonOperators<T,T,bool>end up producing:Related to #78222
cc: @EgorBo, @tannergooding