C#是如何对HasFlag进行优化的

起因

在这里在 .Net Framework使用Enum中HasFlag注意事项 有说到.Net Framework到.Net Core中是对枚举(Enum)的HasFlag进行了性能优化.通过BenchmarkDotNet进行性能基准测试发现差距还是有点大.差距主要是在.Net Framework和.Net 3.0对比,在更新.Net源码发现对HasFlag进行了重写.我们先看看HasFlag源码变更文件.
枚举Enum的HasFlag实现代码,从运行时剥离出来,移到System.Private.CoreLib中,同时支持.Net Core和Mono,并将CLR和Mono中HasFlag实现进行移除

改进代码

[Intrinsic]  //通过这个修饰,在编译时或者在JIT进行优化处理
public bool HasFlag(Enum flag)
{
    if (flag is null)
        throw new ArgumentNullException(nameof(flag));
    if (GetType() != flag.GetType() && !GetType().IsEquivalentTo(flag.GetType()))
        throw new ArgumentException(SR.Format(SR.Argument_EnumTypeDoesNotMatch, flag.GetType(), GetType()));

    ref byte pThisValue = ref this.GetRawData();
    ref byte pFlagsValue = ref flag.GetRawData();

    switch (InternalGetCorElementType()) //InternalGetCorElementType这个是运行时方法
    {
        case CorElementType.ELEMENT_TYPE_I1:
        case CorElementType.ELEMENT_TYPE_U1:
        case CorElementType.ELEMENT_TYPE_BOOLEAN:
            {
                byte flagsValue = pFlagsValue;
                return (pThisValue & flagsValue) == flagsValue; //关键实现
            }
        case CorElementType.ELEMENT_TYPE_I2:
        case CorElementType.ELEMENT_TYPE_U2:
        case CorElementType.ELEMENT_TYPE_CHAR:
            {
                ushort flagsValue = Unsafe.As<byte, ushort>(ref pFlagsValue);   //将类型转换
                return (Unsafe.As<byte, ushort>(ref pThisValue) & flagsValue) == flagsValue;  //关键实现
            }
        case CorElementType.ELEMENT_TYPE_I4:
        case CorElementType.ELEMENT_TYPE_U4:
#if TARGET_32BIT
        case CorElementType.ELEMENT_TYPE_I:
        case CorElementType.ELEMENT_TYPE_U:
#endif
        case CorElementType.ELEMENT_TYPE_R4:
            {
                uint flagsValue = Unsafe.As<byte, uint>(ref pFlagsValue);
                return (Unsafe.As<byte, uint>(ref pThisValue) & flagsValue) == flagsValue;  //关键实现
            }
        case CorElementType.ELEMENT_TYPE_I8:
        case CorElementType.ELEMENT_TYPE_U8:
#if TARGET_64BIT
        case CorElementType.ELEMENT_TYPE_I:
        case CorElementType.ELEMENT_TYPE_U:
#endif
        case CorElementType.ELEMENT_TYPE_R8:
            {
                ulong flagsValue = Unsafe.As<byte, ulong>(ref pFlagsValue);
                return (Unsafe.As<byte, ulong>(ref pThisValue) & flagsValue) == flagsValue;  //关键实现
            }
        default:
            Debug.Fail("Unknown enum underlying type");
            return false;
    }
}

接着看CorElementType枚举源码:

internal enum CorElementType : byte
    {
        Invalid = 0x0,
 
        ELEMENT_TYPE_VOID = 0x1,
        ELEMENT_TYPE_BOOLEAN = 0x2,
        ELEMENT_TYPE_CHAR = 0x3,
        ELEMENT_TYPE_I1 = 0x4, // SByte
        ELEMENT_TYPE_U1 = 0x5, // Byte
        ELEMENT_TYPE_I2 = 0x6, // Int16
        ELEMENT_TYPE_U2 = 0x7, // UInt16
        ELEMENT_TYPE_I4 = 0x8, // Int32
        ELEMENT_TYPE_U4 = 0x9, // UInt32
        ELEMENT_TYPE_I8 = 0xA, // Int64
        ELEMENT_TYPE_U8 = 0xB, // UInt64
        ELEMENT_TYPE_R4 = 0xC, // Single
        ELEMENT_TYPE_R8 = 0xD, // Double
        ELEMENT_TYPE_STRING = 0xE,
 
        // every type above PTR will be simple type
        ELEMENT_TYPE_PTR = 0xF,      // PTR <type>
        ELEMENT_TYPE_BYREF = 0x10,     // BYREF <type>
 
        // Please use ELEMENT_TYPE_VALUETYPE. ELEMENT_TYPE_VALUECLASS is deprecated.
        ELEMENT_TYPE_VALUETYPE = 0x11,     // VALUETYPE <class Token>
        ELEMENT_TYPE_CLASS = 0x12,     // CLASS <class Token>
        ELEMENT_TYPE_VAR = 0x13,     // a class type variable VAR <U1>
        ELEMENT_TYPE_ARRAY = 0x14,     // MDARRAY <type> <rank> <bcount> <bound1> ... <lbcount> <lb1> ...
        ELEMENT_TYPE_GENERICINST = 0x15,     // GENERICINST <generic type> <argCnt> <arg1> ... <argn>
        ELEMENT_TYPE_TYPEDBYREF = 0x16,     // TYPEDREF  (it takes no args) a typed reference to some other type
 
        ELEMENT_TYPE_I = 0x18,     // native integer size
        ELEMENT_TYPE_U = 0x19,     // native unsigned integer size
 
        ELEMENT_TYPE_FNPTR = 0x1B,     // FNPTR <complete sig for the function including calling convention>
        ELEMENT_TYPE_OBJECT = 0x1C,     // Shortcut for System.Object
        ELEMENT_TYPE_SZARRAY = 0x1D,     // Shortcut for single dimension zero lower bound array
        // SZARRAY <type>
        ELEMENT_TYPE_MVAR = 0x1E,     // a method type variable MVAR <U1>
 
        // This is only for binding
        ELEMENT_TYPE_CMOD_REQD = 0x1F,     // required C modifier : E_T_CMOD_REQD <mdTypeRef/mdTypeDef>
        ELEMENT_TYPE_CMOD_OPT = 0x20,     // optional C modifier : E_T_CMOD_OPT <mdTypeRef/mdTypeDef>
 
        ELEMENT_TYPE_HANDLE = 0x40,
        ELEMENT_TYPE_SENTINEL = 0x41, // sentinel for varargs
        ELEMENT_TYPE_PINNED = 0x45,
    }

因为这是在.Net 6 RC2进行重写HasFlag实现,没有编译.Net源码,没法进行性能测试,等RC2安装包出来后,在进行性能测试

秋风 2021-09-27