热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C#之反射性能优化3

阅读目录开始用Delegate优化反射的缺点用Delegate优化反射的优点用CodeDOM优化反射的优点如何用好CodeDOM?用CodeDOM优化反射的缺点能不能不使用委托?根

阅读目录

  • 开始
  • 用Delegate优化反射的缺点
  • 用Delegate优化反射的优点
  • 用CodeDOM优化反射的优点
  • 如何用好CodeDOM?
  • 用CodeDOM优化反射的缺点
  • 能不能不使用委托?
  • 根据反射密集程度选择优化方法
  • CodeDOM优化的误区
  • 反射优化的总结

在前二篇博客中,我分别介绍了二种优化反射的方法:
1. Delegate:委托。
2. CodeDOM:动态代码生成。
这是二种截然不同的方法,性能的差距也很大。
今天的博客将着重比较它们的优缺点,以及给出它们的使用建议。

回到顶部

在评价委托方案时,我认为有必要细分一下委托方案:
1. 强类型委托,例如:Action
2. 弱类型委托,例如:Action

它们的优点分别是:
强类型委托:速度快,已经最接近直接调用的性能,然而它的缺点是 不通用。
弱类型委托:比较通用,且经过一些代码封装后,使用方便,但是 封装后的性能会变差。

回到顶部

用Delegate优化反射的优点

优点有二个:
1. 实现简单,不管是使用Emit, ExpressionTree还是CreateDelegate,代码量都不大。
2. 方法通用,使用弱类型委托,我们可以封装出很容易使用的API,且适用于任何项目。

回到顶部

用CodeDOM优化反射的优点

最大的,也是唯一的优点就是:性能好。
由于生成的是直接调用的代码,因此最终运行的是直接调用的代码,所以没有性能损耗。
另外,代码生成器可以决定最终生成的代码质量,代码生成器越优秀,代码的性能也会更优秀。

注意:当使用这种技术时,不同人可能会有不同的使用方法,最终可以得到性能不同的结果, (理论上)最坏情况下可能比委托还差。

如果希望借助这种优化方式实现最好的性能,需要做好二件事情:
1. 保证最终生成的代码质量是最优的。
2. 编译方式的设计要合理(用好CodeDOM)。

如何保证最终生成的代码质量是最优的,我给不了建议,需要您自己去思考,
我们接着讨论第2点。

回到顶部

如何用好CodeDOM?

虽然采用动态编译技术,我们可以生成直接调用的代码来代替反射调用,这样就不会有任何性能损失。
但是,还有一个问题也是需要考虑的:我该以什么粒度去生成代码?
1. 是为每个反射调用生成代码?
2. 还是为每个类型批量生成一段代码?
3. 还是为一堆类型大批量的生成一批代码?

由于动态编译的结果并不能直接调用,我们只能借助委托或者接口的方式去调用,
所以如果每次代码生成的粒度较小,将会产生大量的程序集,也会消耗较多的编译器启动时间,
因此,这并不是高效的做法。高效的做法应该是一次尽可能生成较多的代码。

除此之外,还有一个问题也要考虑:当需要循环调用编译结果时,该怎么办?
对于这类场景,我建议在生成代码时,把循环过程直接生成出来,最终只用一次调用编译结果完成整个调用过程。
例如我们可以为数据访问层生成这样类似的代码,把循环、创建实体对象,以及给属性赋值的所有操作全部包含进来:

public static List LoadProduct(DbDataReader reader)
{
    List list = new List();

    while( reader.Read() ) {
        Product p = new Product();
        p.ProductID = (int)reader["ProductID"];
        p.ProductName = reader["ProductName"].ToString();
        p.CategoryID = (int)reader["CategoryID"];
        p.Unit = reader["Unit"].ToString();
        p.UnitPrice = (decimal)reader["UnitPrice"];
        p.Remark = reader["Remark"].ToString();
        p.Quantity = (int)reader["Quantity"];
        list.Add(p);
    }
    return list;
}

如果我们生成了这样的代码,最后只需要一次调用,就可以代替以前上百次的委托调用以及缓存查找,锁的冲突也会减少到最低。

回到顶部

用CodeDOM优化反射的缺点

