ChinaDHF
学而不思则罔,思而不学则殆。
博客园
首页
新文章
新随笔
订阅
管理
posts - 41, comments - 210, trackbacks - 4
性能测试: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秒
(由于换用了一台性能好一些的机器,平均速度比以上测试偏快)
posted on 2006-08-11 21:12
东海风
阅读(12447)
评论(22)
编辑
收藏
FeedBack:
#1楼
2006-08-11 21:54 |
idior
不知道stopwatch吗?你这样测是不准的
回复
引用
查看
#2楼
2006-08-11 22:21 |
edison1024
DateTime这种是测不准滴。
应该采用stopwatch,在 System::Diagnostics下
回复
引用
查看
#3楼
[
楼主
]
2006-08-11 22:41 |
东海风
非常感谢 idior , edison1024 。
我改用Stopwatch后重新测试了一下,结果为:
IDE环境中运行:
第一次:Refletion 3.007秒,CodeDom 0.498秒
第二次:Refletion 2.989秒,CodeDom 0.454秒
第二次:Refletion 2.979秒,CodeDom 0.430秒
Windows中直接运行:
第一次:Refletion 0.575秒,CodeDom 0.417秒
第二次:Refletion 0.565秒,CodeDom 0.423秒
第二次:Refletion 0.528秒,CodeDom 0.436秒
回复
引用
查看
#4楼
2006-08-11 22:52 |
海蓝
@东海风
你的Build类型是选的Debug吗?如果是,VS的IDE要处理Debug信息,这可能会对结果有些影响
能否把你的完整的代码发布出来?我尝试把你的代码放到我的工程里,但运行时总是会报错
回复
引用
查看
#5楼
[
楼主
]
2006-08-11 23:03 |
东海风
@海蓝
除了实例化一个Student(用作方法中的实参)及Main方法对以上三种方法的调用外,这已经是全部代码了。需要注意的是文中提到的那个虚基类,需要编译为一个Dll文件(Entity.dll)供动态生成类使用。
回复
引用
查看
#6楼
[
楼主
]
2006-08-11 23:26 |
东海风
@海蓝
非常感谢你的提醒。在IDE中和Windows中执行反射性能之所以有这么大的差别,答案就在这儿了。我生成Release版本后重新测试,在IDE中执行与在Windows中直接执行结果基本一致了,就是以上在Windows中运行时的结果。
回复
引用
查看
#7楼
2006-08-12 00:31 |
海蓝
@东海风
如果两种办法的性能差异不大,我就推荐使用反射了。因为CodeDOM这种拼凑代码的方式会导致代码的阅读和维护很麻烦,而且不能在编译期进行语法检查,有错只能在运行期才暴露,这也增加了调试和排错的困难
不过msdn中关于CodeDOM的介绍中提到,在一些特定的场合,CodeDOM还是很有用的
回复
引用
查看
#8楼
2006-08-12 00:47 |
路过 [未注册用户]
问个 菜鸟的问题
int? 是什么东西。。
谢谢。
回复
引用
查看
#9楼
2006-08-12 01:33 |
U2U
学习中。,。。。
回复
引用
查看
#10楼
2006-08-12 08:02 |
Hi [未注册用户]
@路过
int? 是C#2.0中的新类型,即Nullable(可空)类型。
回复
引用
查看
#11楼
2006-08-12 13:35 |
yzx110
在.net2.0种,反射的性能相比已经大幅度提高了,在.net2.0没有发布以前就有微软官方的测试出来过。
分别比较了:直接调用,Interface调用,反射调用,多态调用,好像不止这些,记得不全了。
反射性能很不错了。
CodeDOM这种方式我一直觉得是个比较不错的东西,但是一直没有遇到适用的场景。
回复
引用
查看
#12楼
[
楼主
]
2006-08-12 15:54 |
东海风
@海蓝
是的。如果使用CodeDom没有明显的性能优势,就真的是“费力不讨好”了,还是用反射合算一些。
回复
引用
查看
#13楼
[
楼主
]
2006-08-12 16:05 |
东海风
@yzx110
从这个测试看,只要不是滥用反射并尽量优化使用反射,反射的性能问题不会对应用程序的性能造成大的影响,大可不必“谈反射色变”,甚至把应用程序的性能问题全部归咎于反射。
从相对数看,反射性能的确较低,应该是直接调用的几十倍甚至上百倍。但从绝对数看,200000次反射耗时0.5秒左右,完全是可以接受的,这在整个应用程序(可能包括数据访问、业务处理、界面显示)中只占很小的比例,不会对应用程序性能造成明显影响。
回复
引用
查看
#14楼
2006-08-12 21:00 |
达达
路过顶一下
回复
引用
查看
#15楼
2006-08-13 12:52 |
skywood [未注册用户]
楼主要是有空的话用Emit再做一个试试
回复
引用
查看
#16楼
2006-08-14 11:10 |
henry
830条记录获取测试情况,运行10次第一次和最后一次不要取其中8次的平均时间(查看方便只保留3位小数).
0.040(秒) CodeDom映射到实体集合
0.123(秒) .NET的反射功能的映射到实体集合(反射信息已缓存)
0.024(秒) 直接填充到DataSet
这些数据是我很早之前的测试结果。
现在使用是经过优化的,在此功能上CodeDom的效率和反射的效率大概有6-8倍的差距。
在.NET2。0中没有测试过。
回复
引用
查看
#17楼
2006-08-14 13:41 |
Colin Han
关注一下。
建议能够将CodeDOM的性能作一些详细的分析,例如:动态编译用了多少时间?访问用了多少时间等?
然后具体根据实际情况,择优使用。
回复
引用
查看
#18楼
[
楼主
]
2006-08-14 15:15 |
东海风
@henry
CodeDom方式下的耗时 0.040秒 包含动态编译时间吗?
回复
引用
查看
#19楼
[
楼主
]
2006-08-14 15:39 |
东海风
@Colin Han
感谢关注,我已将测试结果补充在上文中。
回复
引用
查看
#20楼
2006-08-14 15:44 |
Colin Han
@东海风
大大反应神速,感谢共享这么好的信息。
回复
引用
查看
#21楼
2006-08-14 17:04 |
henry
@东海风
我的测试结果是完全整个数据获取操作。
在新版的组件中,这种查询填充对象操作已的效率已经优越于Fill DataSet.
以下是新的测试结果:
P4 1.7 1G
获取NorthWind.Orders表所有记录的结果(头和尾去丢)
测试代码
System.Collections.IList list = NorthWind.Entities.Orders.Mapper.List
结果
Record:830
Time:00.02713864
=================================================
Record:830
Time:00.02972976
=================================================
Record:830
Time:00.01973798
=================================================
Record:830
Time:00.02078476
=================================================
Record:830
Time:00.02262941
=================================================
Record:830
Time:00.02215533
=================================================
Record:830
Time:00.02194888
=================================================
Record:830
Time:00.02048417
=================================================
回复
引用
查看
#22楼
2007-10-27 19:06 |
cokkiy [未注册用户]
CodeDom不是这样用的,如果这样用,我觉得不如反射,既简单,性能也没多少差别。CodeDom的应用场景,应该是Create一次,查询很多次的场景。比如在动态查询中,管理员选择需要查询的字段(属性),组合生成一个新的类,编译后缓存到backstore,其他人员利用这个查询。如果不是这种场合,真的没必要用CodeDom。
回复
引用
查看
社区
新闻
新用户注册
刷新评论列表
标题
姓名
主页
Email
(只有博主才能看到)
验证码
*
看不清,换一张
内容(请不要发表任何与政治相关的内容)
Remember Me?
登录
使用高级评论
新用户注册
返回页首
恢复上次提交
[使用Ctrl+Enter键可以直接提交]
该文被作者在 2006-08-14 15:44 编辑过
另存
打印
最新IT新闻:
博客园新闻频道
博客园首页
社区
计数器:
阿里妈妈再掀疯狂采购风,网站广告位严重告急,急召天下站长
<
2006年8月
>
日
一
二
三
四
五
六
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
与我联系
发短消息
常用链接
我的随笔
我的空间
我的短信
我的评论
更多链接
我的参与
我的新闻
最新评论
我的标签
留言簿
(2)
给我留言
查看留言
随笔档案
(39)
2008年6月 (1)
2008年4月 (2)
2007年12月 (1)
2007年11月 (3)
2007年4月 (1)
2006年10月 (1)
2006年8月 (2)
2006年7月 (7)
2006年5月 (1)
2006年3月 (7)
2006年1月 (4)
2005年12月 (9)
收藏夹
(12)
博客园文章(11)
其他技术网站(1)
技术网站
CSDN.Net
CSharpCorner
Csharp-Source.Net
GotDotNet
MSDN 中文网站
SourceForge.net
The Code Project
UML软件工程组织
博客堂
微软中国社区
中国IT实验室
阅读排行榜
1. 性能测试:Reflection VS CodeDom(12447)
2. Regulator的下载地址(5978)
3. 中缀表达式到逆波兰表达式的转换及求值(3748)
4. 使用ASP.Net Forms模式实现WebService身份验证(3152)
5. 用 .Net Remoting 技术实现“定向广播”(2308)