JGTM'2008 [MVP]

Thinking in WPF...
随笔 - 36, 评论 - 453, 引用 - 16

导航

标签

每月存档

最新留言

广告

 

ASP.NET使用动态编译技术,在运行时动态将同一目录的*.aspx文件先生成*.cs,然后调用CompilerServices将其编译成assemblies(可以到你的%SYSTEMROOT%\Microsoft.NET\Framework\V1.x.xxxx\Temporary ASP.NET Files下面看看)。因此了解ASP.NET编译的过程是优化ASP.NET运行效率的关键之一。以常用的<%# %>数据绑定语法为例,我们可以发现它的转化规则是:

.aspx:  <%# data-binding expression %>

->

.cs: System.Convert.ToString(data-binding expression);

其中data-binding expression是原封不动复制过来的,这样你写数据绑定表达式的时候就心里有谱了吧。关于常见于数据绑定表达式中的Container、DataItem、DataBinder.Eval是这样:DataBinder是System.Web里面的一个静态类,它提供了Eval方法用于简化数据绑定表达式的编写,但是它使用的方式是通过Reflection等开销比较大的方法来达到易用性,因此其性能并不是最好的。而Container则根本不是任何一个静态的对象或方法,它是ASP.NET页面编译器在数据绑定事件处理程序内部声明的局部变量,其类型是可以进行数据绑定的控件的数据容器类型(如在Repeater内部的数据绑定容器叫RepeaterItem),在这些容器类中基本都有DataItem属性,因此你可以写Container.DataItem,这个属性返回的是你正在被绑定的数据源中的那个数据项。如果你的数据源是DataTable,则这个数据项的类型实际是DataRowView。

现在你可以想想下面哪种写法效率最高(以Repeater+DataTable数据源为例):

  1. <@% DataBinder.Eval(Container.DataItem, "ColumnName") %>
  2. <@% DataBinder.Eval(Container.DataItem, "ColumnName", null) %>
  3. <@% DataBinder.Eval(Container, "DataItem.ColumnName", null) %>
  4. <@% ((DataRowView)Container.DataItem)["ColumnName"] %>
  5. <@% ((DataRowView)Container.DataItem).Row["ColumnName"] %>

NOTE: 后两种用法需要引入System.Data名称空间……答案一天后揭晓,欢迎有空的朋友自己测试得出结论!笑脸


揭晓+简要分析:

乍一看1-3都是使用DataBinder.Eval方法来进行数据绑定计算,而4-5是使用strong type直接获取数据绑定的值。按照我之前的推理,很多朋友会认为4-5都会比1-3快,而实际上第4种用法也是在网上很常见的一种针对DataBinder.Eval而进行的“优化”。

实际上根据我们的测试,第4种写法的效率在某些很常见的情形下(即传入的字段名与数据表内部的字段名大小写有出入时)甚至比不上最普遍的第1种写法。不过原理还是对的,就是避免通过reflection或类似机制(如System.ComponentModel中的PropertyDescriptor机制)获得数据,然而使用DataRowView的indexer的效率在字段数量较多导致Hashtable产生寻址冲突时不如使用其Row属性(DataRow类型)的indexer的效率。原因是DataRowView的indexer实现了view的功能,而这个功能对于大多数应用在这个场合都是不需要的,且它的开销甚至比DataBinder.Eval还要大!(本段内容过于武断,在被反复质疑之下我又做了若干试验寻得正确原因)因此简单的使用第五种写法通常是可以获得较佳的性能的,而最好不要在不必要的时候直接使用DataRowView的indexer。

现在回到1-3的讨论。首先一点,请大家注意看Eval方法的二种overload:

object DataBinder.Eval(object container, string expression)
string DataBinder.Eval(object container, string expression, string format)

注意到ASP.NET在生成的.cs文件中是使用System.Convert.ToString来将Eval的结果转换成string的,因此显式的提供值为null或String.Empty的format参数将使得Eval首先调用第一种方法得到绑定结果的对象,然后直接调用该对象的ToString()方法将其返回到Convert.ToString方法,对于该方法编译器已经在编译期将其连接到Convert.ToString(string)的重载上,而该方法则直接返回传入的字串。那如果直接使用第一种方法呢?虽然第二种方法是先调用第一种方法的,但是由于它的返回值是object类型,编译器将为其选择Convert.ToString(object)的重载,在这个重载方法中将进行一些额外的判断以将对象转换为string类型,而这些额外判断显然带来了额外的开销——尽管基本上算不得主要矛盾。