缺点有三个:
1. 方法不通用,需要针对不同的类型,不同的数据源生成不同的直接调用代码,因此难以通用化。
2. 复杂性较高,由于是生成直接调用的代码,且数据类型及格式未知,因此需要周密的考虑各种情况,复杂性也随之增高。
3. 难以封装,由于编译的结果是一个程序集,它并不能直接调用,还需要借助其它的方式来调用,所以难以实现较为通用的封装。

回到顶部

能不能不使用委托?

既然我们可以在运行时动态生成代码并编译它们,达到代替反射的目标,因此也就不需要委托调用的优化方法了。
那么,委托还有意义吗? 或者说:优化反射时能不能不使用委托?

在上篇博客中,我演示过动态编译的方法。
由于动态编译的结果是一个程序集,它本身是不能直接调用,我们需要采用其它的方法去调用它。
那篇博客给大家介绍了二种方法,其中一种方法就是用委托去调用程序集中的方法。
由于那些在运行时生成的代码是由我们的代码生成的,方法的签名我们可以控制,
所以,这时调用 Delegate.CreateDelegate 方法您不会遇到任何麻烦,
因此,通过强类型的委托来调用CodeDOM的编译结果,这种配合会非常方便。
正是由于这个原因,当您选择生成static类型的方法时,委托还是必须的,此时委托和CodeDOM将是一种共存关系。

如果您在生成代码时采用了接口的设计方案,那么委托就没有必要使用了。

回到顶部

根据反射密集程度选择优化方法

优化反射,到底是选择CodeDOM,还是选择Delegate ?
我认为要按不同的反射密集程度分开讨论。

1. 反射密集程度低:例如:一次HTTP请求过程,我们的代码只需要一二次反射操作,
或者对于桌面程序来说,在响应用户点击事件时,使用了几次反射调用。
在这类场景中,反射的密集程度就可认为是很低的。那么这种情况下该如何优化呢?
我的答案是:优不优化都无所谓,因为反射并不是慢得不能接受。
反射的速度到底有多慢? 我们还是来看一下以前做过的测试吧:

C# 之 反射性能优化3

从这张图片(来源于本系列的第一篇)可以看出,用反射的方式执行属性赋值操作,就算运行1000000次,也只花了1.2秒! 要知道我的测试机器是3年前买的笔记本电脑,如果换成目前专业的服务器,消耗的时间会更少, 因此,这类反射的优化价值不大。 当然了,如果您愿意优化它们那也不是件坏事。

2. 反射密集程度高:例如,数据访问层的应用中, 当一次加载一个实体列表时,反射次数是分页数量乘以字段数量,再加上创建实体对象数量。 这个数量很容易达到百次级别,而且一次HTTP请求过程中,可能需要加载多种数据,那么反射次数就很可观了。 我们经常感觉各种序列化和反序列化程序的执行效率不高,这与反射有着很直接的关系。 不过,我们通常不需要编写序列化反序列化程序,也只能***接受它们的性能了。 因此,对于反射密集程度很高的代码,如果优化手段不理想,肯定会影响性能。

3. 当处于前二者之间的密集程度。由于这类场景实在是无法定性衡量, 而且不同人对性能敏感程度也不一样,或者由于不同的应用对性能的要求也不同。
因此,这类场景的范围只能靠自己去评估了,优化方式也只能是自行选择了:
1. 关注性能的话,就选择CodeDOM,
2. 否则就选择Delegate吧,毕竟这种方法使用简单。

回到顶部

CodeDOM优化的误区

1. CodeDOM真能让程序的性能提升千倍吗?
根据前面的截图,我们知道直接调用比反射调用的性能要提升千倍, 因此是不是可以认为采用动态编译的方法,程序的性能就能提升千倍?
答案是否定的。举例来说,拿创建实体对象的场景来说,虽然反射调用所花时间和直接调用时间差了千倍, 即使我们用动态编译代替了反射,但是给属性赋值前,我们需要为那些属性获取数据。 然而,获取数据的操作极有可能比反射更慢,因此,对于整个过程来说,我们能优化的只是其中的一小部分, 所以,当我们测试整个过程时,性能不会提升到千倍。 性能提升多少倍,取决于反射在整个过程中所花时间的比例。

2. CodeDOM方案一定比Delegate方案快。
答案也是否定的,前面已经解释过了,如果您为每个反射调用去生成一个方法(委托的思路),那么最后还是需要一个委托或者一个接口来调用, 而且此时还要加上编译器的启动时间,最终的性能将比委托更慢。

