寻找性能更优秀的不可变小字典

Dictionary 是一个很常用的键值对管理数据结构。但是在性能要求严苛的情况下,字典的查找速度并不高。所以,我们需要更快的方案。

需求说明

这里,我们需要一个 PropertyInfo 和委托对应的映射关系,这样我们就可以存储《寻找性能更优秀的动态 Getter 和 Setter 方案》提到的委托。

因此,这个字典有这些特点:

  1. 这个字典一旦创建就不需要修改。

  2. 字典项目并不多,因为通常一个 class 不会有太多属性。

方案说明

方案 1,Switch 表达式法。使用表达式生成一个包含 switch case 语句的委托。

方案 2,数组跳表。我们知道,switch case 之所以比连续的 if else 要快的原因是因为其生成的 IL 中包含一个跳表算法。因此,如果我们有办法使用连续数字作为下标,以及一个数组。就可以在 C#中自己实现跳表。

知识要点

  1. 使用表达式创建委托

  2. PropertyInfo 有一个 int MetadataToken 属性,根据目前的观察,可以知道在一个类型中的属性其 MetadataToken 似乎是连续的,因此可以取模后作为跳表的 key。

  3. 所谓的跳表,可以简单理解为,使用数组的下标来定位数组中的特定元素。

实现代码

这里,我们直接给出基准测试中使用的代码。

其中:

  • Directly 直接读,没有任何查找

  • ArrayIndex 数组跳表

  • SwitchExp 表达式生成 Switch 方案

  • Dic 传统字典方案

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using BenchmarkDotNet.Attributes;namespace Newbe.ObjectVisitor.BenchmarkTest
{[Config(typeof(Config))]public class FuncSearchTest{private Func<Yueluo, object>[] _target;private readonly Yueluo _yueluo;private readonly Func<Yueluo, string> _func;private readonly PropertyInfo _nameP;private readonly Func<PropertyInfo, Func<Yueluo, object>> _switcher;private readonly Dictionary<PropertyInfo, Func<Yueluo, object>> _dic;public FuncSearchTest(){_yueluo = Yueluo.Create();var propertyInfos = typeof(Yueluo).GetProperties().ToArray();CreateCacheArrayD(propertyInfos);_switcher = ValueGetter.CreateGetter<Yueluo, object>(propertyInfos,info => Expression.SwitchCase(Expression.Constant(CreateFunc(info)), Expression.Constant(info)));_dic = propertyInfos.ToDictionary(x => x, CreateFunc);_nameP = typeof(Yueluo).GetProperty(nameof(Yueluo.Name));_func = x => x.Name;}private void CreateCacheArrayD(IReadOnlyCollection<PropertyInfo> propertyInfos){_target = new Func<Yueluo, object>[propertyInfos.Count];foreach (var info in propertyInfos){var key = GetKey(info);var index = key % propertyInfos.Count;_target[index] = CreateFunc(info);}}private static Func<Yueluo, object> CreateFunc(PropertyInfo info){var pExp = Expression.Parameter(typeof(Yueluo), "x");var bodyExp = Expression.Property(pExp, info);var finalExp =Expression.Lambda<Func<Yueluo, object>>(Expression.Convert(bodyExp, typeof(object)), pExp);return finalExp.Compile();}private static int GetKey(MemberInfo info){var token = info.MetadataToken;return token;}[Benchmark(Baseline = true)]public string Directly() => _func(_yueluo);[Benchmark]public string ArrayIndex() => (string) _target[_nameP.MetadataToken % _target.Length](_yueluo);[Benchmark]public string SwitchExp() => (string) _switcher(_nameP)(_yueluo);[Benchmark]public string Dic() => (string) _dic[_nameP](_yueluo);}
}

基准测试


BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.572 (2004/?/20H1)
Intel Xeon CPU E5-2678 v3 2.50GHz, 1 CPU, 24 logical and 12 physical cores
.NET Core SDK=5.0.100-rc.2.20479.15[Host]       : .NET Core 2.1.23 (CoreCLR 4.6.29321.03, CoreFX 4.6.29321.01), X64 RyuJITnet461       : .NET Framework 4.8 (4.8.4250.0), X64 RyuJITnet48        : .NET Framework 4.8 (4.8.4250.0), X64 RyuJITnetcoreapp21 : .NET Core 2.1.23 (CoreCLR 4.6.29321.03, CoreFX 4.6.29321.01), X64 RyuJITnetcoreapp31 : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJITnetcoreapp5  : .NET Core 5.0.0 (CoreCLR 5.0.20.47505, CoreFX 5.0.20.47505), X64 RyuJIT

结论

  1. 字典真拉胯。

  2. Framework 真拉胯。

  3. Net 5 简直太强了。

  4. 数组跳表是非直接方案中最快的。

图表

FuncSearch

数据

MethodJobRuntimeMeanErrorStdDevRatioRatioSDRank
Directlynet461.NET 4.6.10.9347 ns0.0363 ns0.0321 ns1.000.001
ArrayIndexnet461.NET 4.6.115.0904 ns0.3820 ns0.3752 ns16.130.642
SwitchExpnet461.NET 4.6.117.1585 ns0.0624 ns0.0521 ns18.300.563
Dicnet461.NET 4.6.134.3348 ns0.2447 ns0.2169 ns36.771.184









Directlynet48.NET 4.80.6338 ns0.0233 ns0.0218 ns1.000.001
ArrayIndexnet48.NET 4.815.3098 ns0.2794 ns0.2613 ns24.170.692
SwitchExpnet48.NET 4.817.8113 ns0.0740 ns0.0656 ns28.200.983
Dicnet48.NET 4.833.7930 ns0.4538 ns0.4245 ns53.361.644









Directlynetcoreapp21.NET Core 2.11.2153 ns0.1168 ns0.1434 ns1.000.001
ArrayIndexnetcoreapp21.NET Core 2.14.6545 ns0.1044 ns0.0871 ns4.010.512
SwitchExpnetcoreapp21.NET Core 2.18.1995 ns0.2567 ns0.2747 ns6.810.903
Dicnetcoreapp21.NET Core 2.124.2669 ns0.5440 ns0.5586 ns20.072.514









Directlynetcoreapp31.NET Core 3.10.7382 ns0.1148 ns0.1074 ns1.000.001
ArrayIndexnetcoreapp31.NET Core 3.14.3580 ns0.1299 ns0.1085 ns6.100.772
SwitchExpnetcoreapp31.NET Core 3.17.5985 ns0.1310 ns0.1161 ns10.451.413
Dicnetcoreapp31.NET Core 3.122.2433 ns0.2756 ns0.2443 ns30.614.204









Directlynetcoreapp5.NET Core 5.01.3323 ns0.0527 ns0.0493 ns1.000.001
ArrayIndexnetcoreapp5.NET Core 5.05.0058 ns0.1361 ns0.1206 ns3.770.152
SwitchExpnetcoreapp5.NET Core 5.09.0576 ns0.0985 ns0.0921 ns6.810.263
Dicnetcoreapp5.NET Core 5.020.4052 ns0.2724 ns0.2275 ns15.440.594

总结

不论是数组跳表还是表达式 Switch 方案都可以解决这个问题,而且都要比使用字典要快。

但是这里有一个问题,就是目前作者还没有找到任何有关 MetadataToken 是否真的具备同 class 连续的性质。

因此建议还是使用 Switch 方案实现。

我只是知识的搬运工

