`
luotuoass
  • 浏览: 639202 次
文章分类
社区版块
存档分类
最新评论

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

阅读更多

今天与大家分享:一种优化页面执行速度的方法。
采用这个方法,可以使用页面的执行速度获得【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。

本文将使用FishAspnetTool来编译测试网站获得编译后的DLL文件。

FishAspnetTool是什么?
FishAspnetTool是我在使用Visual Web Developer 2005时,为了方便编译网站而写的一个小工具。
下载地址:http://www.cnblogs.com/fish-li/archive/2011/10/30/2229497.html
注意:下载的是一个工具包,安装后,从开始菜单中运行FishTools\FishAspnetTool即可。
下面是工具的运行截图。

操作方法:
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

再次申明:测试结果也仅仅只是一个参考数字。
事实上,使用服务器控件产生的对象涉及到GC的回收以及内存占用的影响也是不可忽视的。

0
0
分享到:
评论

相关推荐

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

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

    Asp.NET性能优化.rar

    根据经验的总结,让我们来看看十个能帮助你提升你的应用程序性能的经验,我将按将它们提升效率的多少从大到小小依次说明,一、返回多个数据集,二、对数据进行分页,三、连接池 ,四、 ASP.NET缓存API ,五、 预请求缓存,...

    asp.net知识库

    在ASP.NET页面中推荐使用覆写(Override)而不是事件处理(Event Handler) 常用编码工具类,支持base64,md5,des,crc32 也谈谈技术面试 在C#里把ArrayList转换为Array 或 把Array转换为ArrayList C# 2.0 在.NET 2.0中...

    ASP.NET 2.0技术内幕

    1.1.4 ASP.NET页面的结构 8 1.2 ASP.NET组件模型 11 1.2.1 组件交互模型 11 1.2.2 runat属性 12 1.2.3 ASP.NET服务器控件 15 1.3 ASP.NET开发栈 16 1.3.1 表示层 16 ...

    ASP.NET性能优化之让浏览器缓存动态网页的方法

    本篇要介绍的浏览器缓存则是针对单个用户,让浏览器在我们的控制下彻底不持续访问服务器上的动态内容,也就是我们要让浏览器变成我们的缓存机制中的一部分,在某些特定的场景下最大化地提升ASP.NET站点的性能。...

    庖丁解牛:纵向切入ASP.NET 3.5控件和组件开发技术

    6.9 页面状态性能优化策略 238 6.9.1 存储位置优化——把视图状态信息保存在服务端而非客户端 238 6.9.2 体积优化——压缩视图状态数据 240 6.9.3 分块存储视图状态数据 243 6.10 视图状态和控件状态的总结 243 ...

    asp.net博客系统

    本博客采用ASP.NET2.0 c# + MSSQL2005 + XML + jquery + anthem.net开发,暂时为单用户版本,以下简单介绍... 程序基于类似petshop的标准三层架构,方便维护与扩展(不过暂时只支持MSSQL数据库-_-||) 前台使用...

    ASP.NET.2.0技术内幕.part3

    本书将引导您掌握基础知识,并逐步提升编程技能,是钻研ASP.NET 2.0核心编程主题的重要参考书。本书适合有经验的开发人员,也适合想掌握最新编程技能的读者。书中贯穿着专家的悉心指导、通俗易懂的编程指令和丰富的...

    ASP.NET.2.0技术内幕.part2

    本书将引导您掌握基础知识,并逐步提升编程技能,是钻研ASP.NET 2.0核心编程主题的重要参考书。本书适合有经验的开发人员,也适合想掌握最新编程技能的读者。书中贯穿着专家的悉心指导、通俗易懂的编程指令和丰富的...

    ASP.NET.2.0技术内幕.part1

    本书将引导您掌握基础知识,并逐步提升编程技能,是钻研ASP.NET 2.0核心编程主题的重要参考书。本书适合有经验的开发人员,也适合想掌握最新编程技能的读者。书中贯穿着专家的悉心指导、通俗易懂的编程指令和丰富的...

    史上最好传智播客就业班.net培训教程60G 不下会后悔

    由于ASP.Net MVC解决了ASP.Net WebForm的很多缺点,非常适合大型、中型项目的开发,一经推出就受到了.Net开发社区的追捧,很多.Net开发人员的职位要求中都提到了MVC,可见掌握ASP.Net MVC技术必将提升自己就业的砝码...

    庖丁解牛纵向切入ASP.NET 3.5控件和组件开发技术.pdf

    如果扎实地掌握了asp.net控件的运行机制,开发一个页面级的asp.net应用程序会变得非常简单。本书宗旨就是让开发人员真正理解asp.net技术,帮助开发人员提高asp.net开发的技术水平。学完本书后您不仅能够掌握控件开发...

    庖丁解牛 纵向切入ASP.NET 3.5控件和组件开发 part1

    6.9 页面状态性能优化策略 238 6.9.1 存储位置优化——把视图状态信息保存在服务端而非客户端 238 6.9.2 体积优化——压缩视图状态数据 240 6.9.3 分块存储视图状态数据 243 6.10 视图状态和控件状态的总结 243 ...

    庖丁解牛 纵向切入ASP.NET 3.5控件和组件开发 part2

    6.9 页面状态性能优化策略 238 6.9.1 存储位置优化——把视图状态信息保存在服务端而非客户端 238 6.9.2 体积优化——压缩视图状态数据 240 6.9.3 分块存储视图状态数据 243 6.10 视图状态和控件状态的总结 243 ...

    亮剑.NET深入体验与实战精要2

    5.4.3 ASP.NET页面级别的事务 230 5.4.4 企业级服务COM+事务 231 5.4.5 System.Transactions 事务处理 236 5.5 Oracle开发常见问题 240 5.5.1 Oracle和SQL Server的常用函数对比 240 5.5.2 Oracle和SQL Server的...

    一款基于asp.net技术业内领先的Web2.0社区平台。

    一款基于asp.net技术业内领先的Web2.0社区平台。...这次优化不仅可以使SpaceBuilder轻松的部署到几十台服务器,而且对于用单台服务器部署SpaceBuilder的站点也同样可以明显感觉到页面响应速度的提升及内存占用的减少。

    计算机专业毕设asp.net选课系统毕业设计全套参考资料.rar

    该资源包以ASP.NET技术为基础,构建了一个功能完善、操作简便的选课系统,旨在帮助毕业生顺利完成毕业设计任务,同时提升他们的实际开发能力和技术水平。 该资源包包含了以下几个方面的内容: 1. 需求分析文档:...

    亮剑.NET深入体验与实战精要3

    5.4.3 ASP.NET页面级别的事务 230 5.4.4 企业级服务COM+事务 231 5.4.5 System.Transactions 事务处理 236 5.5 Oracle开发常见问题 240 5.5.1 Oracle和SQL Server的常用函数对比 240 5.5.2 Oracle和SQL Server的...

Global site tag (gtag.js) - Google Analytics