• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    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服务器控件的使用,测试结果也仅仅只是一个参考数字。
    如果您认为您的开发工作非常依赖于服务器控件的使用,
    那么测试结果对您来说其实是无意义的,请不要在意这个结果。
    测试方法
    在这次优化过程中,我并没有设计很复杂的测试页面,而是一个很简单的测试页面,页面显示效果如下:

    这个页面其实就是显示了一堆超链接,它们来自于我的博客侧边栏的【推荐排行榜】,总共有20条记录,我让页面重复5次输出,也就是生成了100个超链接。

    测试的数据是这样获取的:
    我复制了我的博客侧边栏的【推荐排行榜】的那段HTML代码,保存到一个文件中:

    然后,网站在初始化时,从这段HTML代码提取链接地址以及显示文字,保存到一个BlogInfo的列表中,代码如下:
    复制代码 代码如下:

    public class BlogInfo
    {
    public string Title;
    public string Href;
    }

    public static class XmlDb
    {
    public static ListBlogInfo> 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时,为了方便编译网站而写的一个小工具。
    下载地址:https://www.jb51.net/article/29875.htm
    注意:下载的是一个工具包,安装后,从开始菜单中运行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\nhtml xmlns=\"http://www.w3.org/1999/xhtml\">\r\nhead>\r\n title>PagePerformanceTest http://www.cnblogs.com/fish-li//title>\r\n/head>\r\nbody>\r\n\r\np>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\nhtml xmlns=\"http://www.w3.org/1999/xhtml\">\r\nhead>\r\n title>PagePerformanceTest http://www.cnblogs.com/fish-li//title>\r\n/head>\r\nbody>\r\n\r\np>This is InlinePage.aspx/p>\r\n\r\n");
    foreach (BlogInfo info in XmlDb.Blogs)
    {
    __w.Write("\r\n\ta href=\"");
    __w.Write(info.Href);
    __w.Write("\" target=\"_blank\">");
    __w.Write(info.Title);
    __w.Write("/a>br />\r\n");
    }
    __w.Write("\r\nhr />\r\n\r\n");
    foreach (BlogInfo info2 in XmlDb.Blogs)
    {
    __w.Write("\r\n\ta href=\"");
    __w.Write(info2.Href);
    __w.Write("\" target=\"_blank\">");
    __w.Write(info2.Title);
    __w.Write("/a>br />\r\n");
    }
    __w.Write("\r\nhr />\r\n\r\n");
    foreach (BlogInfo info3 in XmlDb.Blogs)
    {
    __w.Write("\r\n\ta href=\"");
    __w.Write(info3.Href);
    __w.Write("\" target=\"_blank\">");
    __w.Write(info3.Title);
    __w.Write("/a>br />\r\n");
    }
    __w.Write("\r\nhr />\r\n\r\n");
    foreach (BlogInfo info4 in XmlDb.Blogs)
    {
    __w.Write("\r\n\ta href=\"");
    __w.Write(info4.Href);
    __w.Write("\" target=\"_blank\">");
    __w.Write(info4.Title);
    __w.Write("/a>br />\r\n");
    }
    __w.Write("\r\nhr />\r\n\r\n");
    foreach (BlogInfo info5 in XmlDb.Blogs)
    {
    __w.Write("\r\n\ta href=\"");
    __w.Write(info5.Href);
    __w.Write("\" target=\"_blank\">");
    __w.Write(info5.Title);
    __w.Write("/a>br />\r\n");
    }
    __w.Write("\r\nhr />\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

    点击此处下载示例代码
    您可能感兴趣的文章:
    • ASP.NET 高性能分页代码
    • ASP.NET(C#) String, StringBuilder 与 StringWriter性能比较
    • asp.net 程序性能优化的七个方面 (c#(或vb.net)程序改进)
    • ASP.NET Session会导致的性能问题
    • 增加asp.net应用程序性能的20种方法(简单有效)
    • ASP.NET 提高首页性能的十大做法
    • ASP.NET性能优化小结(ASP.NETC#)
    • 打造基于jQuery的高性能TreeView(asp.net)
    • 分享提高ASP.NET Web应用性能的技巧
    上一篇:当年学习ADO.NET的笔记
    下一篇:asp.net 专业源代码打包工具
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

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