首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

探索一下 Enum 优化

编程知识
2024年08月31日 12:45

探索一下 Enum 优化

SV.Enums主要是探索如何让 enum 更高效

其中涉及的优化手段并非完全自创

很多内容参考于以下项目

主要优化手段

其实主要全是 空间换时间,大量缓存

封装入口方法以及 source-generators 生成

不过本项目尝试了封装入口方法、ModuleInitializersource-generators 来避免对使用影响(其实更主要是尝试如何避免使用interceptors

    public static class Enums<T> where T : struct, Enum
    {
        public static bool IsFlags => CheckInfo().IsFlags;
        public static bool IsEmpty => CheckInfo().IsEmpty;

        internal static IEnumInfo<T> Info;

        [MethodImpl(Enums.Optimization)]
        internal static IEnumInfo<T> CheckInfo()
        {
            if (Info == null)
            {
                Info = new EnumInfo<T>();
            }
            return Info;
        }

        public static T Parse(string name, bool ignoreCase)
        {
            if (CheckInfo().TryParse(name, ignoreCase, out var result))
                return result;
            throw new ArgumentException($"Specified value '{name}' is not defined.", nameof(name));
        }

这样做主要就可以利用 source-generators 生成enum 处理代码,

并通过 ModuleInitializer 在运行时启用生成的enum代码

如下为生成enum 代码示例

internal class EnumInfoAD125120120540FC9AA056E2DD394A7C : EnumBase<global::Benchmark.Fruits2>
    {
        public override bool IsDefined(string name)
        {
            return name switch
            {
                nameof(global::Benchmark.Fruits2.Apple) => true,
nameof(global::Benchmark.Fruits2.Lemon) => true,
nameof(global::Benchmark.Fruits2.Melon) => true,
nameof(global::Benchmark.Fruits2.Banana) => true,
                _ => false,
            };
        }

        public override string? GetName(global::Benchmark.Fruits2 t)
        {
            switch (t)
            {
                case global::Benchmark.Fruits2.Apple: return nameof(global::Benchmark.Fruits2.Apple);
case global::Benchmark.Fruits2.Lemon: return nameof(global::Benchmark.Fruits2.Lemon);
case global::Benchmark.Fruits2.Melon: return nameof(global::Benchmark.Fruits2.Melon);
case global::Benchmark.Fruits2.Banana: return nameof(global::Benchmark.Fruits2.Banana);
                default:
                    return null;
            }
        }

        protected override bool TryParseCase(in ReadOnlySpan<char> name, out global::Benchmark.Fruits2 result)
        {
            switch (name)
            {
                case ReadOnlySpan<char> current when current.Equals(nameof(global::Benchmark.Fruits2.Apple).AsSpan(), global::System.StringComparison.Ordinal):
                    result = global::Benchmark.Fruits2.Apple;
                    return true;
                case ReadOnlySpan<char> current when current.Equals(nameof(global::Benchmark.Fruits2.Lemon).AsSpan(), global::System.StringComparison.Ordinal):
                    result = global::Benchmark.Fruits2.Lemon;
                    return true;
                case ReadOnlySpan<char> current when current.Equals(nameof(global::Benchmark.Fruits2.Melon).AsSpan(), global::System.StringComparison.Ordinal):
                    result = global::Benchmark.Fruits2.Melon;
                    return true;
                case ReadOnlySpan<char> current when current.Equals(nameof(global::Benchmark.Fruits2.Banana).AsSpan(), global::System.StringComparison.Ordinal):
                    result = global::Benchmark.Fruits2.Banana;
                    return true;
                default:
                    result = default;
                    return false;
            }
        }

        protected override bool TryParseIgnoreCase(in ReadOnlySpan<char> name, out global::Benchmark.Fruits2 result)
        {
            switch (name)
            {
                case ReadOnlySpan<char> current when current.Equals(nameof(global::Benchmark.Fruits2.Apple).AsSpan(), global::System.StringComparison.OrdinalIgnoreCase):
                    result = global::Benchmark.Fruits2.Apple;
                    return true;
                case ReadOnlySpan<char> current when current.Equals(nameof(global::Benchmark.Fruits2.Lemon).AsSpan(), global::System.StringComparison.OrdinalIgnoreCase):
                    result = global::Benchmark.Fruits2.Lemon;
                    return true;
                case ReadOnlySpan<char> current when current.Equals(nameof(global::Benchmark.Fruits2.Melon).AsSpan(), global::System.StringComparison.OrdinalIgnoreCase):
                    result = global::Benchmark.Fruits2.Melon;
                    return true;
                case ReadOnlySpan<char> current when current.Equals(nameof(global::Benchmark.Fruits2.Banana).AsSpan(), global::System.StringComparison.OrdinalIgnoreCase):
                    result = global::Benchmark.Fruits2.Banana;
                    return true;
                default:
                    result = default;
                    return false;
            }
        }
    }

        internal static partial class EnumsF1029F0E5915401BBDD8559E2B5289B1
    {
        [ModuleInitializer]
        internal static void Init4771B8A4BD2E4761973279D81E61089C()
        {
            global::SV.Enums.SetEnumInfo<global::Benchmark.Fruits2>(new EnumInfoAD125120120540FC9AA056E2DD394A7C());
        }
    }

不过这样封装入口,存在一定性能损失

空间换时间

当然如果不使用 source-generators, 对应功能也有默认实现

部分代码如下, 大部分东西都内存缓存了

    public class EnumInfo<T> : IEnumInfo<T> where T : struct, Enum
    {
        private readonly string[] names;
        private readonly T[] values;
        private readonly (string Name, T Value)[] members;
        private readonly FastReadOnlyDictionary<string, T> membersByName;
        private readonly FastReadOnlyDictionary<T, (string Name, EnumMemberAttribute Member, FastReadOnlyDictionary<int, string> Labels)> namesByMember;
        private readonly Type underlyingType;
        private readonly TypeCode underlyingTypeCode;

        public bool IsFlags { get; private set; }
        public bool IsEmpty => values.Length == 0;

        public EnumInfo() : base()
        {
            var t = typeof(T);
            names = Enum.GetNames(t);
            members = names.Select(i => (i, (T)Enum.Parse(t, i))).ToArray();
            values = members.Select(i => i.Value).ToArray();
            membersByName = members.ToFastReadOnlyDictionary(i => i.Name, i => i.Value);
            namesByMember = membersByName.AsEnumerable().DistinctBy(i => i.Value).ToFastReadOnlyDictionary(i => i.Value, i =>
            {
                var fieldInfo = t.GetField(i.Key)!;
                return (i.Key, fieldInfo.GetCustomAttribute<EnumMemberAttribute>(), fieldInfo.GetCustomAttributes<LabelAttribute>().DistinctBy(i => i.Index).ToFastReadOnlyDictionary(x => x.Index, x => x.Value));
            });
            underlyingType = Enum.GetUnderlyingType(t);
            underlyingTypeCode = Type.GetTypeCode(underlyingType);
            IsFlags = t.IsDefined(typeof(FlagsAttribute), true);
        }

        [MethodImpl(Enums.Optimization)]
        public bool TryParseIgnoreCase(in ReadOnlySpan<char> name, out T result)
        {
            foreach (var member in members.AsSpan())
            {
                if (name.Equals(member.Name.AsSpan(), StringComparison.OrdinalIgnoreCase))
                {
                    result = member.Value;
                    return true;
                }
            }
            result = default;
            return false;
        }

enum 转换方法

同时提供一些 不用拆箱装箱的 enum 转换方法,里面移除了类型检查的逻辑,所以理论只能保证正常使用不会有问题

public static T ToEnum(int value)

public static T ToEnum(byte value)

public static T ToEnum(Int16 value)

public static T ToEnum(Int64 value)

...

性能测试

简单做下性能测试, 部分代码如下

public enum Fruits
    {
        Apple,
        Lemon,
        Melon,
        Banana,
        Lemon1,
        Melon2,
        Banana3,
        Lemon11,
        Melon21,
        Banana31,
        Lemon12,
        Melon22,
        Banana32,
        Lemon13,
        Melon23,
        Banana33,
        Lemon131,
        Melon231,
        Banana331,
        Lemon14,
        Melon24,
        Banana34,
    }

    [MemoryDiagnoser, Orderer(summaryOrderPolicy: SummaryOrderPolicy.FastestToSlowest), GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory), CategoriesColumn]
    public class EnumBenchmarks
    {
        private readonly EnumInfo<Fruits> test;

        public EnumBenchmarks()
        {
            test = new EnumInfo<Fruits>();
        }

        [Benchmark(Baseline = true), BenchmarkCategory("IgnoreCase")]
        public Fruits ParseIgnoreCase()
        {
            return Enum.Parse<Fruits>("melon", true);
        }

        [Benchmark, BenchmarkCategory("IgnoreCase")]
        public Fruits FastEnumParseIgnoreCase()
        {
            return FastEnum.Parse<Fruits>("melon", true);
        }

        [Benchmark, BenchmarkCategory("IgnoreCase")]
        public Fruits SVEnumsParseIgnoreCase()
        {
            Enums<Fruits>.TryParse("melon", true, out var v);
            return v;
        }

        [Benchmark, BenchmarkCategory("IgnoreCase")]
        public Fruits EnumInfoParseIgnoreCase()
        {
            test.TryParse("melon", true, out var v);
            return v;
        }

        [Benchmark(Baseline = true)]
        public Fruits Parse()
        {
            return Enum.Parse<Fruits>("Melon", false);
        }

        [Benchmark]
        public Fruits FastEnumParse()
        {
            return FastEnum.Parse<Fruits>("Melon", false);
        }

        [Benchmark]
        public Fruits SVEnumsParse()
        {
            Enums<Fruits>.TryParse("Melon", out var v);
            return v;
        }

        [Benchmark]
        public Fruits EnumInfoParse()
        {
            test.TryParse("Melon", false, out var v);
            return v;
        }

        ...

    [MemoryDiagnoser, Orderer(summaryOrderPolicy: SummaryOrderPolicy.FastestToSlowest), GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory), CategoriesColumn]
    public class ToEnumBenchmarks
    {
        private readonly EnumInfo<Fruits> test;

        public ToEnumBenchmarks()
        {
            test = new EnumInfo<Fruits>();
            //Enums.SetEnumInfo<Fruits>(new TestIEnumInfo());
        }

        [Benchmark(Baseline = true), BenchmarkCategory("ToEnumInt")]
        public Fruits ToEnumInt()
        {
            return (Fruits)Enum.ToObject(typeof(Fruits), 11);
        }

        [Benchmark, BenchmarkCategory("ToEnumInt")]
        public Fruits SVEnumsToEnumInt()
        {
            return Enums<Fruits>.ToEnum(11);
        }

        [Benchmark, BenchmarkCategory("ToEnumInt")]
        public Fruits ToEnumIntByte()
        {
            return (Fruits)Enum.ToObject(typeof(Fruits), (byte)11);
        }

        [Benchmark, BenchmarkCategory("ToEnumInt")]
        public Fruits SVEnumsToEnumIntByte()
        {
            return Enums<Fruits>.ToEnum((byte)11);
        }

        [Benchmark, BenchmarkCategory("ToEnumInt")]
        public Fruits ToEnumIntObject()
        {
            return (Fruits)Enum.ToObject(typeof(Fruits), (object)11);
        }

        [Benchmark, BenchmarkCategory("ToEnumInt")]
        public Fruits SVEnumsToEnumIntObject()
        {
            return Enums<Fruits>.ToEnum((object)11);
        }
    }

结果如下


BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.4651/22H2/2022Update)
Intel Core i7-10700 CPU 2.90GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK 9.0.100-preview.5.24307.3
  [Host]     : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX2


Method Mean Error StdDev Median Ratio RatioSD Gen0 Allocated Alloc Ratio
SVEnumsToEnumInt 0.9796 ns 0.0062 ns 0.0055 ns 0.9781 ns 0.02 0.00 - - 0.00
SVEnumsToEnumIntByte 1.0990 ns 0.0089 ns 0.0074 ns 1.0966 ns 0.03 0.00 - - 0.00
SVEnumsToEnumIntObject 5.1211 ns 0.0842 ns 0.0746 ns 5.1295 ns 0.12 0.00 0.0029 24 B 1.00
ToEnumIntByte 40.9720 ns 0.2100 ns 0.1861 ns 40.9065 ns 1.00 0.03 0.0029 24 B 1.00
ToEnumInt 41.1962 ns 0.8452 ns 1.4122 ns 40.4985 ns 1.00 0.05 0.0029 24 B 1.00
ToEnumIntObject 48.2590 ns 0.4380 ns 0.3882 ns 48.0802 ns 1.17 0.04 0.0057 48 B 2.00
Method Categories Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
SVEnumsParse 2.8382 ns 0.0508 ns 0.0450 ns 0.12 0.00 - - - NA
FastEnumParse 6.9671 ns 0.0437 ns 0.0388 ns 0.28 0.00 - - - NA
EnumInfoParse 7.1513 ns 0.1049 ns 0.0930 ns 0.29 0.00 - - - NA
Parse 24.5338 ns 0.0548 ns 0.0485 ns 1.00 0.00 - - - NA
FastEnumGetName GetName 0.8608 ns 0.0175 ns 0.0155 ns 0.32 0.01 - - - NA
EnumInfoGetName GetName 1.4291 ns 0.0147 ns 0.0130 ns 0.54 0.01 - - - NA
SVEnumsGetName GetName 1.6210 ns 0.0148 ns 0.0131 ns 0.61 0.01 - - - NA
GetName GetName 2.6512 ns 0.0150 ns 0.0125 ns 1.00 0.01 - - - NA
SVEnumsGetNames GetNames 0.2539 ns 0.0061 ns 0.0051 ns 0.01 0.00 - - - 0.00
FastEnumGetNames GetNames 0.6874 ns 0.0195 ns 0.0163 ns 0.03 0.00 - - - 0.00
GetNames GetNames 21.0463 ns 0.4645 ns 0.5162 ns 1.00 0.03 0.0239 0.0001 200 B 1.00
SVEnumsGetValues GetValues 0.3022 ns 0.0296 ns 0.0277 ns 0.009 0.00 - - - 0.00
FastEnumGetValues GetValues 0.6683 ns 0.0098 ns 0.0082 ns 0.021 0.00 - - - 0.00
GetValues GetValues 32.5145 ns 0.6732 ns 0.5968 ns 1.000 0.03 0.0134 - 112 B 1.00
SVEnumsParseIgnoreCase IgnoreCase 3.0465 ns 0.0680 ns 0.0727 ns 0.12 0.00 - - - NA
EnumInfoParseIgnoreCase IgnoreCase 10.1299 ns 0.1660 ns 0.1472 ns 0.42 0.01 - - - NA
FastEnumParseIgnoreCase IgnoreCase 10.3531 ns 0.0807 ns 0.0674 ns 0.42 0.00 - - - NA
ParseIgnoreCase IgnoreCase 24.3767 ns 0.1270 ns 0.1060 ns 1.00 0.01 - - - NA
SVEnumsIsDefinedName IsDefinedName 2.7188 ns 0.0111 ns 0.0098 ns 0.11 0.00 - - - NA
EnumInfoIsDefinedName IsDefinedName 6.6075 ns 0.0190 ns 0.0148 ns 0.26 0.00 - - - NA
FastEnumIsDefinedName IsDefinedName 6.7011 ns 0.0388 ns 0.0303 ns 0.26 0.00 - - - NA
IsDefinedName IsDefinedName 25.3131 ns 0.2064 ns 0.1829 ns 1.00 0.01 - - - NA

完整代码参见 https://github.com/fs7744/Enums

From:https://www.cnblogs.com/fs7744/p/18390200
本文地址: http://shuzixingkong.net/article/1611
0评论
提交 加载更多评论
其他文章 使用 nuxi build-module 命令构建 Nuxt 模块
title: 使用 nuxi build-module 命令构建 Nuxt 模块 date: 2024/8/31 updated: 2024/8/31 author: cmdragon excerpt: nuxi build-module 命令是构建 Nuxt 模块的核心工具,它将你的模块打包成适合
使用 nuxi build-module 命令构建 Nuxt 模块 使用 nuxi build-module 命令构建 Nuxt 模块
手把手在STM32F103C8T6上构建可扩展可移植的DHT11驱动
前言 如何驱动一个你陌生的传感器呢?别看我,也别在网上死马当活马医!你需要做的,首先是明确你的传感器的名称,在这里,我们想要使用的是DHT11温湿度传感器 可能需要的前置知识 简单的OLED驱动原理 简单的IIC通信知识 基础的查手册能力 相对稳固的C语言基础 不会没关系,我会详细说明的! 一种可能
手把手在STM32F103C8T6上构建可扩展可移植的DHT11驱动 手把手在STM32F103C8T6上构建可扩展可移植的DHT11驱动 手把手在STM32F103C8T6上构建可扩展可移植的DHT11驱动
SimpleTranslationAIAgent:基于C#与LLM的翻译AI Agent
基于C#与LLM通过简单对话即可实现文件到文件的翻译任务 该软件是MIT协议完全开源免费的,但是调用LLM的API可能需要费用,但是没关系,赛博菩萨硅基流动与智谱AI等都有免费的模型可调了。 这个Translation AI Agent只是一个简单的AI Agent示例应用,可能很多人都不需要它。
SimpleTranslationAIAgent:基于C#与LLM的翻译AI Agent SimpleTranslationAIAgent:基于C#与LLM的翻译AI Agent SimpleTranslationAIAgent:基于C#与LLM的翻译AI Agent
jenkins动态切换环境
一.代码层实现动态切换 1.首先在conftest.py下声明pytest_addoption钩子函数,写法如下 def pytest_addoption(parser): # 设置要接收的命令行参数 parser.addoption(&quot;--env&quot;, default=&quot
jenkins动态切换环境 jenkins动态切换环境 jenkins动态切换环境
使用 Quickwit 的搜索流功能为 ClickHouse 添加全文搜索
本指南将帮助您使用 Quickwit 的搜索流功能为知名的 OLAP 数据库 ClickHouse 添加全文搜索。Quickwit 暴露了一个 REST 端点,可以极快地(每秒最多 5000 万条)流式传输匹配搜索查询的 ID 或其他属性,ClickHouse 可以轻松地使用它们进行连接查询。 我们
使用 Quickwit 的搜索流功能为 ClickHouse 添加全文搜索
使用广播星历计算卫星坐标(Python)
前言 本代码为GNSS课程设计代码,仅供参考,使用的计算方法与公式均来源于王坚主编的《卫星定位原理与应用(第二版)》。 本代码计算结果可以通过下载精密星历进行比照,误差在1-10m左右。 实现功能:读取卫星广播星历,并将其计算为WGS-84坐标系下的坐标,每颗卫星,每15分钟输出一次。 广播星历下载
设计模式之迭代器模式
迭代器模式很多人都熟悉,但是什么是迭代器,为什么要用迭代器?这个很多人就很难做出具体回答了,只是知道如果有了迭代器,那么我们就能foreach遍历了,方便循环处理。这只是对迭代器的用途,进行了回答,foreach语法是java1.5时加入的语法糖,那么在这之前呢,之前是怎么做的?要知道并不是所有容器
设计模式之迭代器模式
2024 NepCTF
NepCTF NepMagic —— CheckIn 直接玩游戏就能出 注意有一关要把隐藏的方块全找到 NepCamera 先使用tshark读取数据 结果文件中发现大量jpeg头ffd8ffe0。 猜测是多个图片,编写脚本,提取出来。 脚本: import re inputFilePath=&qu
2024 NepCTF 2024 NepCTF 2024 NepCTF