至于说第3种写法,由于在expression参数中多引入了一层间接,因此需要多进行一次反射以解析表达式,因此效率非常之低。

那这里再卖个关子,请推测第5种方法是否还可以进一步优化?(我是指在最常见的ASP.NET开发情形中):P

通过上面的分析,我们可以得到下面的结论:

  1. DataBinder.Eval是最常用也比较易用的数据绑定表达式写法,但由于其实现机制使用了反射,所以需要关注其所带来的性能损失。通常,当应用开发进入稳定期后可以针对性的对这些表达式进行优化。
  2. 但是优化不是光从字面上就能感觉到的,第4种所谓优化随处可见,然而在某些情况下它反而带来其他环节的开销,带来比较低的执行效率。
  3. 要注意方法重载是一种编译期机制,通过显式告诉编译器需要使用的方法重载,通常可以在得到同样结果的前提下获得更佳的性能。
  4. 性能虽重要,功能价更高——在一般的项目开发中,还是首先关注功能的实现,然后再通过实际测试有针对性的优化比较突出的性能瓶颈。

希望我的这点儿心得对您有所启发。:)

打印 | 张贴于 2003-12-11 20:02:00 | Tag:暂无标签

留言反馈

#回复: ASP.NET中的数据绑定:哪个更快? 编辑
<%# Eval("name")%>
用这种方法是不是开销很大?
2008-01-12 22:16:00 | [匿名用户:suger]
#ASP.NET中的数据绑定:哪个更快? 编辑
ASP.NET中的数据绑定:哪个更快?
2008-01-10 11:36:00 | [匿名用户:octverve]
#ASP.NET中的数据绑定:哪个更快? 编辑
ASP.NET使用动态编译技术,在运行时动态将同一目录的*.aspx文件先生成*.cs,然后调用CompilerServices将其编译成assemblies(可以到你的%SYSTEMROOT%\Mi...
2007-08-18 10:04:00 | [匿名用户:kaixin110]
#re: ASP.NET中的数据绑定:哪个更快? 编辑
原来说的最慢的第4种情况*确实*会在某些情况下出现(主要是由Hashtable在使用字段名进行搜索时带来的开销,但这在字段数量很小的时候很罕见)。

还是不太明白
使用Hashtable搜索字段名的效率应该比遍历搜索高吧,为什么你认为有了索引反倒不好了呢
2006-04-24 13:56:00 | [匿名用户:jason]
#re: ASP.NET中的数据绑定:哪个更快? 编辑
很好。<br><br>
2006-03-22 13:54:00 | [匿名用户:nigo]
#ASP.NET中的数据绑定:哪个更快? 编辑
http://blog.joycode.com/jgtm2000/archive/2003/12/11/9089.aspx
2006-01-11 15:08:00 | [匿名用户:zhh007]
#选择合适的数据控件 编辑
Ping Back来自:blog.csdn.net
2005-05-24 12:36:00 | [匿名用户:jierry]
#回复: ASP.NET中的数据绑定:哪个更快? 编辑
首先要感谢朋友们的参与和讨论,给我带来很大启发。就大家疑惑的问题,我又重新进行了本次试验,结果又有新的发现!原来说的最慢的第4种情况*确实*会在某些情况下出现(主要是由Hashtable在使用字段名进行搜索时带来的开销,但这在字段数量很小的时候很罕见)。

无论如何,我得承认我的结论确实有些片面了。稍后我会再补充描述这次相关的新发现并给出测试代码(楼上朋友们的代码似乎都不很精确)。希望大家继续关注并给予指教,我们离最佳实践已经不远了。谢谢!:)
2003-12-13 22:44:00 | [匿名用户:JGTM'2003]
#回复: ASP.NET中的数据绑定:哪个更快? 编辑
>>原因是DataRowView的indexer实现了view的功能,而这个功能对于大多数应用在这个场合都是不需要的,且它的开销甚至比DataBinder.Eval还要大

我已经我的文档中做了更新

repeater1.DataSource=ds.Tables[0].DefaultView; 和
repeater1.DataSource=ds.Tables[0];

两者有区别吗?dino esposito都犯了错误
在 构建web解决方案—应用asp.net 和ado.net 中,第12页和第13页中写了,如果DataSource是DataTable,则数据绑定语法写作
<%# ((DataRow)Container.DataItem)["field"] %>
当然,在实际中这是行不通的
如果我们了解asp.net 服务器控件数据绑定过程和DataTable的实现,就会理解为什么是上面的看法是错误的
对于实现IListSource接口的对象,像DataTable,服务器控件总是会读取其IListSource的GetList返回的数据作为数据源
DataTable的GetList实现是
IList IListSource.GetList()
{
return DefaultView;
}
因此,上面两行代码其实是一样的
所有,只有写成
<%# ((DataRowView)Container.DataItem)["field"] %>
编译才能通过
2003-12-13 18:50:00 | [匿名用户:jjx]
#回复: ASP.NET中的数据绑定:哪个更快? 编辑
对于4比1还慢,我表示怀疑:
>>原因是DataRowView的indexer实现了view的功能,而这个功能对于大多数应用在这个场合都是不需要的,且它的开销甚至比DataBinder.Eval还要大
--- 好象很牵强,没有具体原理代码分析.

>>那这里再卖个关子,请推测第5种方法是否还可以进一步优化?(我是指在最常见的ASP.NET开发情形中):
-- 那就用<@% ((DataRowView)Container.DataItem).Row[0] %>
用索引代替"NAME".
2003-12-13 18:12:00 | [匿名用户:erictang2003]
#回复: ASP.NET中的数据绑定:哪个更快? 编辑
我也作了类似jjx的测试,在1.1环境下,不同的是我只取top 50,我想这样应该更接近于真实情况,一般返回要Bind()的纪录已经是比较少了,每个叶面测试200次,结果是5>4>2=1>3
3明显慢很多,至于4和5差别不是很大,2和1我测试200次的结果保留6位小数,结果竟然还是一模一样。
2003-12-13 16:59:00 | [匿名用户:mmkk]
#回复: ASP.NET中的数据绑定:哪个更快? 编辑
我不清除你测试的是否全面
在现在的1.2下
我的测试是
第四种最快
838条记录,单位毫秒

第四种始终在170左右
第五种在170~180到200多左右
第一种在210左右
没测试二和三

2003-12-13 12:28:00 | [匿名用户:jjx]
#回复: ASP.NET中的数据绑定:哪个更快? 编辑
其实,让我们说的更简单些,原理JGTM'2003 已经分析了,那我来简单化,呵呵。

简单说——重载是最快的。重载/覆写都是编译期的东西,所以速度当然快,不过JGTM'2003 的分析个人感觉还是比较深入的。
2003-12-13 09:44:00 | [匿名用户:NetFire(Fire.Rolland.Han) ]
#回复: ASP.NET中的数据绑定:哪个更快? 编辑
5使用column ordinal比使用column name应该更快一点.
2003-12-13 00:21:00 | [匿名用户:mmkk]
#回复: ASP.NET中的数据绑定:哪个更快? 编辑
不知道大家注意了没有,quickstart上面就有过这样一句话——DataBinder.Eval 会对标准数据绑定语法带来很明显的性能损失,因为它使用后期绑定反射,注意这一点很重要。使用 DataBinder.Eval 时需谨慎,尤其是在不需要字符串格式化时。

所以,我一直很上心,但是一直没有机会做测试,今天看到,关注一下。暂时不发表评论,只是希望JGTM'2003 能把里有说充分些
2003-12-12 15:43:00 | [匿名用户:NetFire(Fire.Rolland.Han)]
#回复: ASP.NET中的数据绑定:哪个更快? 编辑
为什么4会最慢?
2003-12-12 14:32:00 | [匿名用户:microhelper]
#回复: ASP.NET中的数据绑定:哪个更快? 编辑
猜想是最后一种吧
2003-12-12 13:16:00 | [匿名用户:microhelper]
对不起,目前本随笔不允许发表新评论.

Powered by: Joycode.MVC引擎 0.5.1.8