Конвертация enum в int в generic методах
От: Silver_S Ниоткуда  
Дата: 24.04.22 12:15
Оценка:
Хочется для enum реализовать простенькие функции для битовых операций, но это работает в десятки раз медленнее, чем нужно.
Нету никаких способов чтобы это ускорить?
Жаль, что в C# даже нету простеньких препроцессорных текстовых макросов.

[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static bool HasAnyFlag<T>(T en1, T en2) where T : IConvertible
{
    return (en1.ToInt32(null) & en2.ToInt32(null)) != 0;
}
//...
HasAnyFlag((int)f, (int)(SomeFlags.F1 | SomeFlags.F2)); //Быстро.
HasAnyFlag(f, SomeFlags.F1 | SomeFlags.F2); //В десятки раз медленнее!
Re: Конвертация enum в int в generic методах
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 24.04.22 13:35
Оценка:
Здравствуйте, Silver_S, Вы писали:

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

S_S>Нету никаких способов чтобы это ускорить?
S_S>Жаль, что в C# даже нету простеньких препроцессорных текстовых макросов.

Ну вообще то есть Генераторы источников

Можешь нагенерить что хочешь/
Roles обещали, но так их и нет http://rsdn.org/forum/dotnet/7749568.flat
Автор: varenikAA
Дата: 08.06.20
и солнце б утром не вставало, когда бы не было меня
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>>
Re: Конвертация enum в int в generic методах
От: Ночной Смотрящий Россия  
Дата: 24.04.22 19:04
Оценка: 119 (2)
Здравствуйте, Silver_S, Вы писали:

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

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

https://github.com/rsdn/CodeJam/blob/master/CodeJam.Main/EnumHelper.cs
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[2]: Конвертация enum в int в generic методах
От: Silver_S Ниоткуда  
Дата: 24.04.22 19:44
Оценка: 12 (1)
Здравствуйте, Serginio1, Вы писали:

S>Roles обещали, но так их и нет http://rsdn.org/forum/dotnet/7749568.flat
Автор: varenikAA
Дата: 08.06.20


Это уже почти есть, при активации preview. Если глянуть декомпиляцию для "int" там уже есть наследование от кучи интерфейсов со статическими методами, в том числе и от IBitwiseOperators<int, int, int>.
Только вот у enum нету никаких подобных интерфейсов.
Re[3]: Конвертация enum в int в generic методах
От: Ночной Смотрящий Россия  
Дата: 25.04.22 07:14
Оценка: +1
Здравствуйте, Silver_S, Вы писали:

S_S>Только вот у enum нету никаких подобных интерфейсов.


enum без каких либо последствий для перфоманса кастится в свой underlying тип.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re: Конвертация enum в int в generic методах
От: Teolog  
Дата: 25.04.22 08:20
Оценка: 131 (3)
Обход религиозных запретов с помощью черной магии
public static class HackEnumConvert<TEnumType> where TEnumType : struct, Enum
    {
        private static readonly Func<TEnumType, int> Wrapper;
        public static int ToInt(TEnumType enu) {
            return Wrapper(enu);
        }
        static HackEnumConvert() {
            var p = Expression.Parameter(typeof(TEnumType), null);
            var c = Expression.ConvertChecked(p, typeof(int));
            Wrapper = Expression.Lambda<Func<TEnumType, int>>(c, p).Compile();
        }
    }

HackEnumConvert<EMyEnum>.ToInt(value);
Отредактировано 25.04.2022 14:04 AndrewVK . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.