原 .NET/C# 反射的的性能数据,以及高性能开发建议(反射获取 Attribute 和反射调用方法)
大家都说反射耗性能,但是到底有多耗性能,哪些反射方法更耗性能;这些问题却没有统一的描述。
本文将用数据说明反射各个方法和替代方法的性能差异,并提供一些反射代码的编写建议。为了解决反射的性能问题,你可以遵循本文采用的各种方案。
本文内容
反射各方法的性能数据
反射的高性能开发建议
创建类型的实例
反射获取 Attribute
反射调用公共 / 私有方法
使用预编译框架
附本文性能测试所用的代码
所有反射相关方法
IsDefined 和 GetCustomAttribute 的专项比较
参考资料
反射各方法的性能数据
我使用 BenchmarkDotNet 基准性能测试来评估反射各个方法的性能。测试的程序基于 .NET Core 2.1 开发。
先直观地贴出我的运行结果:
▲ 各反射不同方法的运行基准测试结果
我把上面的表格复制下来成为文字,这样你也可以拿走我的这部分数据:
Method Mean Error StdDev Median
Assembly 13.5315 ns 0.3004 ns 0.4764 ns 13.4878 ns
Attributes 7.0893 ns 0.1248 ns 0.1168 ns 7.0982 ns
CustomAttributes 1,489.1654 ns 29.4428 ns 27.5408 ns 1,482.5038 ns
GetCustomAttributesData 1,514.5503 ns 29.6863 ns 39.6303 ns 1,507.2949 ns
GetCustomAttributes 1,171.8969 ns 22.5305 ns 27.6695 ns 1,167.2777 ns
GetCustomAttribute 1,139.8609 ns 22.8043 ns 24.4003 ns 1,140.5437 ns
GetCustomAttribute_Generic 1,115.0049 ns 13.1929 ns 11.6952 ns 1,111.4426 ns
GetCustomAttributes_Generic 1,164.5132 ns 22.7775 ns 24.3716 ns 1,165.2747 ns
New 0.0003 ns 0.0013 ns 0.0012 ns 0.0000 ns
Lambda 0.0063 ns 0.0149 ns 0.0139 ns 0.0000 ns
Activator_CreateInstance 48.8633 ns 0.6300 ns 0.5893 ns 48.8906 ns
Activator_CreateInstance_Generic 47.7029 ns 0.9649 ns 1.0724 ns 47.5851 ns
Expression_New 75,634.4035 ns 1,467.3285 ns 1,372.5400 ns 75,413.2837 ns
CachedExpression_New 7.8923 ns 0.1988 ns 0.4105 ns 7.7004 ns
如果你希望了解以上每一项的意思,可以通过阅读本文文末的代码来了解其实现。基本上名称就代表着反射调用相同的方法。
你一定会说这张表不容易看出性能差距。那么我一定会放图:
那个 Expression_New 在图中独树一帜,远远把其他方法甩在了后面。那是个什么方法?
那是在使用 Expression 表达式创建一个类型的新实例:
var @new = Expression.New(typeof(ReflectionTarget));
var lambda = Expression.Lambda<Func<ReflectionTarget>>(@new).Compile();
var instance = lambda.Invoke();
也就是说,如果你只是希望创建一个类型的新实例,就不要考虑使用 Expression.New 的方式了。除非此方法将执行非常多次,而你把那个 lambda 表达式缓存下来了。这对应着图表中的 CachedExpression_New。
其他的现在都看不出来性能差异,于是我们把耗时最长的 Expression_New 一项去掉:
我们立刻可以从图中得到第二梯队的性能巨头 —— 就是 CustomAttributes 系列。我使用了多种不同的 CustomAttribute 获取方法,得到的结果差异不大,都“比较耗时”。不过在这些耗时的方法里面找到不那么耗时的,就是 Type 的扩展方法系列 GetCustomAttribute 了,比原生非扩展方法的性能稍好。
不过其他的性能差异又被淹没了。于是我们把 CustomAttributes 系列也删掉:
于是我们又得到了第三梯队的性能大头 —— Activator.CreateInstance 系列。而是否调用泛型方法的耗时差异不大。
然后,我们把 Activator.CreateInstance 也干掉,可以得到剩下其他的性能消耗。
也就是说,只是获取 Type 中的一些属性,例如 Assembly 和 Attributes 也是比较“耗时”的;当然,这是纳秒级别,你可以将它忽略。
要不要试试把第四梯队的也干掉呢?于是你可以得到 new 和 Lambda 的差异:
原本在上面所有图中看起来都没有时间的 new 和 Lambda 竟然差异如此巨大;不过,这都是千分之一纳秒级别了;如果你创建的类数量不是百万级别以上,你还真的可以忽略。
而 new 指的是 new Foo(),Lambda 指的是 var func = () => new Foo(); func();。
对于 GetCustomAttribute,还有另一个方法值得注意:IsDefined;可以用来判断是否定义了某个特定的 Attribute。
var isDefined = _targetType.IsDefined(typeof(ReflectionTargetAttribute), false);
if (isDefined)
{
var attribute = _targetType.GetCustomAttribute<ReflectionTargetAttribute>()
而这个方法与 GetCustomAttribute 的性能差距也有些大:
Method Mean Error StdDev Ratio RatioSD
IsDefined 653.8 ns 13.07 ns 16.53 ns 1.00 0.00
GetCustomAttribute 1,149.6 ns 22.97 ns 22.56 ns 1.76 0.06
GetGenericCustomAttribute 1,216.5 ns 24.15 ns 54.51 ns 1.81 0.07
咋看之下似乎与 GetCustomAttribute 方法重复,而且如果先判断再获取,可能总时间更长。不过这种方法就是适用于一次性对大量类型进行判断,如果只有少量类型定义了某种 Attribute,那么提前使用 IsDefined 判断可以获得总体更加的性能。
反射的高性能开发建议
创建类型的实例
如果你能访问到类型:
建议直接使用 new,性能最好。
如果不希望直接 new 出来,可以考虑使用 Func 或者 Lazy 创建。这时会多消耗一些性能,不过基数小,增量不大。
如果你不能访问到类型:
如果只能从 Type 创建,则使用 Activator.CreateInstance 系列。
如果你使用其他方式创建,请一定使用缓存。
除了使用 Expression 创建,你还可以使用 Emit 创建,不过这也要求能够访问到类型:
使用 Emit 生成 IL 代码 - 吕毅
对于缓存,可以参考:
.NET Core/Framework 创建委托以大幅度提高反射调用的性能 - 吕毅
.NET/C# 推荐一个我设计的缓存类型(适合缓存反射等耗性能的操作,附用法) - 吕毅
对于创建对象更多的性能数据,可以参考:
C# 直接创建多个类和使用反射创建类的性能 - 林德熙
C# 性能分析 反射 VS 配置文件 VS 预编译 - 林德熙
反射获取 Attribute
获取 Attribute 也是耗时的操作。
如果你只是获取极少数类型的 Attribute,建议直接调用 GetCustomAttribute 扩展方法。
如果你需要判断大量类型的 Attribute,建议先使用 IsDefined 判断是否存在,如果存在才使用 GetCustomAttribute 方法获取真实实例。
反射调用公共 / 私有方法
反射调用方法与构造方法几乎是一样的,不同之处就在于公共方法可以创建出委托缓存,而私有方法却不行。
有了委托缓存,你只有第一次才需要真的调用反射,后续可以使用缓存的委托或 Lambda 表达式;而私有方法是无法创建的,你每次都需要通过反射来调用相关方法。
关于私有方法的反射:
C# 使用反射获取私有属性的方法
C# 反射调用私有事件
关于缓存:
.NET Core/Framework 创建委托以大幅度提高反射调用的性能 - 吕毅
.NET/C# 推荐一个我设计的缓存类型(适合缓存反射等耗性能的操作,附用法) - 吕毅
使用预编译框架
使用预编译框架,你可以在编译期间将那些耗时的反射操作编译成类似 new 和属性 get 这样的简单 CLR 调用,性能差距近乎于最开始图表中第二张图和第五张图那样,具有数千倍的差距。
课程 预编译框架,开发高性能应用 - 微软技术暨生态大会 2018 - walterlv
dotnet-campus/SourceFusion: SourceFusion is a pre-compile framework based on Roslyn. It helps you to build high-performance .NET code.
附本文性能测试所用的代码
本文性能测试使用 BenchmarkDotNet,在 Main 函数中调用以下代码跑起来:
BenchmarkRunner.Run<Reflections>();
1
你可以阅读 C# 标准性能测试 - 林德熙 了解基准性能测试的基本用法,在 C# 标准性能测试高级用法 - 林德熙 中了解到更多基准测试方法的使用。
所有反射相关方法
using BenchmarkDotNet.Attributes;
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace Walterlv.Demo.Reflection
{
public class Reflections
{
private static readonly Type _targetType = typeof(ReflectionTarget);
private static Func<ReflectionTarget> _cachedExpressionFunc;
private static Func<ReflectionTarget> CachedExpressionFunc
{
get
{
if (_cachedExpressionFunc == null)
{
var @new =www.meiwanyule.cn Expression.New(typeof(ReflectionTarget));
var lambda =www.mhylpt.com Expression.Lambda<Func<ReflectionTarget>>(@new).Compile();
_cachedExpressionFunc = lambda;
}
return _www.dasheng178.com cachedExpressionFunc;
}
}
[Benchmark]
public void Assembly()
{
var assembly =www.mcyllpt.com _targetType.Assembly;
}
[Benchmark]
public void Attributes()
{
var attributes = _targetType.Attributes;
}
[Benchmark]
public void CustomAttributes(www.hjylp178.com)
{
var attribute = _targetType.CustomAttributes.FirstOrDefault(
x => x.AttributeType www.ysyl157.com== typeof(ReflectionTargetAttribute));
}
[Benchmark]
public void GetCustomAttributesData()
{
var attribute = _targetType.GetCustomAttributesData().FirstOrDefault(
x => x.AttributeType == typeof(ReflectionTargetAttribute));
}
[Benchmark]
public void GetCustomAttributes()
{
var attribute = _targetType.GetCustomAttributes(typeof(ReflectionTargetAttribute), false).FirstOrDefault();
}
[Benchmark]
public void GetCustomAttribute()
{
var attribute =www.yigouyule2.cn _targetType.GetCustomAttribute(typeof(ReflectionTargetAttribute), false);
}
[Benchmark]
public void GetCustomAttribute_Generic(www.hengy178.com)
{
var attribute = _targetType.GetCustomAttribute<ReflectionTargetAttribute>(false);
}
[Benchmark]
public void GetCustomAttributes_Generic()
{
var attribute = _targetType.GetCustomAttributes<ReflectionTargetAttribute>(false);
}
[Benchmark]
public void New(www.120xh.cn)
{
var instance = new ReflectionTarget();
}
[Benchmark]
public void Lambda()
{
var instance = new ReflectionTarget();
}
[Benchmark]
public void Activator_CreateInstance()
{
var instance = (ReflectionTarget) Activator.CreateInstance(_targetType);
}
[Benchmark]
public void Activator_CreateInstance_Generic()
{
var instance = Activator.CreateInstance<ReflectionTarget>();
}
[Benchmark]
public void Expression_New()
{
var @new = Expression.New(typeof(ReflectionTarget));
var lambda = Expression.Lambda<Func<ReflectionTarget>>(@new).Compile();
var instance = lambda.Invoke();
}
[Benchmark]
public void CachedExpression_New(
IsDefined 和 GetCustomAttribute 的专项比较
using System;
using System.Reflection;
using BenchmarkDotNet.Attributes;
namespace Walterlv.Demo.Reflection
{
public class IsDefinedVsGetCustomAttribute
{
private static readonly Type _targetType = typeof(ReflectionTarget);
[Benchmark(Baseline = true)]
public void IsDefined()
{
var isDefined = _targetType.IsDefined(typeof(ReflectionTargetAttribute), false);
}
[Benchmark]
public void GetCustomAttribute()
{
var attribute = _targetType.GetCustomAttribute(typeof(ReflectionTargetAttribute), false);
}
[Benchmark]
public void GetGenericCustomAttribute()
{
var attribute = _targetType.GetCustomAttribute<ReflectionTargetAttribute>(false);
参考资料
c# - Is there a benefit of using IsDefined over GetCustomAttributes - Stack Overflow
Accessing Attributes by Using Reflection (C#) - Microsoft Docs
win10 uwp 反射
Reference Source
A Super-Fast C# Extension Method using Expression Trees to Create an instance from a Type
Retrieving Custom Attributes Using Reflection - Scott Dorman
Showtime - BenchmarkDotNet
Choosing RunStrategy - BenchmarkDotNet
原 .NET/C# 反射的的性能数据,以及高性能开发建议(反射获取 Attribute 和反射调用方法)的更多相关文章
- .NET/C# 反射的的性能数据,以及高性能开发建议(反射获取 Attribute 和反射调用方法)——转载
原文链接:https://blog.walterlv.com/post/dotnet-high-performance-reflection-suggestions.html ***** 大家都说反射 ...
- 使用反射将DataTable的数据转成实体类
利用反射避免了硬编码出现的错误,但是实体类的属性名必须和数据库名字对应(相同) 1.利用反射把DataTable的数据写到单个实体类 /// <summary> ///利用反射把DataT ...
- 性能测试 基于Python结合InfluxDB及Grafana图表实时采集Linux多主机或Docker容器性能数据
基于Python结合InfluxDB及Grafana图表实时采集Linux多主机性能数据 by:授客 QQ:1033553122 实现功能 1 测试环境 1 环境搭建 3 使用前提 3 使用方法 ...
- XML序列化 判断是否是手机 字符操作普通帮助类 验证数据帮助类 IO帮助类 c# Lambda操作类封装 C# -- 使用反射(Reflect)获取dll文件中的类型并调用方法 C# -- 文件的压缩与解压(GZipStream)
XML序列化 #region 序列化 /// <summary> /// XML序列化 /// </summary> /// <param name="ob ...
- iOS-监听原生H5性能数据window.performance
WebKit-WKWebView iOS8开始苹果推荐使用WKWebview作为H5开发的核心组件,以替代原有的UIWebView,以下是webkit基本介绍介绍: 介绍博客 Webkit H5 - ...
- .Net最佳实践3:使用性能计数器收集性能数据
本文值得阅读吗? 本文讨论我们如何使用性能计数器从应用程序收集数据.我们将先了解的基本知识,然后我们将看到一个简单的示例,我们将从中收集一些性能数据. 介绍: - 我的应用程序的性能是最好的,像火箭 ...
- 通过ngx-lua来统计Nginx上的虚拟主机性能数据
Web server调研分析 Filed under: Web Server — cmpan @ 2012-10-29 20:38:34 摘要 简单可依赖的架构首先需要有一个简单可依赖的前端WebSe ...
- Performance Monitor采集性能数据
Performance Monitor采集性能数据 Windows本身为我们提供了很多好用的性能分析工具,大家日常都使用过资源管理器,在里面能即时直观的看到CPU占用率.物理内存使用量等信息.此外新系 ...
- Redis各种数据结构性能数据对比和性能优化实践
很对不起大家,又是一篇乱序的文章,但是满满的干货,来源于实践,相信大家会有所收获.里面穿插一些感悟和生活故事,可以忽略不看.不过听大家普遍的反馈说这是其中最喜欢看的部分,好吧,就当学习之后轻松一下. ...
随机推荐
- C#语言基础 Main 函数中变量 整型
在我们每次上网或者用电脑的时候,请输入你的xxx 或者你的名字(年龄/身高/学校/籍贯)是 在这里我们就要学到一些变量,就是不确定的东西 string a: //赋予变量 a ="内容& ...
- P2421 A-B数对(增强版)
题目背景 woshiren在洛谷刷题,感觉第一题:求两数的和(A+B Problem)太无聊了,于是增加了一题:A-B Problem,难倒了一群小朋友,哈哈. 题目描述 给出N 个从小到大排好序的整 ...
- gEdit - GTK+ 基础文本编辑器
语法 gedit [--help] [--version] [文件名] [文件名] [文件名] 等等... 描述 gEdit 是一个 X窗口系统下的基础文本编辑器由 GTK+ 写成.它现在支持建立,打 ...
- .net MVC下跨域Ajax请求(CORS)
二.CROS (Cross-origin Resource Sharing) CROS相当于一种协议,由浏览器.服务端共同完成安全验证,进行安全的跨域资源共享.对于开发人员来说就跟在本站AJAX请求一 ...
- 洛谷 P1214 等差数列
https://www.luogu.org/problemnew/show/P1214 首先暴力枚举可以凑出来的数,对于每个数进行标记. 对于每一个等差数列,当我们知道前两个数后即可以得出整个序列,那 ...
- NOIP 成绩
这道题中点是在小数上,因为成绩可能是:“95.5 87.7……”所以我们就要用:printf和scanf这样就可以控制小数了!!! code: #include<bits/stdc++.h> ...
- GIMP素描效果
1/打开图片,拖动图片到GIMP软件 2/复制两次图层 3/选中最上面的一个图层,mode改为Dodge 4/点击Color,选择Invert,可以看到图片变淡了 5/点击Filters,Distor ...
- linux中mysql自带同步
今天打算给一个做主备web服务器.考虑到数据库的同步,现在自己本地虚拟机做个实验. 经过慎重考虑(其实就是参考了下论坛大家的看法). 最后决定用mysql自带的同步设置. 话不多说 配置过程如下.
- 如何用纯 CSS 创作一个均衡器 loader 动画
效果预览 在线演示 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/oybWBy 可交互视频教 ...
- fshc之请求仲裁机制(from mcu and cache)
1.arbiter模块本身放在sclk时钟域,但是输入都是来之HCLK时钟域. 2.当MCU/CACHE访问FSHC时,FSHC不接受其他请求,FSHC只可以同时处理一个请求的操作. 3.如果原子操作 ...