-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Open
Labels
area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMICLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMItenet-performancePerformance related issuePerformance related issue
Milestone
Description
Description
I'm implementing a generic Point<T> struct with below code:
record struct Point<T>(T X, T Y) : INumberBase<Point<T>> where T : INumberBase<T>
{
public static Point<T> One => new(T.One, T.One);
public static Point<T> Zero => new(T.Zero, T.Zero);
public static Point<T> AdditiveIdentity => new(T.Zero, T.Zero);
public static Point<T> MultiplicativeIdentity => new(T.One, T.One);
public static Point<T> operator checked +(Point<T> left, Point<T> right) => new (checked(left.X + right.X), checked(left.Y + right.Y));
public static Point<T> operator checked -(Point<T> value) => new(checked(-value.X), checked(-value.Y));
public static Point<T> operator checked -(Point<T> left, Point<T> right) => new(checked(left.X - right.X), checked(left.Y - right.Y));
public static Point<T> operator checked ++(Point<T> value) => checked(value + One);
public static Point<T> operator checked --(Point<T> value) => checked(value - One);
public static Point<T> operator checked *(Point<T> left, Point<T> right) => new(checked(left.X * right.X), checked(left.Y * right.Y));
public static Point<T> operator checked /(Point<T> left, Point<T> right) => new(checked(left.X / right.X), checked(left.Y / right.Y));
public string ToString(string? format, IFormatProvider? formatProvider) => $"({X.ToString(format, formatProvider)}, {Y.ToString(format, formatProvider)})";
public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider? provider) => throw new NotImplementedException();
public static Point<T> operator +(Point<T> value) => value;
public static Point<T> operator +(Point<T> left, Point<T> right) => new(left.X + right.X, left.Y + right.Y);
public static Point<T> operator -(Point<T> value) => new(-value.X, -value.Y);
public static Point<T> operator -(Point<T> left, Point<T> right) => new(left.X - right.X, left.Y - right.Y);
public static Point<T> operator ++(Point<T> value) => value + One;
public static Point<T> operator --(Point<T> value) => value - One;
public static Point<T> operator *(Point<T> left, Point<T> right) => new(left.X * right.X, left.Y * right.Y);
public static Point<T> operator /(Point<T> left, Point<T> right) => new(left.X / right.X, left.Y / right.Y);
}But when I test it with a simple snippet:
var p1 = new Point<int>(3, 4);
var p2 = new Point<int>(5, 6);
return (p1 + p2).X + (p1 + p2).Y;The JIT fails to inline operator+ thus it produces extremely bad codegen:
push rsi
sub rsp,40
mov dword ptr [rsp+30],3
mov dword ptr [rsp+34],4
mov dword ptr [rsp+28],5
mov dword ptr [rsp+2C],6
mov rcx,[rsp+30]
mov rdx,[rsp+28]
call qword ptr [7FFF8E0B7CA8]
mov [rsp+38],rax
mov esi,[rsp+38]
mov dword ptr [rsp+30],3
mov dword ptr [rsp+34],4
mov dword ptr [rsp+28],5
mov dword ptr [rsp+2C],6
mov rcx,[rsp+30]
mov rdx,[rsp+28]
call qword ptr [7FFF8E0B7CA8]
mov [rsp+38],rax
mov eax,esi
add eax,[rsp+3C]
add rsp,40
pop rsi
retOnly if I put [MethodImpl(MethodImplOptions.AggressiveInlining)] will the codegen be optimal:
mov eax,12
retAnd the performance difference:
| Method | Mean | Error | StdDev | Code Size |
|---|---|---|---|---|
| Test_NoInlining | 23.12 ns | 0.499 ns | 0.820 ns | 172 B |
| Test_Inlining | 0.0099 ns | 0.0145 ns | 0.0332 ns | 6 B |
It's 2335x slower compared to the inlined one! Note that turning on PGO does not help in this case.
Operators are usually extremely hot path so I expect them to be inlined.
IL for operator+:
IL_0000: ldarga.s 00
IL_0002: call Point <T>.get_X ()
IL_0007: ldarga.s 01
IL_0009: call Point <T>.get_X ()
IL_000E: constrained. Point <T>.T
IL_0014: call IAdditionOperators <T, T, T>.op_Addition (T, T)
IL_0019: ldarga.s 00
IL_001B: call Point <T>.get_Y ()
IL_0020: ldarga.s 01
IL_0022: call Point <T>.get_Y ()
IL_0027: constrained. Point <T>.T
IL_002D: call IAdditionOperators <T, T, T>.op_Addition (T, T)
IL_0032: newobj Point <T>..ctor
IL_0037: ret
Configuration
.NET 7: build from master
category:cq
theme:inlining
skill-level:intermediate
cost:medium
impact:medium
nietras
Metadata
Metadata
Assignees
Labels
area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMICLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMItenet-performancePerformance related issuePerformance related issue