服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - ASP.NET教程 - ASP.NET页面优化 性能提升8倍的方法

ASP.NET页面优化 性能提升8倍的方法

2019-09-29 11:31asp.net教程网 ASP.NET教程

今天与大家分享:一种优化页面执行速度的方法。采用这个方法,可以使用页面的执行速度获得【8倍】的提升效果

为了让您对优化的效果有个直观的了解,我准备了下面的测试结果截图: 

ASP.NET页面优化 性能提升8倍的方法

测试环境: 
1. Windows Server 2003 SP2 
2. Viaual Studio 2008,使用自带的WebDev.WebServer.EXE运行网站程序。 
3. (ThinkPad SL510):Core2 T6670 2.2GHz, 4G内存 

二个红框中的数字反映了优化前后的执行时间。 

数字表明:优化前后,执行时间有了8倍多的差别。 

测试背景 

看过了优化结果,再来介绍一下:这个测试到底是在测试什么东西? 

现在有很多做ASP.NET的开发人员,应该都是从ASP.NET的WebForm编程模型开始学习的。大家都很喜欢用服务器控件,不管输出什么,都会使用服务器控件。有时候为了让页面呈现干净的HTML代码,有些人会选择使用Repeater,Literal这类简单的服务器控件。或许有些人认为:我已不使用GridView这样强大复杂的控件,页面执行速度已经很快了。 

真是这样吗? 

今天测试的起点就从使用简单的服务器开始,我会分二次对它做一系列的性能优化。 
最终就是上图中的3个结果,它们反映了二次优化的改进过程。 

在继续介绍之前,有一点我想有必要说明一下: 

优化的过程涉及到ASP.NET服务器控件的使用,测试结果也仅仅只是一个参考数字。 
如果您认为您的开发工作非常依赖于服务器控件的使用, 
那么测试结果对您来说其实是无意义的,请不要在意这个结果。 
测试方法 
在这次优化过程中,我并没有设计很复杂的测试页面,而是一个很简单的测试页面,页面显示效果如下: 
ASP.NET页面优化 性能提升8倍的方法
这个页面其实就是显示了一堆超链接,它们来自于我的博客侧边栏的【推荐排行榜】,总共有20条记录,我让页面重复5次输出,也就是生成了100个超链接。 

测试的数据是这样获取的: 
我复制了我的博客侧边栏的【推荐排行榜】的那段HTML代码,保存到一个文件中: 
ASP.NET页面优化 性能提升8倍的方法
然后,网站在初始化时,从这段HTML代码提取链接地址以及显示文字,保存到一个BlogInfo的列表中,代码如下: 

复制代码代码如下:


public class BlogInfo 

public string Title; 
public string Href; 


public static class XmlDb 

public static List<BlogInfo> Blogs { get; private set; } 


public static void LoadBlogs() 

string filePath = Path.Combine(HttpRuntime.AppDomainAppPath, @"App_Data\RecommendList.html"); 

XElement html = XElement.Parse(System.IO.File.ReadAllText(filePath)); 

