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秒
    (由于换用了一台性能好一些的机器,平均速度比以上测试偏快)
  • 相关阅读:
    Running APP 使用说明
    Android 控件八 WebView 控件
    Android 控件七 ImageView 控件
    Android 控件六 CheckBox 控件
    Android 控件五 RadioButton 控件
    Android 控件四 EditText 控件
    Android 控件三 TextView 控件实现 Button
    Android 控件二 Button
    Android 基础控件演示实例
    Android 控件一 TextView
  • 原文地址:https://www.cnblogs.com/chinadhf/p/474638.html
Copyright © 2011-2022 走看看