Re: Конвертация enum в int в generic методах
От: rameel https://github.com/rsdn/CodeJam
Дата: 24.04.22 14:56
Оценка: 151 (7)
Здравствуйте, Silver_S, Вы писали:

S_S>Хочется для enum реализовать простенькие функции для битовых операций, но это работает в десятки раз медленнее, чем нужно.

S_S>Нету никаких способов чтобы это ускорить?

Если используешь .NET 5.0 и выше, с включенным Tieried JIT, то есть способ сделать это быстро и эффективно

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static unsafe bool IsAnyFlagMatch<T>(this T value, T flag) where T : unmanaged, Enum
    {
        if (TypeHelper<T>.TypeCode == TypeCode.Byte)
        {
            var v = (int)Unsafe.As<T, byte>(ref value);
            var f = (int)Unsafe.As<T, byte>(ref flag);
            return (v & f) != 0 ? true : false;
        }

        if (TypeHelper<T>.TypeCode == TypeCode.SByte)
        {
            var v = (int)Unsafe.As<T, sbyte>(ref value);
            var f = (int)Unsafe.As<T, sbyte>(ref flag);
            return (v & f) != 0 ? true : false;
        }

        if (TypeHelper<T>.TypeCode == TypeCode.Int16)
        {
            var v = (int)Unsafe.As<T, short>(ref value);
            var f = (int)Unsafe.As<T, short>(ref flag);
            return (v & f) != 0 ? true : false;
        }

        if (TypeHelper<T>.TypeCode == TypeCode.UInt16)
        {
            var v = (int)Unsafe.As<T, ushort>(ref value);
            var f = (int)Unsafe.As<T, ushort>(ref flag);
            return (v & f) != 0 ? true : false;
        }

        if (sizeof(T) == 4)
        {
            var v = Unsafe.As<T, int>(ref value);
            var f = Unsafe.As<T, int>(ref flag);
            return (v & f) != 0 ? true : false;
        }

        if (sizeof(T) == 8)
        {
            var f = Unsafe.As<T, long>(ref flag);
            return (Unsafe.As<T, long>(ref value) & f) != 0 ? true : false;
        }

        return false;
    }


Где TypeHelper<T>.TypeCode это:

public static class TypeHelper<T>
{
    public static readonly TypeCode TypeCode = Type.GetTypeCode(GetUnderlyingType());

    private static Type GetUnderlyingType()
    {
        var type = typeof(T);
        if (IsNullable(type))
            type = type.GetGenericArguments()[0];

        return type.IsEnum
            ? type.GetEnumUnderlyingType()
            : type;
    }

    private static bool IsNullable(Type type) =>
        type.IsGenericType && !type.IsGenericTypeDefinition && type.GetGenericTypeDefinition() == typeof(Nullable<>);
}


Можно спросить, почему sizeof(T) ну или Unsafe.SizeOf<T> было недостаточно, то на типах, размерностью менее int имеет значение знаковость типа, и если не угадать, то JIT для манипуляции с ними задействует стек и менее эффективный код.

Tired compilation нужен, чтобы вот это TypeHelper<T>.TypeCode устранялось и счиаталось за константу.

PS. Ну и не забываем, что Enum.HasFlag с .NET6 теперь считается интринсиком и разворачивается джитом в эффективный код, если на момент компиляции известен тип
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.