首页 > 其他分享 >.Net 7内容汇总(3)--反射优化

.Net 7内容汇总(3)--反射优化

时间:2022-09-26 12:11:36浏览次数:42  
标签:反射 -- CreateInstance 汇总 public AssemblyName Net NET

反射这玩意,一直以来都是慢的代名词。一说XXX系统大量的反射,好多人第一印象就是会慢。


但是呢,我们又不得不使用反射来做一些事情,毕竟这玩意可以说啥都能干了对吧。

It’s immensely powerful, providing the ability to query all of the metadata for code in your process and for arbitrary assemblies you might encounter, to invoke arbitrary functionality dynamically, and even to emit dynamically-generated IL at run-time.

当然.Net也提供了一些性能更高的方法。

比如SG,这玩意是性能最好的方案,它在编译的时候生成代码,运行的时候一点反射没有,同时也完美支持Native AOT。但是呢,它还不是真正的动态生成,只能说是开发时动态。所以更适合一些框架程序使用来提高执行效率。

还有比如Emit,这玩意是动态编织IL代码的,效率也比反射要快。但是呢,写起来极度复杂,10个人有8个都挠头。


所以,.Net 7里反射还是非常重要的一部分,也针对它做了一些比较牛逼的优化。

  1. 我们知道,给MethodBase使用CreateDelegate<T>来创建一个委托,然后调用这个委托是最佳方法。但是呢,我们编译的时候经常是不知道这个方法签名的,也就是没法生成这个委托。部分库已经使用Emit来生成代码提高速度了。但是我们普通用户显然区写一堆Emit是不现实的。.Net 7优化后,会把我们的反射代码优化为DynamicMethod形式的委托,然后调用。

    我们来看一下数据

private MethodInfo _method;

[GlobalSetup]
public void Setup() => _method = typeof(Program).GetMethod("MyMethod", BindingFlags.NonPublic | BindingFlags.Static);

[Benchmark]
public void MethodInfoInvoke() => _method.Invoke(null, null);

private static void MyMethod() { }

Method

Runtime

Mean

Ratio

MethodInfoInvoke

.NET 6.0

43.846 ns

1.00

MethodInfoInvoke

.NET 7.0

8.078 ns

0.18

我们可以看到,这玩意速度提升了好几倍。


反射还有一个用处就是对类型、方法、属性等等这些东西进行获取。一些其他的改进也会影响到这一部分。比如.Net最近一直在做的把原生类型转换为托管类型的工作,就产生了这么一个东西。

[Benchmark]
public Type GetUnderlyingType() => Enum.GetUnderlyingType(typeof(DayOfWeek));

Method

Runtime

Mean

Ratio

GetUnderlyingType

.NET 6.0

27.413 ns

1.00

GetUnderlyingType

.NET 7.0

5.115 ns

0.19

是的,原生类型转换为托管类型,不但没有拖慢反射,反而让它快了好几倍。


同样的例子,有大量的AssemblyName的内容从原生转向了CoreLib,所以Activator.CreateInstance也跟着变快了。

private readonly string _assemblyName = typeof(MyClass).Assembly.FullName;
private readonly string _typeName = typeof(MyClass).FullName;
public class MyClass { }

[Benchmark]
public object CreateInstance() => Activator.CreateInstance(_assemblyName, _typeName);

Method

Runtime

Mean

Ratio

CreateInstance

.NET 6.0

3.827 us

1.00

CreateInstance

.NET 7.0

2.276 us

0.60

这玩意虽然没有那么夸张,但是提升可以说也是不小了。


RuntimeType.CreateInstanceImpl现在使用Type.EmptyTypes代替了new Type[0],所以节省了一部分开销。

[Benchmark]
public void CreateInstance() => Activator.CreateInstance(typeof(MyClass), BindingFlags.NonPublic | BindingFlags.Instance, null, Array.Empty<object>(), null);

internal class MyClass
{
    internal MyClass() { }
}

Method

Runtime

Mean

Ratio

Allocated

Alloc Ratio

CreateInstance

.NET 6.0

167.8 ns

1.00

320 B

1.00

CreateInstance

.NET 7.0

143.4 ns

0.85

200 B

0.62

我们再回到AssemblyName来,AssemblyName里把AssemblyName.FullName的实现由StringBuilder改为了ArrayPool<char>,所以:

