Performance Tips for ASP.NET Applications

17 באוגוסט 2008

I collect some important performance tips for ASP.NET Applications and decide to write a post on it, because I think this topic is very important for ASP.NET developers.


Cache Aggressively:


There are several features and tools in ASP that you can make use of to gain performance.


Output Caching – Specified using the <@% OutputCache %> directive.



  • Duration—Time item exists in the cache
  • VaryByParam—Varies cache entries by Get/Post params
  • VaryByHeader—Varies cache entries by Http header
  • VaryByCustom—Varies cache entries by browser
  • Override to vary by whatever you want:


    • Fragment Caching—When it is not possible to store an entire page (privacy, personalization, dynamic content), you can use fragment caching to store parts of it for quicker retrieval later.

      a) VaryByControl—Varies the cached items by values of a control


    • Cache API—Provides extremely fine granularity for caching by keeping a hashtable of cached objects in memory (System.web.UI.caching). It also:

      a) Includes Dependencies (key, file, time)


      b) Automatically expires unused items


      c) Supports Callbacks


Use Session State Only If You Need To:


ASP.NET Session mechanism is on by default, you pay the cost in memory even if you don't use it. If you're not using Session State, turn it off and save yourself the overhead by adding <@% EnabledSessionState = false %> to your asp or disabled it on the web.config. For pages that only read session state, you can choose EnabledSessionState=readonly. This carries less overhead than full read/write session state, and is useful when you need only part of the functionality and don't want to pay for the write capabilities.


Use View State Only If You Need To:


ASP.NET ViewState mechanism is on by default, You will pay on traffic network (The ViewState data forward to server on each round-trip) and server performance, memory, because of serializing and deserializing of the ViewState data. I recommended to turn the ViewState mechanism off on the web.config and use it only on pages that need this feature. Disabled in web.config example:



<pages enableViewState="false" enableViewStateMac="false"/>


Avoid STA COM:


Apartment COM is designed to deal with threading in unmanaged environments. There are two kinds of Apartment COM: single-threaded and multithreaded. MTA COM is designed to handle multithreading, whereas STA COM relies on the messaging system to serialize thread requests. The managed world is free-threaded, and using Single Threaded Apartment COM requires that all unmanaged threads essentially share a single thread for interop. This results in a massive performance hit, and should be avoided whenever possible. If you can't port the Apartment COM object to the managed world, use <@%AspCompat = "true" %> for pages that use them. For a more detailed explanation of STA COM, see the MSDN Library.


Remove Unnecessary Http Modules:


Depending on the features used, remove unused or unnecessary http modules from the pipeline. Reclaiming the added memory and wasted cycles can provide you with a small speed boost. The default modules are defined in machine.config file:


<httpModules>


  <add name="OutputCache" type="System.Web.Caching.OutputCacheModule" />


  <add name="Session" type="System.Web.SessionState.SessionStateModule" />


  <add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" />


  <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />


  <add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule" />


  <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />


  <add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule" />


  <add name="ErrorHandlerModule" type="System.Web.Mobile.ErrorHandlerModule, System.Web.Mobile, Version=1.0.5000.0,   Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />


</httpModules>


 


You can remove these default modules from your Web application by adding <remove> nodes in your site's web.config. For example:


<httpModules>


  <remove name="Session" />


  <remove name="WindowsAuthentication" />


  <remove name="PassportAuthentication" />


  <remove name="AnonymousIdentification" />


  <remove name="UrlAuthorization" />


  <remove name="FileAuthorization" />


</httpModules>


 


Avoid the Autoeventwireup Feature:


 


Instead of relying on autoeventwireup, override the events from Page. For example, instead of writing a Page_Load() method, try overloading the public void OnLoad() method. This allows the run time from having to do a CreateDelegate() for every page.


You can disabled autoeventwireup on web.config for example:



<pages autoEventWireup="false"/>


or You can do it directly on each page:


<%@ Page Language="C#" Codebehind="Default.aspx.cs" AutoEventWireup="false" %>


ASP.NET Process Configuration Optimization:


