今天与大家分享:一种优化页面执行速度的方法。
采用这个方法,可以使用页面的执行速度获得【8倍】的提升效果。
为了让您对优化的效果有个直观的了解,我准备了下面的测试结果截图:
测试环境:
1. Windows Server 2003 SP2
2. Viaual Studio 2008,使用自带的WebDev.WebServer.EXE运行网站程序。
3. (ThinkPad SL510):Core2 T6670 2.2GHz, 4G内存
二个红框中的数字反映了优化前后的执行时间。
数字表明:优化前后,执行时间有了8倍多的差别。
本文的测试结果也仅仅只是一个参考数字,这个结果也只是根据我所设计的测试页面得出的。
优化的过程中,如果不使用服务器控件,那么给GC减少的压力其实也是无法测试到的。
在测试过程中,我还发现测试结果并不是很稳定,因此截图具有一定的偶然性。
测试页面或许在某些方面存在一些片面性,因此,结果仅供参考。
测试背景
看过了优化结果,再来介绍一下:这个测试到底是在测试什么东西?
现在有很多做ASP.NET的开发人员,应该都是从ASP.NET的WebForm编程模型开始学习的。
大家都很喜欢用服务器控件,不管输出什么,都会使用服务器控件。
有时候为了让页面呈现干净的HTML代码,有些人会选择使用Repeater,Literal这类简单的服务器控件。
或许有些人认为:我已不使用GridView这样强大复杂的控件,页面执行速度已经很快了。
真是这样吗?
今天测试的起点就从使用简单的服务器开始,我会分二次对它做一系列的性能优化。
最终就是上图中的3个结果,它们反映了二次优化的改进过程。
在继续介绍之前,有一点我想有必要说明一下:
优化的过程涉及到ASP.NET服务器控件的使用,测试结果也仅仅只是一个参考数字。
如果您认为您的开发工作非常依赖于服务器控件的使用,
那么测试结果对您来说其实是无意义的,请不要在意这个结果。
测试方法
在这次优化过程中,我并没有设计很复杂的测试页面,而是一个很简单的测试页面,页面显示效果如下:
这个页面其实就是显示了一堆超链接,它们来自于我的博客侧边栏的【推荐排行榜】,总共有20条记录,
我让页面重复5次输出,也就是生成了100个超链接。
测试的数据是这样获取的:
我复制了我的博客侧边栏的【推荐排行榜】的那段HTML代码,保存到一个文件中:
然后,网站在初始化时,从这段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 { Title = a. Value, Href = a. Attribute("href" ). Value }). ToList();
}
}
测试时,就把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。
在编译网站之后,我就可以知道网站在运行时如何运行页面了。
测试用例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
再次申明:测试结果也仅仅只是一个参考数字。
事实上,使用服务器控件产生的对象涉及到GC的回收以及内存占用的影响也是不可忽视的。
点击此处下载示例代码
如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐 】 按钮。
如果,您希望更容易地发现我的新博客,不妨点击一下右下角的【关注 Fish Li 】 。
因为,我的写作热情也离不开您的肯定支持。
感谢您的阅读,如果您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是Fish Li 。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