zoukankan      html  css  js  c++  java
  • 性能测试:Reflection VS CodeDom


        进行这个测试是由数据持久层的性能问题引起的。
        访问一个实体的属性,直接访问当然是最好的方式,但在数据持久层(包括其他一些工厂模式)中,持久层不能预知实体的属性,自然也无法用直接访问的方式来获取或设置属性值。在这种前提下,反射成为被广泛采用的访问方式。但众所周知,反射的性能是比较低的,大量的使用反射会造成程序性能的下降。.Net框架提供的CodeDOM 对象模型为我们提供了另一种访问实体属性的方式,通过使用CodeDom,可以创建可在项目间共享的可重用的样板源代码,实现对C++ 模板的模拟。
        用CodeDom动态编译在内存中生成我们根据实体定制的类后,执行效率是非常高的,但是为此需要付出的代价是:动态编译是一个非常消耗性能和内存的过程。与使用反射相比,哪一种方式更合算呢?以下是我对两种方式的简单测试:

    1.测试内容
    获取一个简单实体类的实例的属性值。
    实体类代码:
    public class Student
        
    {
            
    private int? id;
            
    private string name;

            
    public int? Id
            
    {
                
    get return id; }
                
    set { id = value; }
            }


            
    public string Name
            
    {
                
    get return name; }
                
    set { name = value; }
            }

        }

    2.测试过程
    分别用三种方式(直接、反射、动态编译)获取实体实例的属性值100000次。
    测试代码:
     public void GetValue(object obj)
            
    {
                DateTime startTime 
    = DateTime.Now;

                
    for (int i = 0; i < 100000; i++)
                
    {
                    Student s 
    = (Student)obj;
                    
    int? id = s.Id;
                    
    string name = s.Name;
                }


                DateTime endTime 
    = DateTime.Now;

                Console.WriteLine(((TimeSpan)(endTime 
    - startTime)).TotalMilliseconds);
            }


            
    public void GetValueByReflection(object obj)
            
    {
                DateTime startTime 
    = DateTime.Now;

                PropertyInfo pId 
    = obj.GetType().GetProperty("Id");
                PropertyInfo pName 
    = obj.GetType().GetProperty("Name");
                
    for (int i = 0; i < 100000; i++)
                
    {
                    
    int? id = (int?)pId.GetValue(obj, null);
                    
    string name = (string)pName.GetValue(obj, null);
                }


                DateTime endTime 
    = DateTime.Now;

                Console.WriteLine(((TimeSpan)(endTime 
    - startTime)).TotalMilliseconds);
            }


            
    public void GetValueByCodeDom(object obj)
            
    {
                DateTime startTime 
    = DateTime.Now;

                CodeDomProvider prov 
    = new CSharpCodeProvider();
                CompilerParameters para 
    = new CompilerParameters();
                para.ReferencedAssemblies.Add(
    "Entity.dll");
                para.GenerateInMemory 
    = true;
                
    string code = 
                    
    "namespace Entity{"+
                    
    "public class StudentValue : EntityValue" +
                    
    "{" +
                    
    "   public override object GetPropertyValue(object obj,string propertyName)" +
                    
    "   {" +
                    
    "        Student s = (Student)obj;" +
                    
    "        switch (propertyName)" +
                    
    "        {" +
                    
    "        case \"Id\":" +
                    
    "            return s.Id;" +
                    
    "        case \"Name\":" +
                    
    "            return s.Name;" +
                    
    "        default:" +
                    
    "            return null;" +
                    
    "        }" +
                    
    "   }" +
                    
    "}}";
                CompilerResults cr 
    = prov.CompileAssemblyFromSource(para, code);
                Assembly assembly 
    = cr.CompiledAssembly;
                EntityValue sv 
    = assembly.CreateInstance("Entity.StudentValue"as EntityValue;

                
    for (int i = 0; i < 100000; i++)
                
    {
                    
    int? id = (int?)sv.GetPropertyValue(obj, "Id");
                    
    string name = (string)sv.GetPropertyValue(obj, "Name");
                }

                DateTime endTime 
    = DateTime.Now;

                Console.WriteLine(((TimeSpan)(endTime 
    - startTime)).TotalMilliseconds);
            }

    (注:为简化测试,代码中省略了获取属性名称、属性类型的步骤,这个步骤对于使用反射和CodeDom是相同的。)
    在第三种方式中,为方便动态生成类的调用,让其从一个虚基类中继承而来。虚基类代码如下:
    public abstract class EntityValue
        
    {
            
    public abstract object GetPropertyValue(object obj,string propertyName);
        }

    即便是循环100000次,多数情况下也无法取得第一种方式(直接读取)所用时间,此处暂且忽略不计。以下是第二、三种方式的测试结果(在IDE环境中直接运行):
    第一次:Refletion 2.859秒,CodeDom 0.438秒
    第二次:Refletion 2.609秒,CodeDom 0.422秒
    第二次:Refletion 2.703秒,CodeDom 0.391秒
    从这个结果看,使用CodeDom比使用Reflection使性能有大幅度提高,但是我们需要考虑另外两个因素:
    1.我们用100000次进行测试,但现实中是否会用到如此多的反射次数。由于使用CodeDom方式时,绝大多数性能消耗在动态编译过程中,如果完成编译后只是少量的使用,其性能将远远低于使用反射。
    2.CodeDom方式需使用更多的内存空间用于缓存编译结果。
    而且不仅如此,如果在Windows平台上直接运行以上编译好的执行文件,发现几乎是另一个测试结果:
    第一次:Refletion 0.594秒,CodeDom 0.406秒
    第二次:Refletion 0.563秒,CodeDom 0.391秒
    第二次:Refletion 0.609秒,CodeDom 0.406秒
    在Windows平台上直接运行时,反射性能有了大幅度的提高!以致于使用CodeDom已经几乎没有优势。

    我不知道这个测试是否合理,也无从得知这个测试结果是否可靠。但测试结果着实让我对CodeDom在避免反射、提高性能方面难以选择,不知大家如何认为?

    06.08.14补充:
    非常感谢大家的关注,根据回复中的建议(改用Stopwatch计时、Ralease方式编译、分别测试CodeDom编译时间和访问时间),我于今天重新进行了测试,10次测试的平均结果为:
    使用Reflection:0.530秒
    使用CodeDom:动态编译 0.281秒,访问用时 0.107秒,总计用时 0.388秒
    (由于换用了一台性能好一些的机器,平均速度比以上测试偏快)
  • 相关阅读:
    创建字典的方法
    python中,a=10.0 b=10.0 a is b 为什么输出是false
    Python 拷贝对象(深拷贝deepcopy与浅拷贝copy)
    二十三种设计模式及其python实现
    python字符串替换的2种方法
    数据库索引的实现原理?
    异步IO数据库队列缓存RabbitMQ队列
    Python 如何用列表实现栈和队列?
    Python数据结构与算法?
    django -----原生SQL语句查询与前端数据传递?
  • 原文地址:https://www.cnblogs.com/chinadhf/p/474638.html
Copyright © 2011-2022 走看看