ASP.NET Process Model configuration defines some process level properties like how many number of threads ASP.NET uses, how long it blocks a thread before timing out, how many requests to keep waiting for IO works to complete and so on. The default is in many cases too limiting. Nowadays hardware has become quite cheap and dual core with gigabyte RAM servers have become a very common choice. So, the process model configuration can be tweaked to make ASP.NET process consume more system resources and provide better scalability from each server.


A regular ASP.NET installation will create machine.config with the following configuration:


<system.web>


  <processModel autoConfig="true" />


You need to tweak this auto configuration and use some specific values for different attributes in order to customize the way ASP.NET worker process works. Microsoft Recommendations:





  • Set the values of the maxWorkerThreads parameter and the maxIoThreads parameter to 100.


  • Set the values of the minFreeThreads parameter to 88*N and the minLocalRequestFreeThreads parameter to76*N.


  • Set the value of minWorkerThreads to 50. Remember, minWorkerThreads is not in the configuration file by default. You must add it.

More information can be found on my previous post: Tune your .NET Application that suffer from contention, poor performance, and deadlocks


 


Besides the processModel, there's another very important section with the system.net where you can specify the maximum number of outbound requests that can be made to a single IP.


Default is 2, which is just too low. This means you cannot make more than 2 simultaneous connections to an IP from your Web application. Sites that fetch external content a lot suffer from congestion due to the default setting.


Microsoft Recommendation:





  • Set the value of the maxconnection parameter to 12*N (where N is the number of CPUs that you have).

Kernel Caching (Only with IIS 6.0):


If you are using IIS 6.0, there is a nice little feature called kernel caching that does not require any code changes to ASP.NET. When a request is output-cached by ASP.NET, the IIS kernel cache receives a copy of the cached data. When a request comes from the network driver, a kernel-level driver (no context switch to user mode) receives the request, and if cached, flushes the cached data to the response, and completes execution.


The following default setting in the Machine.config file ensures that dynamically generated ASP.NET pages can use kernel mode caching, subject to the requirements listed below.


Dynamically generated ASP.NET pages are automatically cached subject to the following restrictions:


·          Pages must be retrieved by using HTTP GET requests. Responses to HTTP POST requests are not cached in the kernel.


·         Query strings are ignored when responses are cached. If you want a request for http://abc.com/myapp.aspx?id=1234 to be cached in the kernel, all requests for http://abc.com/myapp.aspx are served from the cache, regardless of the query string.


·          Pages must have an expiration policy. In other words, the pages must have an Expires header.


·          Pages must not have VaryByParams.


·          Pages must not have VaryByHeaders.


·         The page must not have security restrictions. In other words, the request must be anonymous and not require authentication. The HTTP.sys driver only caches anonymous responses.


·         There must be no filters configured for the W3wp.exe file instance that are unaware of the kernel cache.


Trim Your page Size:


Use script includes for any static scripts in your page to enable the client to cache these scripts for subsequent requests. The following script element shows how to do this.


<script language=jscript src="scripts\myscript.js">


Remove characters such as tabs and spaces that create white space before you send a response to the client. Removing white spaces can dramatically reduce the size of your pages. The following sample table contains white spaces.


// with white space
<table>
  <tr>
    <td>hello</td>
    <td>world</td>
   </tr>
</table>


// without white space
<table>
<tr><td>hello</td><td>world</td></tr>
</table>


In an Internet scenario that involves slow clients, removing white space can increase response times dramatically.




  • Limit the use of graphics, and consider using compressed graphics.


  • Consider using cascading style sheets to avoid sending the same formatting directives to the client repeatedly.


  • Avoid long control names; especially ones that are repeated in a DataGrid or Repeater control. Control names are used to generate unique HTML ID names. A 10-character control name can easily turn into 30 to 40 characters when it is used inside nested controls that are repeated.

Using Page.IsPostBack to Minimize Redundant Processing:


Use the Page.IsPostBack property to ensure that you only perform page initialization logic when a page is first loaded and not in response to client postbacks. The following code fragment shows how to use the Page.IsPostBack property.


Using Server.Transfer instead of Response.Redirect:


Response.Redirect sends a metatag to the client that makes the client send a new request to the server by using the new URL. Server.Transfer avoids this indirection by making a server-side call.


When you use Response.Redirect, ensure you use the overloaded method that accepts a Boolean second parameter, and pass a value of false to ensure an internal exception is not raised.



Response.Redirect("Page.aspx", false);



