C#通用方法的奇怪内联行为-可能存在错误

C#通用方法的奇怪内联行为-可能存在错误,c#,performance,jit,inlining,system.numerics,C#,Performance,Jit,Inlining,System.numerics,出于某种奇怪的原因,除非另一个方法包含循环,否则此泛型方法不会内联到另一个方法中。什么能解释这种奇怪的行为?对于非泛型方法,内联在两种情况下都会发生,有循环和没有循环 代码: using System; using System.Runtime.CompilerServices; using SharpLab.Runtime; [JitGeneric(typeof(int))] public static class GenericOps<T> where T : unmanage

出于某种奇怪的原因,除非另一个方法包含循环,否则此泛型方法不会内联到另一个方法中。什么能解释这种奇怪的行为?对于非泛型方法,内联在两种情况下都会发生,有循环和没有循环

代码:

using System;
using System.Runtime.CompilerServices;
using SharpLab.Runtime;

[JitGeneric(typeof(int))]
public static class GenericOps<T> where T : unmanaged
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool Less(T left, T right)
    {
        if (typeof(T) == typeof(byte)) return (byte)(object)left < (byte)(object)right;
        if (typeof(T) == typeof(sbyte)) return (sbyte)(object)left < (sbyte)(object)right;
        if (typeof(T) == typeof(ushort)) return (ushort)(object)left < (ushort)(object)right;
        if (typeof(T) == typeof(short)) return (short)(object)left < (short)(object)right;
        if (typeof(T) == typeof(uint)) return (uint)(object)left < (uint)(object)right;
        if (typeof(T) == typeof(int)) return (int)(object)left < (int)(object)right;
        if (typeof(T) == typeof(ulong)) return (ulong)(object)left < (ulong)(object)right;
        if (typeof(T) == typeof(long)) return (long)(object)left < (long)(object)right;
        if (typeof(T) == typeof(float)) return (float)(object)left < (float)(object)right;
        if (typeof(T) == typeof(double)) return (double)(object)left < (double)(object)right;
        return default;
    }
}

[JitGeneric(typeof(int))]
public static class C<T> where T : unmanaged
{      
    public static bool M1(T a, T b)
    {
        return GenericOps<T>.Less(a, b);      
    }
        
    public static bool M2(T a, T b)
    {
        for(int i = 0; i<0; i++) {}
            
        return GenericOps<T>.Less(a, b);    
    }        
}
使用系统;
使用System.Runtime.CompilerServices;
使用SharpLab.Runtime;
[JITGeneral(typeof(int))]
公共静态类GenericOps,其中T:非托管
{
[MethodImpl(MethodImplOptions.AggressiveInline)]
公共静态bool Less(左T,右T)
{
if(typeof(T)==typeof(byte))返回(byte)(object)left<(byte)(object)right;
if(typeof(T)==typeof(sbyte))返回(sbyte)(对象)左<(sbyte)(对象)右;
如果(typeof(T)=typeof(ushort))返回(ushort)(对象)左<(ushort)(对象)右;
如果(typeof(T)==typeof(short))返回(short)(object)left<(short)(object)right;
如果(typeof(T)==typeof(uint))返回(uint)(对象)左<(uint)(对象)右;
如果(typeof(T)==typeof(int))返回(int)(对象)左<(int)(对象)右;
如果(typeof(T)=typeof(ulong))返回(ulong)(对象)左<(ulong)(对象)右;
如果(typeof(T)=typeof(long))返回(long)(object)left<(long)(object)right;
如果(typeof(T)==typeof(float))返回(float)(object)left<(float)(object)right;
如果(typeof(T)==typeof(double))返回(double)(对象)左<(double)(对象)右;
返回默认值;
}
}
[JITGeneral(typeof(int))]
公共静态类C,其中T:非托管
{      
公共静态布尔M1(TA,TB)
{
返回GenericOps.Less(a,b);
}
公共静态布尔M2(T a,T b)
{

对于(int i=0;i,考虑到这在
.NET 5
中已修复,我将其称为bug。在中通过以下
.NET
版本验证:

  • x64(.NET 5)内联
  • x86上的核心CLR v5.0.321.7212-内联
  • x86/amd64上的桌面CLR v4.8.4261.00-未内联
  • x86上的核心CLR v4.700.20.20201-未内联
  • x86上的核心CLR v4.700.19.46205-未内联
因此,要回答您的问题:

  • 是的,它可能是一个bug
  • 您不能保证内联。尤其是对于
    类型。规则/启发式可能非常复杂
  • 答案的线索显而易见。Microsoft非常了解JIT,因此如果像
    Vector
    这样的高性能类出现内联问题,那就有点令人惊讶了

  • 请解释此问题与的本质区别。我所问的问题非常相似,但这里的示例更清楚,代码也不同,以防止误解。请更清楚地解释情况。您可以使用
    inlinediagnoser
    ()检查方法未内联的原因。
    // All the type checks are omitted since the type is known during compile time
    // This generated JIT equals to a direct int < int JIT.
    GenericOps`1[[System.Int32, System.Private.CoreLib]].Less(Int32, Int32)
        L0000: cmp ecx, edx
        L0002: setl al
        L0005: movzx eax, al
        L0008: ret
    
    // No Inlining
    C`1[[System.Int32, System.Private.CoreLib]].M1(Int32, Int32)
        L0000: mov rax, GenericOps`1[[System.Int32, System.Private.CoreLib]].Less(Int32, Int32)
        L000a: jmp rax
    
    // Direct Inline
    C`1[[System.Int32, System.Private.CoreLib]].M2(Int32, Int32) // Direct Inline
        L0000: cmp ecx, edx
        L0002: setl al
        L0005: movzx eax, al
        L0008: ret