- [Working with Expression Trees in C#](https://tyrrrz.me/blog/expression-trees)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/307023.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java 8 stream 性能_java8中parallelStream性能测试及结果分析

测试1BenchmarkMode(Mode.AverageTime)OutputTimeUnit(TimeUnit.NANOSECONDS)Warmup(iterations 5, time 3, timeUnit TimeUnit.SECONDS)Measurement(iterations 20, time 3, timeUnit TimeUnit.SECONDS)Fork(1)State(Scope.Benchmark)public class StreamBenchTest {Lis…

一款基于.NET Core的认证授权解决方案-葫芦藤1.0开源啦

背景18年公司准备在技术上进行转型&#xff0c;而公司技术团队是互相独立的&#xff0c;新技术的推动阻力很大。我们需要找到一个切入点。公司的项目很多&#xff0c;而各个系统之间又不互通&#xff0c;导致每套系统都有一套登录体系&#xff0c;给员工和客户都带来极大的不便…

java pc计数器_java虚拟机-程序计数器PC Register

什么是程序计数器&#xff1f;程序计数器是一块 较小 的内存空间&#xff0c;它可以看做是当前线程所执行的字节码的 行号指示器 &#xff1b;在虚拟机的概念模型里(仅仅是概念模型&#xff0c;各种虚拟机可能会通过一些更高效的方式去实现)&#xff0c;字节码解释器工作时&…

.NET架构小技巧(8)——优待异常

天有不测风云&#xff0c;人有旦夕祸福&#xff0c;程序呢——会有异常错误。C#中用try&#xff0c;catch&#xff0c;finally来捕捉处理异常&#xff0c;捕捉谁的异常呢&#xff1f;一般都是系统类库或三方类库中抛出的异常&#xff0c;那如果我自己架构程序&#xff0c;异常也…

跟我一起学.NetCore之EF Core 实战入门,一看就会

前言还记得当初学习数据库操作时&#xff0c;用ADO.NET一步一步地进行数据操作及查询&#xff0c;对于查询到的数据还得对其进行解析&#xff0c;然后封装返回给应用层&#xff1b;遇到这种重复而繁琐的工作&#xff0c;总有一些大神或团队对其进行封装&#xff0c;从而出现了很…

java 声明变量构成_Java—变量

1.1 按数据类型分类1.1.1 基本数据类型(四类八种)☛ 引用数据类型的特点存的是地址值,可以为null值☛ 基本数据类型的特点存的是具体的值,不可以是null值☛ 整型整型取值范围字节数byte(字节)-128 ~ 1271byteshort(短整型)-2byteint(默认整型)-4bytelong(长整型)12345678L8byte…

寻找性能更优秀的动态 Getter 和 Setter 方案

反射获取 PropertyInfo 可以对对象的属性值进行读取或者写入&#xff0c;但是这样性能不好。所以&#xff0c;我们需要更快的方案。方案说明 就是用表达式编译一个Action<TObj,TValue>作为 Setter&#xff0c;编译一个Func<TObj,TValue>作为 Getter。然后把这些编译…

java中什么是同步_Java中,“synchronized”(同步)是什么意思?什么时候应该用synchronized? - Break易站...

synchronized关键字的意义是什么&#xff1f;什么时候应该是方法synchronized&#xff1f;这是什么编程和逻辑&#xff1f;Java中&#xff0c;“synchronized”(同步)是什么意思&#xff1f;什么时候应该用synchronized&#xff1f;该synchronized关键字是所有不同的线程读取和…

Newbe.ObjectVisitor 0.2.10 发布,更花里胡哨

更新内容 现在&#xff0c;你可以通过上下文修改属性的值了&#xff1a;//✔️ from 0.2 // 可以修改属性 o.V().ForEach((context) > ModifyData(context)).Run();public static void ModifyData(IObjectVisitorContext<Yueluo,string> context) {context.Value con…

java获取jsp对象的属性_java-从jsp el中的对象获取布尔属性

好.我真笨.否决这个问题,嘲笑我,等等.问题出在isAdmin()委托给的方法中.该方法中存在一个空指针异常.但是,在我的辩护中,我会说我所得到的堆栈跟踪有点不清楚,并使其看起来像是EL问题,而不是代码中的简单空指针.醋,您可以保证即使没有属性,isAdmin()仍能正常工作,这确实帮助我解…

.NET 5 和 C#9 /F#5 一起到来, 向实现 .NET 统一迈出了一大步

经过一年多的开发&#xff0c;Microsoft 于北京时间 11 月 11 日&#xff08;星期三&#xff09;发布了其 .NET 5软件开发平台&#xff0c;强调平台的统一&#xff0c;并引入了 C# 9 和 F# 5 编程语言&#xff0c;新平台朝着桌面、Web、移动、云和 IoT 目标统一 .NET 开发体验的…

java 非法操作异常_java – 空结果集上的非法操作

我正试图在杂货店建立一个付款台,我的代码实际上执行了我打算做的事情,但有一件事.在我要求用户输入他们想要的项目数量之后,产品信息被收集并且工作正常,但是当它要求用户输入下一个产品的产品ID时,该行重复,我得到以下内容我的捕获中的异常&#xff1a;“空结果集上的非法操作…

.NetCore HttpClient发送请求的时候为什么自动带上了一个RequestId头部?

奇怪的问题最近在公司有个系统需要调用第三方的一个webservice。本来调用一个下很简单的事情&#xff0c;使用HttpClient构造一个SOAP请求发送出去拿到XML解析就是了。可奇怪的是我们的请求在运行一段时间后就会被服务器504给拒绝掉了。导致系统无法使用&#xff0c;用户叫苦连…

mysql dcn_Mysql varchar(max)

设计数据库时&#xff0c;不禁有这样的疑问&#xff0c;varchar()最大可以设置多大呢&#xff1f;与什么有关呢&#xff1f;字符集为什么要有字符集&#xff1f;简单讲&#xff0c;数据在网络上传输与硬盘中储存&#xff0c;都是以二进制的形式存在的。我们熟知的文字、图片以及…

ASP.NETCore小技巧:使用测试用户中间件

哈喽大家好&#xff0c;这篇文章其实很早就想写了&#xff0c;因为一直会有小伙伴问到&#xff0c;但是我却始终拿不到好的方案&#xff0c;最近在录制《eShopOnContainer微服务架构》的视频&#xff0c;碰巧就看到了微软官方的代码中也有这方面的需求&#xff0c;而且和我的需…

【招聘(深圳)】华强方特文化科技集团 .NET工程师

.NET高级开发工程师&#xff08;18-25K&#xff09;岗位职责&#xff1a;负责系统需求分析与设计&#xff1b;根据业务确定实现方案&#xff1b;对现有系统缺陷提出优化方案&#xff1b;负责系统关键功能开发及维护&#xff0c;保障系统的正常运行&#xff1b;带领指导团队开发…

11座城市,58个.NET最新岗位速览,内推直通面试官!

十一月风雪客&#xff0c;十二月乘衣归&#xff01;各个大厂秋招进行时&#xff0c;你行动了吗&#xff1f;借着这阵风&#xff0c;今天为大家提供一批.NET开发岗位内推&#xff01;58个优质的.NET开发岗位年薪过万到百万不等&#xff0c;总有一个适合你&#xff01;包含全国各…

pdo mysql_PDO MySQL

PDO MySQL如果文章有成千上万篇&#xff0c;该怎样保存&#xff1f;数据保存有多种方式&#xff0c;比如单机文件、单机数据库(SQLite)、网络数据库(MySQL、MariaDB)等等。根据项目来选择&#xff0c;做Web一般采用MySQL&#xff0c;本书也以MySQL为例。自学&#xff1a;1天。假…

C# 8: 可变结构体中的只读实例成员

在之前的文章中我们介绍了 C# 中的 只读结构体&#xff08;readonly struct&#xff09;[1] 和与其紧密相关的 in 参数[2]。今天我们来讨论一下从 C# 8 开始引入的一个特性&#xff1a;可变结构体中的只读实例成员&#xff08;当结构体可变时&#xff0c;将不会改变结构体状态的…

python中valueerror怎么改_如何修复Python ValueError:错误的元数据?

在Ubuntu中运行flexget Python脚本&#xff0c;我收到一个错误&#xff1a;$ flexget series forget "Orange is the new black" s03e01Traceback (most recent call last):File "/usr/local/bin/flexget", line 7, in from flexget import mainFile "…