Avoid Using Page.DataBind, instead call data bind on specific control:


Calling Page.DataBind invokes the page-level method. The page-level method in turn calls the DataBind method of every control on the page that supports data binding. Instead of calling the page-level DataBind, call DataBind on specific controls. Both approaches are shown in the following examples.


The following line calls the page level DataBind. The page level DataBind in turn recursively calls DataBind on each control.


DataBind();


The following line calls DataBind on the specific control.


yourServerControl.DataBind();


Minimize Calls to DataBinder.Eval:


The DataBinder.Eval method uses reflection to evaluate the arguments that are passed in and to return the results. If you have a table that has 100 rows and 10 columns, you call DataBinder.Eval 1,000 times if you use DataBinder.Eval on each column. Your choice to use DataBinder.Eval is multiplied 1,000 times in this scenario. Limiting the use of DataBinder.Eval during data binding operations significantly improves page performance. Consider the following ItemTemplate element within a Repeater control using DataBinder.Eval.

<ItemTemplate>
  <tr>
    <td><%# ((DataRowView)Container.DataItem)["field1"] %></td>
    <td><%# ((DataRowView)Container.DataItem)["field2"] %></td>
  </tr>
</ItemTemplate>

There are alternatives to using DataBinder.Eval in this scenario. The alternatives include the following:Use explicit casting. Using explicit casting offers better performance by avoiding the cost of reflection. Cast the Container.DataItem as a DataRowView.

<ItemTemplate>
  <tr>
    <td><%# ((DataRowView)Container.DataItem)["field1"] %></td>
    <td><%# ((DataRowView)Container.DataItem)["field2"] %></td>
  </tr>
</ItemTemplate>

You can gain even better performance with explicit casting if you use a DataReader to bind your control and use the specialized methods to retrieve your data. Cast the Container.DataItem as a DbDataRecord.

<ItemTemplate>
  <tr>
     <td><%# ((DbDataRecord)Container.DataItem).GetString(0) %></td>
     <td><%# ((DbDataRecord)Container.DataItem).GetInt(1) %></td>
  </tr>
</ItemTemplate>
 

Use SqlDataReader to visit the Read-Only Data instead of DataSet:


A DataReader is a lean, mean access method that returns results as soon as they are available, rather than waiting for the whole of the query to be populated into a DataSet. This can boost your application performance quite dramatically and, once you get used to the methodology, can be quite elegant in and of itself.


Not only can we inspect as we go, but the DataReader only stores one result at a time on the client. This results in a significant reduction in memory usage and system resources when compared to the DataSet, where the whole query is stored.


Use of server controls: 


There are two kinds of control in ASP.NET. The one is Server Control, the other one is Html Control. The last only inspires the client-side event; however, the server control would generate objects on server-side by RunAtServer property. So its function is very powerful, but its cost is expensive as well. We should make a choice depending on the different condition.


HttpResponse.IsClientConnected:


Consider using theHttpResponse.IsClientConnected property to verify if the client is still connected before processing a request and performing expensive server-side operations. However, this call may need to go out of process on IIS 5.0 and can be very expensive. If you use it, measure whether it actually benefits your scenario.


Encode Using ASCII When You Don't Need UTF:


By default, ASP.NET comes configured to encode requests and responses as UTF-8. If ASCII is all your application needs, eliminated the UTF overhead can give you back a few cycles. Note that this can only be done on a per-application basis.


Use the Optimal Authentication Procedure:



There are several different ways to authenticate a user and some of more expensive than others (in order of increasing cost: None, Windows, Forms, Passport). Make sure you use the cheapest one that best fits your needs.


Conclusion: 


You can see that ASP.NET has a lot of performance tips you should know, so you have to consider them when writing your ASP.NET application.


Some of the tips taken from Omar Al Zabir blog, Adiseshu Dasari post and some of them are from my own knowledge and MSDN investigation.


I hope these tips will help you speed up your ASP.NET applications.


Thanks Rotem.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

one comment

  1. dwaikat18 בדצמבר 2008 ב 16:22

    I've tested the rule that says:

    Encode Using ASCII When You Don't Need UTF:

    at all cases it gives me the same size, (even ASCII is bigger by about 5 characters!!!)

    please provide us an example that really works

    Reply