寻找性能更优秀的动态 Getter 和 Setter 方案
反射获取 PropertyInfo 可以对对象的属性值进行读取或者写入,但是这样性能不好。所以,我们需要更快的方案。
方案说明
就是用表达式编译一个 Action<TObj,TValue>
作为 Setter,编译一个 Func<TObj,TValue>
作为 Getter。
然后把这些编译好的委托放在一个泛型类的静态字段中保存起来,需要使用的时候从这里面查找就可以了。
知识要点
- 使用表达式创建委托
- 泛型类的静态字段是每个闭合类型独立的,因此用于存储和类型相关的内容非常方便
实现代码
由于代码中混合的使用 Switch 作为字典的阴招,所以代码很长,此处不再罗列,仅给出链接:
基准测试
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.572 (2004/?/20H1) |
结论
- 使用委托明显比使用 PropertyInfo 要快,这个方案可以。
- Framework 真拉胯,Net 5 简直太强了。
- 如果属性是明确的,建议把字典中取出来的委托保存在自己的上下文,这可以明显的省去查找的消耗。
图表
从左往右分别是:直接读取属性、缓存委托、不缓存委托和使用 PropertyInfo。
数据
Getter
Method | Job | Runtime | Mean | Error | StdDev | Median | Ratio | RatioSD | Rank |
---|---|---|---|---|---|---|---|---|---|
DirectlyString | net461 | .NET 4.6.1 | 0.1636 ns | 0.0822 ns | 0.1126 ns | 0.1472 ns | ? | ? | 2 |
DirectlyInt | net461 | .NET 4.6.1 | 0.0318 ns | 0.0348 ns | 0.0342 ns | 0.0217 ns | ? | ? | 1 |
ReflectString | net461 | .NET 4.6.1 | 145.8375 ns | 2.2790 ns | 2.1317 ns | 145.6522 ns | ? | ? | 7 |
ReflectInt | net461 | .NET 4.6.1 | 172.5066 ns | 1.3206 ns | 1.1028 ns | 172.6804 ns | ? | ? | 8 |
GetterString | net461 | .NET 4.6.1 | 31.4379 ns | 0.6017 ns | 0.5334 ns | 31.6316 ns | ? | ? | 4 |
GetterInt | net461 | .NET 4.6.1 | 33.0642 ns | 0.4940 ns | 0.4380 ns | 33.0557 ns | ? | ? | 5 |
GetterObject | net461 | .NET 4.6.1 | 33.9174 ns | 0.5587 ns | 0.5226 ns | 33.7326 ns | ? | ? | 6 |
GetterCached | net461 | .NET 4.6.1 | 7.5878 ns | 0.1223 ns | 0.1144 ns | 7.5765 ns | ? | ? | 3 |
DirectlyString | net48 | .NET 4.8 | 0.0181 ns | 0.0353 ns | 0.0313 ns | 0.0043 ns | ? | ? | 1 |
DirectlyInt | net48 | .NET 4.8 | 0.0050 ns | 0.0089 ns | 0.0079 ns | 0.0000 ns | ? | ? | 1 |
ReflectString | net48 | .NET 4.8 | 143.8313 ns | 2.2501 ns | 2.1047 ns | 143.5568 ns | ? | ? | 5 |
ReflectInt | net48 | .NET 4.8 | 172.1714 ns | 1.9819 ns | 1.7569 ns | 172.3142 ns | ? | ? | 6 |
GetterString | net48 | .NET 4.8 | 31.5887 ns | 0.6310 ns | 0.5902 ns | 31.5385 ns | ? | ? | 3 |
GetterInt | net48 | .NET 4.8 | 32.7140 ns | 0.3992 ns | 0.3734 ns | 32.7343 ns | ? | ? | 4 |
GetterObject | net48 | .NET 4.8 | 33.3063 ns | 0.2069 ns | 0.1834 ns | 33.3053 ns | ? | ? | 4 |
GetterCached | net48 | .NET 4.8 | 7.5540 ns | 0.2201 ns | 0.1951 ns | 7.5069 ns | ? | ? | 2 |
DirectlyString | netcoreapp21 | .NET Core 2.1 | 0.0000 ns | 0.0000 ns | 0.0000 ns | 0.0000 ns | ? | ? | 1 |
DirectlyInt | netcoreapp21 | .NET Core 2.1 | 0.0193 ns | 0.0111 ns | 0.0104 ns | 0.0177 ns | ? | ? | 2 |
ReflectString | netcoreapp21 | .NET Core 2.1 | 110.4180 ns | 2.2159 ns | 1.8503 ns | 110.8038 ns | ? | ? | 7 |
ReflectInt | netcoreapp21 | .NET Core 2.1 | 138.9612 ns | 0.9694 ns | 0.8594 ns | 138.8217 ns | ? | ? | 8 |
GetterString | netcoreapp21 | .NET Core 2.1 | 16.8958 ns | 0.2384 ns | 0.2230 ns | 16.8103 ns | ? | ? | 4 |
GetterInt | netcoreapp21 | .NET Core 2.1 | 19.4407 ns | 0.2041 ns | 0.1809 ns | 19.4539 ns | ? | ? | 6 |
GetterObject | netcoreapp21 | .NET Core 2.1 | 18.6922 ns | 0.2700 ns | 0.2255 ns | 18.6582 ns | ? | ? | 5 |
GetterCached | netcoreapp21 | .NET Core 2.1 | 0.9299 ns | 0.0457 ns | 0.0427 ns | 0.9308 ns | ? | ? | 3 |
DirectlyString | netcoreapp31 | .NET Core 3.1 | 0.0000 ns | 0.0000 ns | 0.0000 ns | 0.0000 ns | ? | ? | 1 |
DirectlyInt | netcoreapp31 | .NET Core 3.1 | 0.0693 ns | 0.0102 ns | 0.0091 ns | 0.0709 ns | ? | ? | 2 |
ReflectString | netcoreapp31 | .NET Core 3.1 | 98.6735 ns | 0.8335 ns | 0.7389 ns | 98.5319 ns | ? | ? | 7 |
ReflectInt | netcoreapp31 | .NET Core 3.1 | 130.6941 ns | 0.9332 ns | 0.8730 ns | 130.5376 ns | ? | ? | 8 |
GetterString | netcoreapp31 | .NET Core 3.1 | 14.8915 ns | 0.2025 ns | 0.1795 ns | 14.8911 ns | ? | ? | 4 |
GetterInt | netcoreapp31 | .NET Core 3.1 | 16.2874 ns | 0.0789 ns | 0.0700 ns | 16.2753 ns | ? | ? | 5 |
GetterObject | netcoreapp31 | .NET Core 3.1 | 17.6202 ns | 0.1130 ns | 0.1057 ns | 17.6092 ns | ? | ? | 6 |
GetterCached | netcoreapp31 | .NET Core 3.1 | 0.6351 ns | 0.0244 ns | 0.0217 ns | 0.6393 ns | ? | ? | 3 |
DirectlyString | netcoreapp5 | .NET Core 5.0 | 0.5098 ns | 0.0328 ns | 0.0291 ns | 0.5131 ns | 1.000 | 0.00 | 2 |
DirectlyInt | netcoreapp5 | .NET Core 5.0 | 0.0000 ns | 0.0000 ns | 0.0000 ns | 0.0000 ns | 0.000 | 0.00 | 1 |
ReflectString | netcoreapp5 | .NET Core 5.0 | 88.8937 ns | 0.9697 ns | 0.8596 ns | 88.7457 ns | 174.838 | 9.26 | 7 |
ReflectInt | netcoreapp5 | .NET Core 5.0 | 123.4464 ns | 1.0582 ns | 0.9898 ns | 123.3193 ns | 242.996 | 14.00 | 8 |
GetterString | netcoreapp5 | .NET Core 5.0 | 7.6628 ns | 0.0931 ns | 0.0777 ns | 7.6703 ns | 15.031 | 0.95 | 5 |
GetterInt | netcoreapp5 | .NET Core 5.0 | 6.6645 ns | 0.0825 ns | 0.0772 ns | 6.6497 ns | 13.085 | 0.69 | 4 |
GetterObject | netcoreapp5 | .NET Core 5.0 | 8.3090 ns | 0.1685 ns | 0.1576 ns | 8.2865 ns | 16.344 | 0.83 | 6 |
GetterCached | netcoreapp5 | .NET Core 5.0 | 0.9791 ns | 0.0293 ns | 0.0245 ns | 0.9764 ns | 1.920 | 0.13 | 3 |
Setter
Method | Job | Runtime | Mean | Error | StdDev | Median | Ratio | RatioSD | Rank |
---|---|---|---|---|---|---|---|---|---|
DirectlyString | net461 | .NET 4.6.1 | 2.0161 ns | 0.0300 ns | 0.0266 ns | 2.0045 ns | 1.000 | 0.00 | 2 |
DirectlyInt | net461 | .NET 4.6.1 | 0.0076 ns | 0.0094 ns | 0.0083 ns | 0.0081 ns | 0.004 | 0.00 | 1 |
ReflectString | net461 | .NET 4.6.1 | 237.5006 ns | 4.5706 ns | 4.4890 ns | 236.5912 ns | 117.871 | 3.40 | 5 |
ReflectInt | net461 | .NET 4.6.1 | 249.3627 ns | 2.1717 ns | 2.0314 ns | 249.0283 ns | 123.681 | 1.94 | 6 |
GetterString | net461 | .NET 4.6.1 | 32.8621 ns | 0.2855 ns | 0.2229 ns | 32.9189 ns | 16.335 | 0.22 | 4 |
GetterInt | net461 | .NET 4.6.1 | 33.6103 ns | 0.4245 ns | 0.3544 ns | 33.5499 ns | 16.695 | 0.26 | 4 |
GetterObject | net461 | .NET 4.6.1 | 33.2561 ns | 0.2966 ns | 0.2629 ns | 33.1795 ns | 16.497 | 0.17 | 4 |
GetterCached | net461 | .NET 4.6.1 | 9.1805 ns | 0.0761 ns | 0.0674 ns | 9.1802 ns | 4.554 | 0.08 | 3 |
DirectlyString | net48 | .NET 4.8 | 1.9272 ns | 0.0298 ns | 0.0264 ns | 1.9245 ns | 1.000 | 0.00 | 2 |
DirectlyInt | net48 | .NET 4.8 | 0.0000 ns | 0.0000 ns | 0.0000 ns | 0.0000 ns | 0.000 | 0.00 | 1 |
ReflectString | net48 | .NET 4.8 | 237.7686 ns | 4.6597 ns | 5.3661 ns | 235.8445 ns | 123.908 | 3.57 | 5 |
ReflectInt | net48 | .NET 4.8 | 249.6291 ns | 4.5333 ns | 4.2404 ns | 249.5459 ns | 129.689 | 3.13 | 6 |
GetterString | net48 | .NET 4.8 | 32.2366 ns | 0.1941 ns | 0.1721 ns | 32.1780 ns | 16.731 | 0.29 | 4 |
GetterInt | net48 | .NET 4.8 | 32.0081 ns | 0.1488 ns | 0.1162 ns | 32.0270 ns | 16.572 | 0.23 | 4 |
GetterObject | net48 | .NET 4.8 | 32.6413 ns | 0.1417 ns | 0.1183 ns | 32.6260 ns | 16.907 | 0.24 | 4 |
GetterCached | net48 | .NET 4.8 | 9.2589 ns | 0.0928 ns | 0.0868 ns | 9.2564 ns | 4.799 | 0.07 | 3 |
DirectlyString | netcoreapp21 | .NET Core 2.1 | 2.4107 ns | 0.0507 ns | 0.0475 ns | 2.3936 ns | 1.000 | 0.00 | 2 |
DirectlyInt | netcoreapp21 | .NET Core 2.1 | 0.0007 ns | 0.0028 ns | 0.0025 ns | 0.0000 ns | 0.000 | 0.00 | 1 |
ReflectString | netcoreapp21 | .NET Core 2.1 | 203.6637 ns | 3.6109 ns | 3.3777 ns | 203.4793 ns | 84.517 | 2.31 | 7 |
ReflectInt | netcoreapp21 | .NET Core 2.1 | 213.8619 ns | 2.1882 ns | 1.9398 ns | 213.7367 ns | 88.757 | 1.71 | 8 |
GetterString | netcoreapp21 | .NET Core 2.1 | 19.5240 ns | 0.0811 ns | 0.0758 ns | 19.5149 ns | 8.102 | 0.15 | 5 |
GetterInt | netcoreapp21 | .NET Core 2.1 | 18.8794 ns | 0.1193 ns | 0.1058 ns | 18.8837 ns | 7.836 | 0.18 | 4 |
GetterObject | netcoreapp21 | .NET Core 2.1 | 20.6765 ns | 0.2709 ns | 0.2115 ns | 20.6419 ns | 8.584 | 0.14 | 6 |
GetterCached | netcoreapp21 | .NET Core 2.1 | 2.7606 ns | 0.0613 ns | 0.0512 ns | 2.7590 ns | 1.148 | 0.02 | 3 |
DirectlyString | netcoreapp31 | .NET Core 3.1 | 2.2625 ns | 0.0647 ns | 0.0605 ns | 2.2555 ns | 1.000 | 0.00 | 2 |
DirectlyInt | netcoreapp31 | .NET Core 3.1 | 0.0072 ns | 0.0165 ns | 0.0146 ns | 0.0000 ns | 0.003 | 0.01 | 1 |
ReflectString | netcoreapp31 | .NET Core 3.1 | 182.7306 ns | 3.3249 ns | 3.1101 ns | 182.3062 ns | 80.804 | 1.99 | 7 |
ReflectInt | netcoreapp31 | .NET Core 3.1 | 192.2510 ns | 2.3691 ns | 2.1002 ns | 191.4821 ns | 85.061 | 2.88 | 8 |
GetterString | netcoreapp31 | .NET Core 3.1 | 16.9918 ns | 0.2115 ns | 0.1979 ns | 16.9651 ns | 7.516 | 0.25 | 5 |
GetterInt | netcoreapp31 | .NET Core 3.1 | 16.1168 ns | 0.3558 ns | 0.3654 ns | 15.9822 ns | 7.138 | 0.19 | 4 |
GetterObject | netcoreapp31 | .NET Core 3.1 | 19.3060 ns | 0.4173 ns | 0.5571 ns | 19.4856 ns | 8.480 | 0.45 | 6 |
GetterCached | netcoreapp31 | .NET Core 3.1 | 2.9276 ns | 0.0156 ns | 0.0146 ns | 2.9313 ns | 1.295 | 0.03 | 3 |
DirectlyString | netcoreapp5 | .NET Core 5.0 | 2.2455 ns | 0.0084 ns | 0.0078 ns | 2.2460 ns | 1.000 | 0.00 | 2 |
DirectlyInt | netcoreapp5 | .NET Core 5.0 | 0.0000 ns | 0.0000 ns | 0.0000 ns | 0.0000 ns | 0.000 | 0.00 | 1 |
ReflectString | netcoreapp5 | .NET Core 5.0 | 162.8780 ns | 0.7135 ns | 0.6674 ns | 162.8741 ns | 72.538 | 0.45 | 7 |
ReflectInt | netcoreapp5 | .NET Core 5.0 | 171.1380 ns | 0.4173 ns | 0.3699 ns | 171.1414 ns | 76.217 | 0.31 | 8 |
GetterString | netcoreapp5 | .NET Core 5.0 | 8.6244 ns | 0.1891 ns | 0.1769 ns | 8.5469 ns | 3.841 | 0.08 | 5 |
GetterInt | netcoreapp5 | .NET Core 5.0 | 6.5511 ns | 0.0347 ns | 0.0325 ns | 6.5634 ns | 2.917 | 0.02 | 4 |
GetterObject | netcoreapp5 | .NET Core 5.0 | 9.0732 ns | 0.0306 ns | 0.0272 ns | 9.0735 ns | 4.041 | 0.02 | 6 |
GetterCached | netcoreapp5 | .NET Core 5.0 | 2.8223 ns | 0.0728 ns | 0.0681 ns | 2.8190 ns | 1.257 | 0.03 | 3 |
总结
使用表达式创建委托来取代 PropertyInfo 读取和写入属性效果很好。
开发者也可以直接引用 Newbe.ObjectVisitor 包来使用已经封装好的 ValueGetter 和 ValueSetter。
我只是知识的搬运工
- 晚绑定场景下对象属性赋值和取值可以不需要 PropertyInfo
- 三种属性操作性能比较:PropertyInfo + Expression Tree + Delegate.CreateDelegate
- 关于 Expression Tree 和 IL Emit 的所谓的” 性能差别”
发布说明
使用样例
番外分享
GitHub 项目地址:https://github.com/newbe36524/Newbe.ObjectVisitor
Gitee 项目地址:https://gitee.com/yks/Newbe.ObjectVisitor
- 本文作者: newbe36524
- 本文链接: https://www.newbe.pro/Newbe.ObjectVisitor/Better-Performance-Getter-Setter/
- 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
寻找性能更优秀的动态 Getter 和 Setter 方案的更多相关文章
- Stylus-NodeJS下构建更富表现力/动态/健壮的CSS
--------------------------本文来自张鑫旭大神博客------------------------------ 一.为什么我会讲Stylus,而不是SASS和LESS? SAS ...
- 比Kafka Mangaer更优秀的开源监控工具-Kafka Eagle
比Kafka Mangaer更优秀的开源监控工具-Kafka Eagle 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在Kafka的监控系统中有很多优秀的开源监控系统.比如Kaf ...
- 通过AI自学习,Google让Pixel 3的人像模式更优秀
通过AI自学习,Google让Pixel 3的人像模式更优秀 Link: https://news.cnblogs.com/n/613720/ 虽然双摄手机已经在市场上普及,其所带来的人像模式.多倍变 ...
- Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%。再往后,每提高0.1%,优化难度成指数级增长了。哪怕是千分之一,也直接影响用户体验,影响每天上万张机票的销售额。 在高并发场景下,提供了保证线程安全的对象、方法。比如经典的ConcurrentHashMap,它比起HashMap,有更小粒度的锁,并发读写性能更好。线程安全的StringBuilder取代S
Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%.再往后,每提高0.1%,优化难度成指数级增长了.哪怕是千分之一,也直接影响用户体验,影响每天上万张机 ...
- Ember.js和Vue.js对比,哪个框架更优秀?
本文由葡萄城技术团队于博客园翻译并首发 转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. JavaScript最初是为Web应用程序创建的.但是随着前端技术的 ...
- 异步IO比同步阻塞IO性能更好吗?为什么?
最近在看node.js, 介绍中提到node是异步io的方式实现, 性能比同步阻塞io的更好. 对于一个request而言, 如果我们依赖io的结果, 异步io和同步阻塞io都是要等到io完成才能继续 ...
- PHP日志扩展 SeasLog-1.6.8, 性能更优
SeasLog-1.6.8 发布了,性能更优. 改进日志: 1.6.8: 优化内存使用和性能,修复已知Bug. - Fixed issue #97 PHP5.* Cached Block. - Fix ...
- 能让你成为更优秀程序员的10个C语言资源
能让你成为更优秀程序员的10个C语言资源 本文由 伯乐在线 - archychu 翻译自 mycplus.欢迎加入 技术翻译小组.转载请参见文章末尾处的要求. 一些人觉得编程无聊,一些人觉得它很好玩. ...
- graphicview和widgets没本质区别。它只是更轻量级,更灵活,性能更高的widgets
graphicview和widgets没本质区别.它只是更轻量级,更灵活,性能更高的widgets.核心就是把widgets变成了更轻量级的graphicitem,把QWidget的各种事件转换成了g ...
随机推荐
- 071 01 Android 零基础入门 01 Java基础语法 09 综合案例-数组移位 03 综合案例-数组移位-显示数组当中所有元素的的方法
071 01 Android 零基础入门 01 Java基础语法 09 综合案例-数组移位 03 综合案例-数组移位-显示数组当中所有元素的的方法 本文知识点:综合案例-数组移位-显示数组当中所有元素 ...
- 晶振(crystal)与谐振荡器(oscillator)
参考: 1. https://wenku.baidu.com/view/e609af62f5335a8102d2202f.html 2. 晶体振荡器也分为无源晶振和有源晶振两种类型.无源晶振与有源晶振 ...
- ASP。NET MVC的部分视图和部分模型
下载source - 1.7 MB 介绍 本文解决了返回视图内容包含表单元素的部分视图的问题. 代码重用是一种非常有用的节省时间的特性,任何优秀的工程师都会在他们的工作过程中构建许多有用的函数.对于W ...
- 踩坑 Pycharm 2020.1.1 安装/ JetBrains破解/ anacode配置
引言 网上的办法试了很多,通常不能解决问题,还会引发一些负效应,选取了一个试了两天终于成功的方案记录一下备用. Pycharm安装 https://www.jetbrains.com/pycharm/ ...
- shell-的特殊变量-进程状态变量$$ $! $? $_详解
一:shell的特殊变量-进程状态变量详解 1. 进程状态变量 $$ 获取当前shell的进程号(pid) $! 执行上一个指令的pid,上一个后台运行进程的进程号 $? 获取执行上一个指令的返回值 ...
- 本溪6397.7539(薇)xiaojie:本溪哪里有xiaomei
本溪哪里有小姐服务大保健[微信:6397.7539倩儿小妹[本溪叫小姐服务√o服务微信:6397.7539倩儿小妹[本溪叫小姐服务][十微信:6397.7539倩儿小妹][本溪叫小姐包夜服务][十微信 ...
- Mysql架构与内部模块-第二章
接上文,上文简述到了Mysql中的查询缓存和解析器,今日我们继续. 先来看一段SQL:SELECT * FROM `jianghuadong`; 先假设我们数据库中并没有一张名为jianghuadon ...
- Hybrid App中原生页面 VS H5页面
Hybrid App中原生页面 VS H5页面 现有3类主流APP,分别为:Web App.Hybrid App(混合模式移动应用,Hybrid有"混合的"意思). Nativ ...
- docker系统化学习图文教程
1.背景 在实际开发中我们经常遇到这样的情况: 1.开发的时候测试好的程序已发布到线上就出问题: 2.线上的集群环境需要扩容时非常麻烦,比如说要装jdk.mysql.redis等,如果扩容100台服务 ...
- 记2020年初对SimpleGUI源码的阅读成果
2020春节,阅读了下SimpleGUI源码,我一直喜欢边阅读,边手绘图片,所以这里只贴几张图片. 一,什么是SimpleGUI ? https://gitee.com/Polarix/simpleg ...