private AssemblyName[] _names = AppDomain.CurrentDomain.GetAssemblies().Select(a => new AssemblyName(a.FullName)).ToArray();

[Benchmark]
public int Names()
{
    int sum = 0;
    foreach (AssemblyName name in _names)
    {
        sum += name.FullName.Length;
    }
    return sum;
}

Method

Runtime

Mean

Ratio

Allocated

Alloc Ratio

Names

.NET 6.0

3.423 us

1.00

9.14 KB

1.00

Names

.NET 7.0

2.010 us

0.59

2.43 KB

0.27

另外由于JIT编译器又进化了,现在可以在编译过程中计算结果,所以:

[Benchmark]
public bool IsByRefLike() => typeof(ReadOnlySpan<char>).IsByRefLike;

Method

Runtime

Mean

Ratio

Code Size

IsByRefLike

.NET 6.0

2.1322 ns

1.00

31 B

IsByRefLike

.NET 7.0

0.0000 ns

0.00

6B

是的,你没看错,时间是0,因为这里在运行的时候已经不需要计算了,直接就是个赋值操作,所以这个时间就。。。

我们来看一下生成的汇编

; Program.IsByRefLike()
       mov       eax,1
       ret
; Total bytes of code 6

这就是反射优化的主要内容。反正就高喊666,知道反射又快了,用起来心里负担又小了就搞定了^ ^。

标签:反射,--,CreateInstance,汇总,public,AssemblyName,Net,NET
From: https://www.cnblogs.com/j4587698/p/16730442.html

相关文章

  • 对软件工程的理解
    对软件工程的理解软件工程就是经过时间考验、实践证明的思想和方法论的集合,是一门经验学科。对于一个稍微大型的工程我们不仅仅只要求数据结构简洁、算法实现快速。因为......
  • 关于WPF自定义控件OnApplyTemplate不执行,手动调用Template.FindName返回空的问题
    我在wpf项目中手写了一个自定义控件,运行得相当的正常,后续调用时,反复遇到问题,前前后后折腾了好几次代码publicExtendCombox(){Loaded+=(e......
  • JAVA 反射与模块化
    JAVA反射与模块化类加载类的初始化步骤假如类还未被加载和连接,则程序现在加载并连接该类假如该类的直接父类还未被初始化,则先初始化其直接父类假如类中有初始化语......
  • pdfkit,retry,wordcloud
    pdfkitpipinstallpdfkit#wkhtmltopdf的Python封装包安装wkhtmltopdf并添加至环境变量sudoaptinstallwkhtmltopdfpdfkit用法pdfkit.from_url('http://google......
  • 【记录】学习 阿里巴巴Java开发手册 (持续更新)
    前言在这里记录一下自己学习编码的规范和规约的过程,希望可以纠正自己的不良习惯,让编码更加规范和合理,具有更加好的可读性。阿里巴巴Java开发手册v1.3.0分为六个部分。针......
  • 软件工程师:意外之旅(如何学习编码和找到工作)
    软件工程师:意外之旅(如何学习编码和找到工作)如果(in_a_rush):如果你上车迟到了,需要完成你最近的副业,或者(上帝保佑)和一个真正的人(不是人工智能)约会,我会为你节省一些时间。TL;......
  • GitHub 如何帮助您构建项目
    GitHub如何帮助您构建项目当我开始开发Nawat应用程序时,我使用笔记来跟进开发任务。工作量越快增长,我就需要另一个开发人员来帮助我完成不同的任务。聘请了一名自由职......
  • 写他们想读的简历
    写他们想读的简历写出一份好的和简洁的简历是一项令人惊讶的没有多少人具备的技能。您的简历是您对招聘经理的第一印象,并且是解释为什么有人应该雇用您的事实的书面陈述。......
  • delphi cxSplitter学习笔记
     //cxSplitter禁用移动和拖拽,只允许点击箭头按钮触发缩放(展开和收起)procedureTFraModleBase.cxSplitter3CanResize(Sender:TObject;varNewSize:Integer;varAcc......
  • Blazor中CSS隔离无法用在Masa Blazor组件上
    最近新学了Blazor,使用了MasaBlazor。Blazor的CSS隔离是个很好的东西,如图,只需添加一个与Razor组件同名的CSS文件,这个文件中的CSS样式只会在同名的Razor组件中使用。原理......