性能测试:Reflection VS CodeDom

本文通过实验对比了直接访问、反射及CodeDom三种方式获取实体属性的性能。结果显示,在不同环境下,反射与CodeDom的表现差异较大。考虑到动态编译的额外开销,实际应用需权衡。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


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

1.测试内容
获取一个简单实体类的实例的属性值。
实体类代码:
None.gifpublic class Student
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif{
InBlock.gif        
private int? id;
InBlock.gif        
private string name;
InBlock.gif
InBlock.gif        
public int? Id
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif            
get dot.gifreturn id; }
ExpandedSubBlockStart.gifContractedSubBlock.gif            
set dot.gif{ id = value; }
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
public string Name
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif            
get dot.gifreturn name; }
ExpandedSubBlockStart.gifContractedSubBlock.gif            
set dot.gif{ name = value; }
ExpandedSubBlockEnd.gif        }

ExpandedBlockEnd.gif    }

2.测试过程
分别用三种方式(直接、反射、动态编译)获取实体实例的属性值100000次。
测试代码:
None.gif public void GetValue(object obj)
ExpandedBlockStart.gifContractedBlock.gif        
dot.gif{
InBlock.gif            DateTime startTime 
= DateTime.Now;
InBlock.gif
InBlock.gif            
for (int i = 0; i < 100000; i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                Student s 
= (Student)obj;
InBlock.gif                
int? id = s.Id;
InBlock.gif                
string name = s.Name;
ExpandedSubBlockEnd.gif            }

InBlock.gif
InBlock.gif            DateTime endTime 
= DateTime.Now;
InBlock.gif
InBlock.gif            Console.WriteLine(((TimeSpan)(endTime 
- startTime)).TotalMilliseconds);
ExpandedBlockEnd.gif        }

None.gif
None.gif        
public void GetValueByReflection(object obj)
ExpandedBlockStart.gifContractedBlock.gif        
dot.gif{
InBlock.gif            DateTime startTime 
= DateTime.Now;
InBlock.gif
InBlock.gif            PropertyInfo pId 
= obj.GetType().GetProperty("Id");
InBlock.gif            PropertyInfo pName 
= obj.GetType().GetProperty("Name");
InBlock.gif            
for (int i = 0; i < 100000; i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
int? id = (int?)pId.GetValue(obj, null);
InBlock.gif                
string name = (string)pName.GetValue(obj, null);
ExpandedSubBlockEnd.gif            }

InBlock.gif
InBlock.gif            DateTime endTime 
= DateTime.Now;
InBlock.gif
InBlock.gif            Console.WriteLine(((TimeSpan)(endTime 
- startTime)).TotalMilliseconds);
ExpandedBlockEnd.gif        }

None.gif
None.gif        
public void GetValueByCodeDom(object obj)
ExpandedBlockStart.gifContractedBlock.gif        
dot.gif{
InBlock.gif            DateTime startTime 
= DateTime.Now;
InBlock.gif
InBlock.gif            CodeDomProvider prov 
= new CSharpCodeProvider();
InBlock.gif            CompilerParameters para 
= new CompilerParameters();
InBlock.gif            para.ReferencedAssemblies.Add(
"Entity.dll");
InBlock.gif            para.GenerateInMemory 
= true;
InBlock.gif            
string code = 
InBlock.gif                
"namespace Entity{"+
InBlock.gif                
"public class StudentValue : EntityValue" +
InBlock.gif                
"{" +
InBlock.gif                
"   public override object GetPropertyValue(object obj,string propertyName)" +
InBlock.gif                
"   {" +
InBlock.gif                
"        Student s = (Student)obj;" +
InBlock.gif                
"        switch (propertyName)" +
InBlock.gif                
"        {" +
InBlock.gif                
"        case \"Id\":" +
InBlock.gif                
"            return s.Id;" +
InBlock.gif                
"        case \"Name\":" +
InBlock.gif                
"            return s.Name;" +
InBlock.gif                
"        default:" +
InBlock.gif                
"            return null;" +
InBlock.gif                
"        }" +
InBlock.gif                
"   }" +
InBlock.gif                
"}}";
InBlock.gif            CompilerResults cr 
= prov.CompileAssemblyFromSource(para, code);
InBlock.gif            Assembly assembly 
= cr.CompiledAssembly;
InBlock.gif            EntityValue sv 
= assembly.CreateInstance("Entity.StudentValue"as EntityValue;
InBlock.gif
InBlock.gif            
for (int i = 0; i < 100000; i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
int? id = (int?)sv.GetPropertyValue(obj, "Id");
InBlock.gif                
string name = (string)sv.GetPropertyValue(obj, "Name");
ExpandedSubBlockEnd.gif            }

InBlock.gif            DateTime endTime 
= DateTime.Now;
InBlock.gif
InBlock.gif            Console.WriteLine(((TimeSpan)(endTime 
- startTime)).TotalMilliseconds);
ExpandedBlockEnd.gif        }

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

即便是循环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秒
(由于换用了一台性能好一些的机器,平均速度比以上测试偏快)

转载于:https://www.cnblogs.com/chinadhf/archive/2006/08/11/474638.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值