diff options
author | Stephen Toub <stoub@microsoft.com> | 2022-09-29 14:37:41 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-09-29 14:37:41 +0300 |
commit | 4a302db0996faf2dadcfaacd1cf54e9a97b80f86 (patch) | |
tree | 73858effd3c336e1c7a4e5ea3f35eca34fb7b5f6 /src | |
parent | ef6cb3dfc868411efc9410ebb1b4725a2b83d5fc (diff) |
Reduce Enum.GetEnumName overheads (#76162)
* Reduce Enum.GetEnumName overheads
This also helps Enum.ToString(), Enum.IsDefined, etc. Many enums are composed of sequential values starting at 0 (half of all enums in corelib, for example, meet this criteria). Today when looking up a value, we search the array of values, but if an enum is known to have such sequential values, we can instead just index into the array at the appropriate location.
* Address PR feedback
Diffstat (limited to 'src')
4 files changed, 57 insertions, 14 deletions
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs index fb1dd0a0349..18d9a6db206 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs @@ -72,11 +72,15 @@ namespace System return underlyingType; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static EnumInfo GetEnumInfo(RuntimeType enumType, bool getNames = true) { - EnumInfo? entry = enumType.GenericCache as EnumInfo; + return enumType.GenericCache is EnumInfo info && (!getNames || info.Names is not null) ? + info : + InitializeEnumInfo(enumType, getNames); - if (entry == null || (getNames && entry.Names == null)) + [MethodImpl(MethodImplOptions.NoInlining)] + static EnumInfo InitializeEnumInfo(RuntimeType enumType, bool getNames) { ulong[]? values = null; string[]? names = null; @@ -88,11 +92,10 @@ namespace System getNames ? Interop.BOOL.TRUE : Interop.BOOL.FALSE); bool hasFlagsAttribute = enumType.IsDefined(typeof(FlagsAttribute), inherit: false); - entry = new EnumInfo(hasFlagsAttribute, values!, names!); + var entry = new EnumInfo(hasFlagsAttribute, values!, names!); enumType.GenericCache = entry; + return entry; } - - return entry; } } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/EnumInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/EnumInfo.cs index 761a38833bd..87509675a96 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/EnumInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/EnumInfo.cs @@ -65,6 +65,16 @@ namespace System.Reflection Array.Copy(rawValues, ValuesAsUnderlyingType, numValues); HasFlagsAttribute = isFlags; + + ValuesAreSequentialFromZero = true; + for (int i = 0; i < values.Length; i++) + { + if (values[i] != (ulong)i) + { + ValuesAreSequentialFromZero = false; + break; + } + } } internal Type UnderlyingType { get; } @@ -72,5 +82,6 @@ namespace System.Reflection internal ulong[] Values { get; } internal Array ValuesAsUnderlyingType { get; } internal bool HasFlagsAttribute { get; } + internal bool ValuesAreSequentialFromZero { get; } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Enum.EnumInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Enum.EnumInfo.cs index 7ab48cfb11f..67f4d5860f6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Enum.EnumInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Enum.EnumInfo.cs @@ -8,6 +8,7 @@ namespace System internal sealed class EnumInfo { public readonly bool HasFlagsAttribute; + public readonly bool ValuesAreSequentialFromZero; public readonly ulong[] Values; public readonly string[] Names; @@ -17,6 +18,17 @@ namespace System HasFlagsAttribute = hasFlagsAttribute; Values = values; Names = names; + + // Store whether all of the values are sequential starting from zero. + ValuesAreSequentialFromZero = true; + for (int i = 0; i < values.Length; i++) + { + if (values[i] != (ulong)i) + { + ValuesAreSequentialFromZero = false; + break; + } + } } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Enum.cs b/src/libraries/System.Private.CoreLib/src/System/Enum.cs index 7e1ac66a010..d35f3a46c46 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Enum.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Enum.cs @@ -116,12 +116,24 @@ namespace System return GetEnumName(GetEnumInfo(enumType), ulValue); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static string? GetEnumName(EnumInfo enumInfo, ulong ulValue) { - int index = FindDefinedIndex(enumInfo.Values, ulValue); - if (index >= 0) + if (enumInfo.ValuesAreSequentialFromZero) + { + string[] names = enumInfo.Names; + if (ulValue < (ulong)names.Length) + { + return names[(uint)ulValue]; + } + } + else { - return enumInfo.Names[index]; + int index = FindDefinedIndex(enumInfo.Values, ulValue); + if (index >= 0) + { + return enumInfo.Names[index]; + } } return null; // return null so the caller knows to .ToString() the input @@ -302,7 +314,7 @@ namespace System internal static string[] InternalGetNames(RuntimeType enumType) => // Get all of the names - GetEnumInfo(enumType, true).Names; + GetEnumInfo(enumType).Names; public static Type GetUnderlyingType(Type enumType) { @@ -411,16 +423,21 @@ namespace System internal static ulong[] InternalGetValues(RuntimeType enumType) { // Get all of the values - return GetEnumInfo(enumType, false).Values; + return GetEnumInfo(enumType, getNames: false).Values; } public static bool IsDefined<TEnum>(TEnum value) where TEnum : struct, Enum { RuntimeType enumType = (RuntimeType)typeof(TEnum); - ulong[] ulValues = Enum.InternalGetValues(enumType); - ulong ulValue = Enum.ToUInt64(value); - - return FindDefinedIndex(ulValues, ulValue) >= 0; + EnumInfo info = GetEnumInfo(enumType, getNames: false); + ulong ulValue = ToUInt64(value); + ulong[] ulValues = info.Values; + + // If the enum's values are all sequentially numbered starting from 0, then we can + // just return if the requested index is in range. Otherwise, search for the value. + return + info.ValuesAreSequentialFromZero ? ulValue < (ulong)ulValues.Length : + FindDefinedIndex(ulValues, ulValue) >= 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] |