Blogs = 
(from a in html.Elements("li").Elements("a") 
select new BlogInfo { /> 测试时,就把XmlDb.Blogs的内容显示在网页中。 
我想这个测试还是比较接近于现实开发的。 

这里又有一个问题:我如何测试页面的执行速度? 

虽然说创建HttpWebRequest访问页面是个很简单的方法,但我并不打算这样做。 
因为从HttpWebRequest发起调用到获取结果,这其中除了有页面的执行时间,还混杂较多的额外调用开销。最终,我选择了在一次HTTP请求中,循环调用Server.Execute来执行页面,并统计时间的方式。其实如何选择测试方法,对于二个测试对象还说,都是公平的。只是说:尽量减少一些额外的调用开销,会让测试结果的差异更大,也更明显。 

说明:为了测试代码写起来简单,我使用了MyMVC框架。 
测试用例1:WebFromPage.aspx 
前面介绍了测试背景以及测试方法。现在就来介绍第1个测试用例,它采用了WebForm编程模型中最经典的写法。 
页面代码: 

复制代码代码如下:


<%@ Page Language="C#" CodeFile="WebFromPage.aspx.cs" Inherits="TestPage_WebFromPage" %> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<title>PagePerformanceTest http://www.cnblogs.com/fish-li/</title> 
</head> 
<body> 
<p>This is WebFromPage.aspx</p> 
<asp:Repeater ID="repeater1" runat="server" onitemdatabound="repeater1_ItemDataBound"> 
<ItemTemplate> 
<asp:HyperLink ID="link1" runat="server"></asp:HyperLink><br /> 
</ItemTemplate> 
<FooterTemplate><hr /></FooterTemplate> 
</asp:Repeater> 
<asp:Repeater ID="repeater2" runat="server" onitemdatabound="repeater1_ItemDataBound"> 
<ItemTemplate> 
<asp:HyperLink ID="link1" runat="server"></asp:HyperLink><br /> 
</ItemTemplate> 
<FooterTemplate><hr /></FooterTemplate> 
</asp:Repeater> 
<asp:Repeater ID="repeater3" runat="server" onitemdatabound="repeater1_ItemDataBound"> 
<ItemTemplate> 
<asp:HyperLink ID="link1" runat="server"></asp:HyperLink><br /> 
</ItemTemplate> 
<FooterTemplate><hr /></FooterTemplate> 
</asp:Repeater> 
<asp:Repeater ID="repeater4" runat="server" onitemdatabound="repeater1_ItemDataBound"> 
<ItemTemplate> 
<asp:HyperLink ID="link1" runat="server"></asp:HyperLink><br /> 
</ItemTemplate> 
<FooterTemplate><hr /></FooterTemplate> 
</asp:Repeater> 
<asp:Repeater ID="repeater5" runat="server" onitemdatabound="repeater1_ItemDataBound"> 
<ItemTemplate> 
<asp:HyperLink ID="link1" runat="server"></asp:HyperLink><br /> 
</ItemTemplate> 
<FooterTemplate><hr /></FooterTemplate> 
</asp:Repeater> 

</body> 
</html> 


页面的CodeFile代码: 

复制代码代码如下:


public partial class TestPage_WebFromPage : System.Web.UI.Page 

protected override void OnLoad(EventArgs e) 

base.OnLoad(e); 
repeater1.DataSource = XmlDb.Blogs; 
repeater1.DataBind(); 
repeater2.DataSource = XmlDb.Blogs; 
repeater2.DataBind(); 
repeater3.DataSource = XmlDb.Blogs; 
repeater3.DataBind(); 
repeater4.DataSource = XmlDb.Blogs; 
repeater4.DataBind(); 
repeater5.DataSource = XmlDb.Blogs; 
repeater5.DataBind(); 

protected void repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e) 

if( e.Item.ItemType == ListItemType.Item ) { 
BlogInfo blog = e.Item.DataItem as BlogInfo; 
HyperLink link1 = e.Item.FindControl("link1") as HyperLink; 
link1.NavigateUrl = blog.Href; 
link1.Text = blog.Title; 



测试代码: 

复制代码代码如下:


[Action] 
public object Test1(string callTimes) 

int count = 0; 
int.TryParse(callTimes, out count); 
if( count <= 0 ) 
return count; 
HttpContext context = HttpContext.Current; 
// 先执行一次,排除编译时间 
string html = MyMVC.PageExecutor.Render(context, "/TestPage/WebFromPage.aspx", null); 
Stopwatch watch = Stopwatch.StartNew(); 
for( int i = 0; i < count; i++ ) 
html = MyMVC.PageExecutor.Render(context, "/TestPage/WebFromPage.aspx", null); 
watch.Stop(); 
return watch.Elapsed.ToString(); 


当我测试执行10000次时,耗时:00:00:07.5607229 
回到顶部 
测试用例2:InlinePage.aspx 
与测试用例1不同,测试用例2则完全不使用服务器控件。 
页面代码: 

复制代码代码如下:


<%@ Page Language="C#" %> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<title>PagePerformanceTest http://www.cnblogs.com/fish-li/</title> 
</head> 
<body> 
<p>This is InlinePage.aspx</p> 
<% foreach( BlogInfo b in XmlDb.Blogs ) { %> 
<a href="<%= b.Href %>" target="_blank"><%= b.Title %></a><br /> 
<% } %> 
<hr /> 
<% foreach( BlogInfo b in XmlDb.Blogs ) { %> 
<a href="<%= b.Href %>" target="_blank"><%= b.Title %></a><br /> 
<% } %> 
<hr /> 
<% foreach( BlogInfo b in XmlDb.Blogs ) { %> 
<a href="<%= b.Href %>" target="_blank"><%= b.Title %></a><br /> 
<% } %> 
<hr /> 
<% foreach( BlogInfo b in XmlDb.Blogs ) { %> 
<a href="<%= b.Href %>" target="_blank"><%= b.Title %></a><br /> 
<% } %> 
<hr /> 
<% foreach( BlogInfo b in XmlDb.Blogs ) { %> 
<a href="<%= b.Href %>" target="_blank"><%= b.Title %></a><br /> 
<% } %> 
<hr /> 
</body> 
</html> 


测试代码: 

复制代码代码如下:


[Action] 
public object Test2(string callTimes) 

int count = 0; 
int.TryParse(callTimes, out count); 
if( count <= 0 ) 
return count; 
HttpContext context = HttpContext.Current; 
// 先执行一次,排除编译时间 
string html = MyMVC.PageExecutor.Render(context, "/TestPage/InlinePage.aspx", null); 
Stopwatch watch = Stopwatch.StartNew(); 
for( int i = 0; i < count; i++ ) 
html = MyMVC.PageExecutor.Render(context, "/TestPage/InlinePage.aspx", null); 
watch.Stop(); 
return watch.Elapsed.ToString(); 


当我测试执行10000次时,耗时:00:00:01.2345842 
回到顶部 
分析优化结果1 
测试用例1执行相同次数所花费的时间是测试用例2的6倍,为什么会这样呢? 
为了回答这个问题,我们首先要知道前面二个页面在执行时,它们是如何运行的。 
说到这里,就不得不谈ASP.NET的页面编译方式了。 
ASP.NET的页面编译过程是个复杂的操作,其实我们可以不用关心页面是如何编译的, 
但要知道:页面编译后是什么样的。 
为了能直观地了解页面编译后的样子,我编译了整个网站,并生成到一个DLL文件中,然后使用Reflector.exe来分析这个DLL的源代码。 
将网站编译成一个DLL文件有二个方法: 
1. 安装WebDeployment插件。 
2. 使用我的工具:FishAspnetTool 
本文将使用FishAspnetTool来编译测试网站获得编译后的DLL文件。 
FishAspnetTool是什么? 
FishAspnetTool是我在使用Visual Web Developer 2005时,为了方便编译网站而写的一个小工具。 
注意:下载的是一个工具包,安装后,从开始菜单中运行FishTools\FishAspnetTool即可。 
下面是工具的运行截图。 
ASP.NET页面优化 性能提升8倍的方法
操作方法: 
1. 点击粉色按钮,选择网站路径。 
2. 单选按钮选择第2项。 
3. 点击【发布网站】按钮。 
在编译网站之后,我就可以知道网站在运行时如何运行页面了。 
测试用例1的页面,最后被编译成这样了: 

复制代码代码如下:


namespace ASP 

using System; 
using System.Diagnostics; 
using System.Runtime.CompilerServices; 
using System.Web; 
using System.Web.UI; 
using System.Web.UI.WebControls; 
[CompilerGlobalScope] 
public class testpage_webfrompage_aspx : TestPage_WebFromPage, IHttpHandler 

private static object __fileDependencies; 
private static bool __initialized; 
[DebuggerNonUserCode] 
public testpage_webfrompage_aspx() 

base.AppRelativeVirtualPath = "~/TestPage/WebFromPage.aspx"; 
if (!__initialized) 

string[] virtualFileDependencies = new string[] { "~/TestPage/WebFromPage.aspx", "~/TestPage/WebFromPage.aspx.cs" }; 
__fileDependencies = base.GetWrappedFileDependencies(virtualFileDependencies); 
__initialized = true; 

base.Server.ScriptTimeout = 0x1c9c380; 

[DebuggerNonUserCode] 
private void __BuildControl__control10(Control __ctrl) 

IParserAccessor accessor = __ctrl; 
accessor.AddParsedSubObject(new LiteralControl("<hr />")); 

[DebuggerNonUserCode] 
private void __BuildControl__control11(Control __ctrl) 

IParserAccessor accessor = __ctrl; 
accessor.AddParsedSubObject(new LiteralControl("\r\n\t")); 
HyperLink link = this.__BuildControl__control12(); 
accessor.AddParsedSubObject(link); 
accessor.AddParsedSubObject(new LiteralControl("<br />\r\n")); 

[DebuggerNonUserCode] 
private HyperLink __BuildControl__control12() 

HyperLink link = new HyperLink { 
TemplateControl = this 
}; 
link.ApplyStyleSheetSkin(this); 
link.ID = "link1"; 
return link; 

[DebuggerNonUserCode] 
private void __BuildControl__control13(Control __ctrl) 

IParserAccessor accessor = __ctrl; 
accessor.AddParsedSubObject(new LiteralControl("<hr />")); 

[DebuggerNonUserCode] 
private void __BuildControl__control14(Control __ctrl) 

IParserAccessor accessor = __ctrl; 
accessor.AddParsedSubObject(new LiteralControl("\r\n\t")); 
HyperLink link = this.__BuildControl__control15(); 
accessor.AddParsedSubObject(link); 
accessor.AddParsedSubObject(new LiteralControl("<br />\r\n")); 

[DebuggerNonUserCode] 
private HyperLink __BuildControl__control15() 

HyperLink link = new HyperLink { 
TemplateControl = this 
}; 
link.ApplyStyleSheetSkin(this); 
link.ID = "link1"; 
return link; 

[DebuggerNonUserCode] 
private void __BuildControl__control16(Control __ctrl) 

IParserAccessor accessor = __ctrl; 
accessor.AddParsedSubObject(new LiteralControl("<hr />")); 

[DebuggerNonUserCode] 
private void __BuildControl__control2(Control __ctrl) 

IParserAccessor accessor = __ctrl; 
accessor.AddParsedSubObject(new LiteralControl("\r\n\t")); 
HyperLink link = this.__BuildControl__control3(); 
accessor.AddParsedSubObject(link); 
accessor.AddParsedSubObject(new LiteralControl("<br />\r\n")); 

[DebuggerNonUserCode] 
private HyperLink __BuildControl__control3() 

HyperLink link = new HyperLink { 
TemplateControl = this 
}; 
link.ApplyStyleSheetSkin(this); 
link.ID = "link1"; 
return link; 

[DebuggerNonUserCode] 
private void __BuildControl__control4(Control __ctrl) 

IParserAccessor accessor = __ctrl; 
accessor.AddParsedSubObject(new LiteralControl("<hr />")); 

[DebuggerNonUserCode] 
private void __BuildControl__control5(Control __ctrl) 

IParserAccessor accessor = __ctrl; 
accessor.AddParsedSubObject(new LiteralControl("\r\n\t")); 
HyperLink link = this.__BuildControl__control6(); 
accessor.AddParsedSubObject(link); 
accessor.AddParsedSubObject(new LiteralControl("<br />\r\n")); 

[DebuggerNonUserCode] 
private HyperLink __BuildControl__control6() 

HyperLink link = new HyperLink { 
TemplateControl = this 
}; 
link.ApplyStyleSheetSkin(this); 
link.ID = "link1"; 
return link; 

[DebuggerNonUserCode] 
private void __BuildControl__control7(Control __ctrl) 

IParserAccessor accessor = __ctrl; 
accessor.AddParsedSubObject(new LiteralControl("<hr />")); 

[DebuggerNonUserCode] 
private void __BuildControl__control8(Control __ctrl) 

IParserAccessor accessor = __ctrl; 
accessor.AddParsedSubObject(new LiteralControl("\r\n\t")); 
HyperLink link = this.__BuildControl__control9(); 
accessor.AddParsedSubObject(link); 
accessor.AddParsedSubObject(new LiteralControl("<br />\r\n")); 

[DebuggerNonUserCode] 
private HyperLink __BuildControl__control9() 

HyperLink link = new HyperLink { 
TemplateControl = this 
}; 
link.ApplyStyleSheetSkin(this); 
link.ID = "link1"; 
return link; 

[DebuggerNonUserCode] 
private Repeater __BuildControlrepeater1() 

Repeater repeater = new Repeater(); 
base.repeater1 = repeater; 
repeater.ItemTemplate = new CompiledTemplateBuilder(new BuildTemplateMethod(this.__BuildControl__control2)); 
repeater.FooterTemplate = new CompiledTemplateBuilder(new BuildTemplateMethod(this.__BuildControl__control4)); 
repeater.ID = "repeater1"; 
repeater.ItemDataBound += new RepeaterItemEventHandler(this.repeater1_ItemDataBound); 
return repeater; 

[DebuggerNonUserCode] 
private Repeater __BuildControlrepeater2() 

Repeater repeater = new Repeater(); 
base.repeater2 = repeater; 
repeater.ItemTemplate = new CompiledTemplateBuilder(new BuildTemplateMethod(this.__BuildControl__control5)); 
repeater.FooterTemplate = new CompiledTemplateBuilder(new BuildTemplateMethod(this.__BuildControl__control7)); 
repeater.ID = "repeater2"; 
repeater.ItemDataBound += new RepeaterItemEventHandler(this.repeater1_ItemDataBound); 
return repeater; 

[DebuggerNonUserCode] 
private Repeater __BuildControlrepeater3() 

Repeater repeater = new Repeater(); 
base.repeater3 = repeater; 
repeater.ItemTemplate = new CompiledTemplateBuilder(new BuildTemplateMethod(this.__BuildControl__control8)); 
repeater.FooterTemplate = new CompiledTemplateBuilder(new BuildTemplateMethod(this.__BuildControl__control10)); 
repeater.ID = "repeater3"; 
repeater.ItemDataBound += new RepeaterItemEventHandler(this.repeater1_ItemDataBound); 
return repeater; 

[DebuggerNonUserCode] 
private Repeater __BuildControlrepeater4() 

Repeater repeater = new Repeater(); 
base.repeater4 = repeater; 
repeater.ItemTemplate = new CompiledTemplateBuilder(new BuildTemplateMethod(this.__BuildControl__control11)); 
repeater.FooterTemplate = new CompiledTemplateBuilder(new BuildTemplateMethod(this.__BuildControl__control13)); 
repeater.ID = "repeater4"; 
repeater.ItemDataBound += new RepeaterItemEventHandler(this.repeater1_ItemDataBound); 
return repeater; 

[DebuggerNonUserCode] 
private Repeater __BuildControlrepeater5() 

Repeater repeater = new Repeater(); 
base.repeater5 = repeater; 
repeater.ItemTemplate = new CompiledTemplateBuilder(new BuildTemplateMethod(this.__BuildControl__control14)); 
repeater.FooterTemplate = new CompiledTemplateBuilder(new BuildTemplateMethod(this.__BuildControl__control16)); 
repeater.ID = "repeater5"; 
repeater.ItemDataBound += new RepeaterItemEventHandler(this.repeater1_ItemDataBound); 
return repeater; 

[DebuggerNonUserCode] 
private void __BuildControlTree(testpage_webfrompage_aspx __ctrl) 

__ctrl.EnableViewState = false; 
__ctrl.EnableViewStateMac = false; 
this.InitializeCulture(); 
IParserAccessor accessor = __ctrl; 
accessor.AddParsedSubObject(new LiteralControl("\r\n\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\r\n<head>\r\n <title>PagePerformanceTest http://www.cnblogs.com/fish-li/</title>\r\n</head>\r\n<body>\r\n\r\n<p>This is WebFromPage.aspx</p>\r\n\r\n")); 
Repeater repeater = this.__BuildControlrepeater1(); 
accessor.AddParsedSubObject(repeater); 
accessor.AddParsedSubObject(new LiteralControl("\r\n\r\n")); 
Repeater repeater2 = this.__BuildControlrepeater2(); 
accessor.AddParsedSubObject(repeater2); 
accessor.AddParsedSubObject(new LiteralControl("\r\n\r\n")); 
Repeater repeater3 = this.__BuildControlrepeater3(); 
accessor.AddParsedSubObject(repeater3); 
accessor.AddParsedSubObject(new LiteralControl("\r\n\r\n")); 
Repeater repeater4 = this.__BuildControlrepeater4(); 
accessor.AddParsedSubObject(repeater4); 
accessor.AddParsedSubObject(new LiteralControl("\r\n\r\n")); 
Repeater repeater5 = this.__BuildControlrepeater5(); 
accessor.AddParsedSubObject(repeater5); 
accessor.AddParsedSubObject(new LiteralControl("\r\n\r\n\r\n</body>\r\n</html>\r\n")); 

[DebuggerNonUserCode] 
protected override void FrameworkInitialize() 

base.FrameworkInitialize(); 
this.__BuildControlTree(this); 
base.AddWrappedFileDependencies(__fileDependencies); 
base.Request.ValidateInput(); 

[DebuggerNonUserCode] 
public override int GetTypeHashCode() 

return -781896338; 

[DebuggerNonUserCode] 
public override void ProcessRequest(HttpContext context) 

base.ProcessRequest(context); 

protected override bool SupportAutoEvents 

get 

return false; 




从这个编译结果我们可以看出:页面上的所有文字最后也被包装到LiteralControl中。 
页面中呈现时,就是循环调用每个控件的Render方法来最终生成HTML结果。 
测试用例2的页面被编译成这个样了: 

复制代码代码如下:


namespace ASP 

using System; 
using System.Diagnostics; 
using System.Runtime.CompilerServices; 
using System.Web; 
using System.Web.Profile; 
using System.Web.UI; 
[CompilerGlobalScope] 
public class testpage_inlinepage_aspx : Page, IHttpHandler 

private static object __fileDependencies; 
private static bool __initialized; 
[DebuggerNonUserCode] 
public testpage_inlinepage_aspx() 

base.AppRelativeVirtualPath = "~/TestPage/InlinePage.aspx"; 
if (!__initialized) 

string[] virtualFileDependencies = new string[] { "~/TestPage/InlinePage.aspx" }; 
__fileDependencies = base.GetWrappedFileDependencies(virtualFileDependencies); 
__initialized = true; 

base.Server.ScriptTimeout = 0x1c9c380; 

[DebuggerNonUserCode] 
private void __BuildControlTree(testpage_inlinepage_aspx __ctrl) 

__ctrl.EnableViewState = false; 
__ctrl.EnableViewStateMac = false; 
this.InitializeCulture(); 
__ctrl.SetRenderMethodDelegate(new RenderMethod(this.__Render__control1)); 

private void __Render__control1(HtmlTextWriter __w, Control parameterContainer) 

__w.Write("\r\n\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\r\n<head>\r\n <title>PagePerformanceTest http://www.cnblogs.com/fish-li/</title>\r\n</head>\r\n<body>\r\n\r\n<p>This is InlinePage.aspx</p>\r\n\r\n"); 
foreach (BlogInfo info in XmlDb.Blogs) 

__w.Write("\r\n\t<a href=\""); 
__w.Write(info.Href); 
__w.Write("\" target=\"_blank\">"); 
__w.Write(info.Title); 
__w.Write("</a><br />\r\n"); 

__w.Write("\r\n<hr />\r\n\r\n"); 
foreach (BlogInfo info2 in XmlDb.Blogs) 

__w.Write("\r\n\t<a href=\""); 
__w.Write(info2.Href); 
__w.Write("\" target=\"_blank\">"); 
__w.Write(info2.Title); 
__w.Write("</a><br />\r\n"); 

__w.Write("\r\n<hr />\r\n\r\n"); 
foreach (BlogInfo info3 in XmlDb.Blogs) 

__w.Write("\r\n\t<a href=\""); 
__w.Write(info3.Href); 
__w.Write("\" target=\"_blank\">"); 
__w.Write(info3.Title); 
__w.Write("</a><br />\r\n"); 

__w.Write("\r\n<hr />\r\n\r\n"); 
foreach (BlogInfo info4 in XmlDb.Blogs) 

__w.Write("\r\n\t<a href=\""); 
__w.Write(info4.Href); 
__w.Write("\" target=\"_blank\">"); 
__w.Write(info4.Title); 
__w.Write("</a><br />\r\n"); 

__w.Write("\r\n<hr />\r\n\r\n"); 
foreach (BlogInfo info5 in XmlDb.Blogs) 

__w.Write("\r\n\t<a href=\""); 
__w.Write(info5.Href); 
__w.Write("\" target=\"_blank\">"); 
__w.Write(info5.Title); 
__w.Write("</a><br />\r\n"); 

__w.Write("\r\n<hr />\r\n\r\n</body>\r\n</html>\r\n"); 

[DebuggerNonUserCode] 
protected override void FrameworkInitialize() 

base.FrameworkInitialize(); 
this.__BuildControlTree(this); 
base.AddWrappedFileDependencies(__fileDependencies); 
base.Request.ValidateInput(); 

[DebuggerNonUserCode] 
public override int GetTypeHashCode() 

return -1307842476; 

[DebuggerNonUserCode] 
public override void ProcessRequest(HttpContext context) 

base.ProcessRequest(context); 

protected global_asax ApplicationInstance 

get 

return (global_asax) this.Context.ApplicationInstance; 


protected DefaultProfile Profile 

get 

return (DefaultProfile) this.Context.Profile; 


protected override bool SupportAutoEvents 

get 

return false; 




请注意下面这段关键的代码:它们实在太重要了。 

复制代码代码如下:


private void __BuildControlTree(testpage_inlinepage_aspx __ctrl) 

// ....... 
__ctrl.SetRenderMethodDelegate(new RenderMethod(this.__Render__control1)); 

private void __Render__control1(HtmlTextWriter __w, Control parameterContainer) 


testpage_inlinepage_aspx与testpage_webfrompage_aspx的编译结果完全不同。 
最大的差别在testpage_inlinepage_aspx有个方法:__Render__control1 
在这个方法中,页面的内容将直接被写入到HtmlTextWriter对象中。 
还有一点我要告诉您:每个Control的输出最后还是要将自己的显示代码写入到HtmlTextWriter对象中。 
因此,从这里就可以明显地看出testpage_inlinepage_aspx的执行速度要快很多, 
因为: 
1. 它没有服务器控件。 
2. 不再需要递归循环每个控件,每个控件的生命周期的调用开销也节省了。 
3. 不用再创建那些服务器控件对象,GC的压力会小很多。 
4. 输出方式更高效。 
通过前面的分析,您现在明白了为什么二个页面的执行速度相差6倍了原因了吧。 
好像还有一点没有解释:__Render__control1如何被调用? 
我们都知道:以ASPX页面为代表的WebForm编程模型在执行时有一个特点:递归循环每个控件。 
页面是在Render阶段输出的,页面的HTML代码也是在那个阶段输出到HtmlTextWriter对象中的。 
可是,testpage_inlinepage_aspx没有任何控件,那又该如何递归呢? 
的确,很多书籍以及技术资料都是说:在Render阶段会递归循环每个控件并调用控件的Render方法。 
其实这种说法是不准确的。Control的Render方法在运行时,会调用下面这个方法: 

复制代码代码如下:


internal void RenderChildrenInternal(HtmlTextWriter writer, ICollection children) 

if ((this.RareFields != null) && (this.RareFields.RenderMethod != null)) 

writer.BeginRender(); 
this.RareFields.RenderMethod(writer, this); 
writer.EndRender(); 

else if (children != null) 

foreach (Control control in children) 

control.RenderControl(writer); 



这段代码中,有个重要的if...else...判断,简单说来,就是说要不要调用前面所说的__Render__control1方法。 
从代码可以看出,如果是进入了if语句块,则不用递归循环每个控件并调用控件的Render方法。 
那么如何能进入if语句块呢? 
答案是:调用Control.SetRenderMethodDelegate方法。 
testpage_inlinepage_aspx的编译生成代码中就有这个调用。 
对于这个方法,MSDN的解释很含糊: 
此 API 支持 .NET Framework 基础结构,不适合在代码中直接使用。 
分配事件处理程序委托,以将服务器控件及其内容呈现到父控件中。 
回到顶部 
测试用例3:InlineUserControl.ascx 
在测试用例3中,我将页面中用于输出的代码移到一个用户控件中。 
用户控件的代码此处省略,与测试用例2的代码基本上一致。编译后的结果也基本差不多。 
测试代码: 

复制代码代码如下:


[Action] 
public object Test3(string callTimes) 

int count = 0; 
int.TryParse(callTimes, out count); 
if( count <= 0 ) 
return count; 
// 先执行一次,排除编译时间 
string html = MyMVC.UcExecutor.Render("/UserControl/InlineUserControl.ascx", null); 
Stopwatch watch = Stopwatch.StartNew(); 
for( int i = 0; i < count; i++ ) 
html = MyMVC.UcExecutor.Render("/UserControl/InlineUserControl.ascx", null); 
watch.Stop(); 
return watch.Elapsed.ToString(); 


当我测试执行10000次时,耗时:00:00:00.9132738 
又快了一点。 
说明:为了这次的性能优化,MyMVC框架也做了一点调整。如果您以前下载过这个框架,请重新下载。 
回到顶部 
分析优化结果2 
经过前面的分析,我们知道:不创建服务器控件对象以及不调用它们的生命周期,可以让页面的执行速度快很多。 
有没有再想像一下:页面也有生命周期啊,而且生命周期的步骤更长,省略它,会不会更快呢? 
不过,Render方法并不是个public方法,我们还不能直接调用,但可以调用RenderControl方法来实现这一过程。 
由于跳过页面的生命周期,任何服务器控件都不能使用了,包括母板页。所以我选择将前面测试用的那段代码移到用户控件中,然后将用户控件加载到Page中来测试。 
测试用例3与测试用例2相比,在测试过程中,由于跳过了页面的生命周期,因此速度也就更快了。 
嗯,基本上,就是这个简单的原因吧。 
由于这种方法没有任何的控件生命周期,因此速度是最快的。 
经过这一系列的优化,页面的执行时间最终由 00:00:07.5607229 减少到 00:00:00.9132738 

延伸 · 阅读

精彩推荐