ASP.NET页面优化性能提升方法记录

今天与大家分享:一种优化页面执行速度的方法。
采用这个方法,可以使用页面的执行速度获得【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的回收以及内存占用的影响也是不可忽视的。

ASP.NET页面优化性能提升方法记录,布布扣,bubuko.com

时间: 2024-10-27 05:13:51

ASP.NET页面优化性能提升方法记录的相关文章

提高ASP.NET页面载入速度的方法

前言 本文是我对ASP.NET页面载入速度提高的一些做法,这些做法分为以下部分: 目录 1.采用 HTTP Module 控制页面的生命周期. 2.自定义Response.Filter得到输出流stream生成动态页面的静态内容(磁盘缓存). 3.页面GZIP压缩. 4.OutputCache 编程方式输出页面缓存. 5.删除页面空白字符串.(类似Google) 6.完全删除ViewState. 7.删除服务器控件生成的垃圾NamingContainer. 8.使用计划任务按时生成页面.(本文不

页面优化有哪些方法?

一.减少操作量 尽量减少 HTTP 请求1) 合并文件,比如把多个 CSS 文件合成一个: 2) CSS Sprites 利用 CSS background 相关元素进行背景图绝对定位: 不要在 HTML 中使用缩放图片缩放图片并没有减少图片的容量,只是控制了图片的大小. Image压缩使用工具对图片进行压缩,保证质量的同时减少了图片的大小. 减少对DOM的操作减少对DOM的操作,减少页面的重绘. 二.提前做加载操作 对域名进行预解析例如京东的做法<link rel="dns-prefet

ASP.NET页面刷新的实现方法总结

先看看ASP.NET页面刷新的实现方法: 第一: C#代码   private void Button1_Click( object sender, System.EventArgs e ) { Response.Redirect( Request.Url.ToString( ) ); } 第二: C#代码   private void Button2_Click( object sender, System.EventArgs e ) { Response.Write( " < scri

asp.net DataTable导出 excel的方法记录(第三方)

官网:http://npoi.codeplex.com/ 简单应用,主要是可以实现我们想要的简单效果,呵呵 需要引入dll,可以在官网下载,也可在下面下载 C#代码   protected void getExcel(DataTable dt) { NPOI.HSSF.UserModel.HSSFWorkbook book = new NPOI.HSSF.UserModel.HSSFWorkbook(); NPOI.SS.UserModel.Sheet sheet = book.CreateSh

asp.net页面跳转的方法

1.超链接 2.response.Redirect(“UrlString”) 过程: 浏览器操作--服务器编译--发回页面--浏览器按新URl发出请求--服务器响应新URl请求--编译新页面--发回浏览器 3.Server.Transfer(“UrlString”) 4.PostBackUrl 凡是具有IButtonControl借口的控件都有PostBackUrl属性,用来定义提交至那个页面地址.(可以是本站也可以使外站) 这种方法的跳转,目标页面可以调用原页面中控件的值. asp.net页面

ASP.NET中常用的优化性能的方法

1. 数据库访问性能优化  数据库的连接和关闭 访问数据库资源需要创建连接.打开连接和关闭连接几个操作.这些过程需要多次与数据库交换信息以通过身份验证,比较耗费服务器资源.ASP.NET中提供了连接池(Connection Pool)改善打开和关闭数据库对性能的影响.系统将用户的数据库连接放在连接池中,需要时取出,关闭时收回连接,等待下一次的连接请求. 连接池的大小是有限的,如果在连接池达到最大限度后仍要求创建连接,必然大大影响性能.因此,在建立数据库连接后只有在真正需要操作时才打开连接,使用完

8 种提升 ASP.NET Web API 性能的方法

ASP.NET Web API 是非常棒的技术.编写 Web API 十分容易,以致于很多开发者没有在应用程序结构设计上花时间来获得很好的执行性能. 在本文中,我将介绍8项提高 ASP.NET Web API 性能的技术. 1) 使用最快的 JSON 序列化工具 JSON 的序列化对整个 ASP.NET Web API 的性能有着关键性的影响. 在我的一个项目里,我从 JSON.NET 序列化工具转到了 ServiceStack.Text 有一年半了. 我测量过,Web API 的性能提升了20

8 种提升ASP.NET Web API性能的方法

ASP.NET Web API 是非常棒的技术.编写 Web API 十分容易,以致于很多开发者没有在应用程序结构设计上花时间来获得很好的执行性能. 在本文中,我将介绍8项提高 ASP.NET Web API 性能的技术. 1) 使用最快的 JSON 序列化工具 JSON 的序列化对整个 ASP.NET Web API 的性能有着关键性的影响. 在我的一个项目里,我从 JSON.NET 序列化工具转到了 ServiceStack.Text 有一年半了. 我测量过,Web API 的性能提升了20

[转载]8 种提升 ASP.NET Web API 性能的方法

http://www.oschina.net/translate/8-ways-improve-asp-net-web-api-performance 英文原文:8 ways to improve ASP.NET Web API performance ASP.NET Web API 是非常棒的技术.编写 Web API 十分容易,以致于很多开发者没有在应用程序结构设计上花时间来获得很好的执行性能. 在本文中,我将介绍8项提高 ASP.NET Web API 性能的技术. 1) 使用最快的 JS