C#是如何对HasFlag进行优化的
起因
在这里在 .Net Framework使用Enum中HasFlag注意事项 有说到.Net Framework到.Net Core中是对枚举(Enum)的HasFlag进行了性能优化.通过BenchmarkDotNet进行性能基准测试发现差距还是有点大.差距主要是在.Net Framework和.Net 3.0对比,在更新.Net源码发现对HasFlag进行了重写.我们先看看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