回到顶部

反射优化的总结

反射优化的根本方法只有一条路:避开反射。
然而,避开的方法可分为二种:
1. 用委托去调用。(绕弯子)
2. 生成直接调用代码,替代反射调用。(直截了当)

这二种方法都有优缺点,我认为选择哪种方法应该根据反射场景来决定:
1. 调用目标明确(名称和类型都是已知):强类型委托方法是较好的选择。
2. 调用目标不明确,且调用程度密集:动态编译方法是最好的选择。
3. 其它情况:可以用弱类型委托,或者不优化。


推荐阅读
  • 本文探讨了一个Web工程项目的需求,即允许用户随时添加定时任务,并通过Quartz框架实现这些任务的自动化调度。文章将介绍如何设计任务表以存储任务信息和执行周期,以及如何通过一个定期扫描机制自动识别并加载新任务到调度系统中。 ... [详细]
  • Java虚拟机及其发展历程
    Java虚拟机(JVM)是每个Java开发者日常工作中不可或缺的一部分,但其背后的运作机制却往往显得神秘莫测。本文将探讨Java及其虚拟机的发展历程,帮助读者深入了解这一关键技术。 ... [详细]
  • Irish budget airline Ryanair announced plans to significantly increase its route network from Frankfurt Airport, marking a direct challenge to Lufthansa, Germany's leading carrier. ... [详细]
  • linux网络子系统分析(二)—— 协议栈分层框架的建立
    目录一、综述二、INET的初始化2.1INET接口注册2.2抽象实体的建立2.3代码细节分析2.3.1socket参数三、其他协议3.1PF_PACKET3.2P ... [详细]
  • 深入理解Java SE 8新特性:Lambda表达式与函数式编程
    本文作为‘Java SE 8新特性概览’系列的一部分,将详细探讨Lambda表达式。通过多种示例,我们将展示Lambda表达式的不同应用场景,并解释编译器如何处理这些表达式。 ... [详细]
  • 实践指南:使用Express、Create React App与MongoDB搭建React开发环境
    本文详细介绍了如何利用Express、Create React App和MongoDB构建一个高效的React应用开发环境,旨在为开发者提供一套完整的解决方案,包括环境搭建、数据模拟及前后端交互。 ... [详细]
  • 本文探讨了如何选择一个合适的序列化版本ID(serialVersionUID),包括使用生成器还是简单的整数,以及在不同情况下应如何处理序列化版本ID。 ... [详细]
  • 本文探讨了如何使用Scrapy框架构建高效的数据采集系统,以及如何通过异步处理技术提升数据存储的效率。同时,文章还介绍了针对不同网站采用的不同采集策略。 ... [详细]
  • 本文详细介绍了在Luat OS中如何实现C与Lua的混合编程,包括在C环境中运行Lua脚本、封装可被Lua调用的C语言库,以及C与Lua之间的数据交互方法。 ... [详细]
  • 本文探讨了Python类型注解使用率低下的原因,主要归结于历史背景和投资回报率(ROI)的考量。文章不仅分析了类型注解的实际效用,还回顾了Python类型注解的发展历程。 ... [详细]
  • 问题场景用Java进行web开发过程当中,当遇到很多很多个字段的实体时,最苦恼的莫过于编辑字段的查看和修改界面,发现2个页面存在很多重复信息,能不能写一遍?有没有轮子用都不如自己造。解决方式笔者根据自 ... [详细]
  • Android与JUnit集成测试实践
    本文探讨了如何在Android项目中集成JUnit进行单元测试,并详细介绍了修改AndroidManifest.xml文件以支持测试的方法。 ... [详细]
  • 深入理解:AJAX学习指南
    本文详细探讨了AJAX的基本概念、工作原理及其在现代Web开发中的应用,旨在为初学者提供全面的学习资料。 ... [详细]
  • 在将 Android Studio 从 3.0 升级到 3.1 版本后,遇到项目无法正常编译的问题,具体错误信息为:org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:processDemoProductDebugResources'。 ... [详细]
  • 本文介绍了一个使用Spring框架和Quartz调度器实现每周定时调用Web服务获取数据的小项目。通过详细配置Spring XML文件,展示了如何设置定时任务以及解决可能遇到的自动注入问题。 ... [详细]
author-avatar
手机用户2602934963
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有