Friday, 9 December 2011

How to Improve ASP.NET Performance


If you are writing code in ASP.NET then you need to go through the following points to ensure good performance:
  • Do you use caching?
  • Do you use session state?
  • Do you use application state?
  • Do you use threading and synchronization features?
  • Do you manage resources efficiently?
  • Do you manage strings efficiently?
  • Do you manage exceptions efficiently?
  • Have you optimized your Web pages?
  • Do you use view state?
  • Do you use server controls?
  • Do you access data from your pages?
  • Do you use data binding?
  • Do you call unmanaged code from ASPX pages?
  • Have you reviewed the settings in Machine.config?

Do You Use Caching?

Use the following review questions to assess your code’s use of ASP.NET caching features:
  • Do you have too many variations for output caching?
  • Check your pages that use the output cache to ensure that the number of variations has a limit. Too many variations of an output cached page can cause an increase in memory usage. You can identify pages that use the output cache by searching for the string “OutputCache”.
  • Could you use output caching?
  • When reviewing your pages, start by asking yourself if the whole page can be cached. If the whole page cannot be cached, can portions of it be cached? Consider using the output cache even if the data is not static. If your content does not need to be delivered in near real-time, consider output caching.  Using the output cache to cache either the entire page or portions of the page can significantly improve performance.
  • Is there static data that would be better stored in the cache?
  • Identify application-side data that is static or infrequently updated. This type of data is a great candidate for storing in the cache.
  • Do you check for nulls before accessing cache items?
  • You can improve performance by checking for null before accessing the cached item as shown in the following code fragment.
    Object item = Cache["myitem"];
    if (item==null)
    {
        // repopulate the cache
    }
    This helps avoid any exceptions which are caused by null objects. To find where in your code you access the cache.

Do You Use Session State?

First need to chosse proper State Store
The in-process state store provides excellent performance and scales well. However, most high volume Web applications run in a Web farm. To be able to scale out, you need to choose between the session state service and the SQL Server state store. With either of these choices, you have to understand the associated impact of network latency and serialization, and you have to measure them to ensure that your application meets its performance objectives. Use the following information to help choose a state store:

Single Web server.
Use the in-process state store when you have a single Web server, when you want optimum session state performance, and when you have a reasonable and limited number of concurrent sessions. Use the session state service running on the local Web server when your sessions are expensive to rebuild and when you require durability in the event of an ASP.NET restart. Use the SQL Server state store when reliability is your primary concern.
Web farm.
Avoid the in-process option, and avoid running the session state service on he local Web server. These cause server affinity. You can use Internet Protocol (IP) affinity to ensure that the same server handles subsequent requests from the same client, but internet service providers (ISP) that use a reverse proxy cause problems for this approach. Use a remote session state service or use SQL Server for Web farm scenarios.

StateServer versus SQLServer.
Use a remote state service, if you do not have a SQL Server database. Use SQL Server for enterprise applications or high volume Web applications. If your remote state service and your Web server are separated by a firewall, then you need to open a port. The default port is port 42424. You can change the port in the following registry key:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\aspnet_state \Parameters.

Use the following review questions to review your code’s use of session state:
  • Do you disable session state when not required?
  • The Session State remains on by default. If your application does not use session state, disable it in the Web.config file as follows:
    <sessionstate mode="Off" />
    If parts of your application need session state, identify pages that do not use it and disable it for those pages by using the following page level attribute.
  • Minimizing the use of session state increases the performance of your application.
  • Do you have pages that do not write to a session?
  • Page requests using session state internally use a ReaderWriterLock to manage access to the session state. For pages that only read session data, consider setting EnableSessionState to ReadOnly.
    <%@ Page EnableSessionState="ReadOnly" . . .%>
    This is particularly useful when you use HTML frames. The default setting (due to ReaderWriterLock) serializes the page execution. By setting it to ReadOnly, you prevent blocking and allow more parallelism.
  • Do you check for nulls before accessing items in session state?
  • You can improve performance by checking for null before accessing the item, as shown in the following code.
    object item = Session["myitem"];
    if(item==null)
    {
        // do something else
    }
    A common pitfall when retrieving data from session state is to not check to see if the data is null before accessing it and then catching the resulting exception. You should avoid this because exceptions are expensive. To find where your code accesses session state, you can search for the string “Session”.
  • Do you store complex objects in session state?
  • Avoid storing complex objects in session state, particularly if you use an out-of process session state store. When using out-of-process session state, objects have to be serialized and de-serialized for each request, which decreases performance.
  • Do you store STA COM objects in session state?
  • Storing single-threaded apartment (STA) COM objects in session state causes thread affinity because the sessions are bound to the original thread on which the component is created. This severely affects both performance and scalability. Make sure that you use the following page level attribute on any page that stores STA COM objects in session state.
    This forces the page to run from the STA thread pool, avoiding any costly apartment switch from the default multithreaded apartment (MTA) thread pool for ASP.NET. Where possible, avoid the use of STA COM objects.

Do You Use Application State?

Use the following review questions to assess how efficiently your code uses application state:
  • Do you store STA COM components in application state?
  • Avoid storing STA COM components in application state where possible. Doing so effectively bottlenecks your application to a single thread of execution when accessing the component. Where possible, avoid using STA COM objects.
  • Do you use the application state dictionary?
  • You should use application state dictionary for storing read-only values that can be set at application initialization time and do not change afterward. There are several issues to be aware of when using application state in your code, such as the following:
    • Memory allocated to the storage of application variables is not released unless they are removed or replaced.
    • Application state is not shared across a Web farm or a Web garden — variables stored in application state are global to the particular process in which the application is running. Each application process can have different values.
  • Consider using the following alternatives to application state:
    • Create static properties for the application rather than using the state dictionary. It is more efficient to look up a static property than to access the state dictionary. For example, consider the following code:
    • Application["name"] = "App Name";
      It is more efficient to use the following code:
      private static String _appName = "App Name";
      public string AppName
      {
        get{return _appName;}
        set{_appName = value;}
      }
    • Use configuration files for storing application configuration information.
    • Consider caching data that is volatile enough that it cannot be stored in application state, but needs updates periodically from a persistent medium,in the Cache object.
    • Use the session store for user-specific information. You can identify places where your code uses application state by searching for the string “Application”.

Do You Use Application State?

The .NET Framework exposes various threading and synchronization features, and the way your code uses multiple threads can have a significant impact on application performance and scalability. Use the following review questions to assess how efficiently your ASP.NET code uses threading:
  • Do you create threads on a per-request basis?
  • Avoid manually creating threads in ASP.NET applications. Creating threads is an expensive operation that requires initialization of both managed and unmanaged resources. If you do need additional threads to perform work, use the CLR thread pool. To find places in your code where you are creating threads, search for the string “ThreadStart”.
  • Do you perform long-running blocking operations?
  • Avoid blocking operations in your ASP.NET applications where possible. If you have to execute a long-running task then consider using asynchronous execution (if you can free the calling thread) or use the asynchronous “fire and forget” Model.

Do You Manage Resources Efficiently?

Use the following review questions to assess how efficiently your code uses resources:
  • Do you explicitly close resources properly?
  • Ensure that your code explicitly closes objects that implement IDisposable by calling the object’s Dispose or Close method. Failure to close resources properly and speedily can lead to increased memory consumption and poor performance.
    Failing to close database connections is a common problem. Use a finally block (or a using block in C#) to release these resources and to ensure that the resource is closed even if an exception occurs.
  • Do you pool shared resources?
  • Check that you use pooling to increase performance when accessing shared resources. Ensure that shared resources, such as database connections and serviced components that can be pooled are being pooled. Without pooling, your code incurs the overhead of initialization each time the shared resource is used.
  • Do you obtain your resources late and release them early?
  • Open shared resources just before you need them and release them as soon as you are finished. Holding onto resources for longer than you need those, increases memory pressure and increases contention for these resources if they are shared.
  • Do you transfer data in chunks over I/O calls?
  • If you do need to transfer data over I/O calls in chunks, allocate and pin buffer for sending and receiving the chunks. If you need to make concurrent I/O calls, you should create a pool of pinned buffers that is recycled among various clients rather than creating a buffer on a per-request basis. This helps you avoid heap fragmentation and reduce buffer creation time.

Do You Manage Strings Efficiently?

Use the following review questions to assess how efficiently your ASP.NET code manipulates strings:
  • Do you use Response.Write for formatting output?
  • Identify areas in your code where you concatenate output, such as to create a table, and consider using Response.Write instead. Response.Write is the most efficient method for writing content to the client.
  • Do you use StringBuilder to concatenate strings?
  • If the number of appends is unknown and you cannot send the data to the client immediately by using a Response.Write, use the StringBuilder class to concatenate strings.
  • Do you use += for concatenating strings?
  • Identify places in your code where you perform string concatenation by using the += operator. If the number of appends is unknown, or you are appending an unknown size of data, consider using the StringBuilder class instead.

Do You Manage Exceptions Efficiently?

Use the following review questions to assess how efficiently your code uses exceptions:
  • Have you implemented an error handler in Global.asax?
  • Although implementing an error handler in Global.asax does not necessarily increase performance, it helps you to identify unexpected exceptions that occur in your application. After you identify the exceptions that occur, take appropriate action to avoid these exceptions.
  • Do you use try/finally on disposable resources?
  • Ensure that disposable resources are released in a finally block to ensure they get cleaned up even in the event of an exception. Failing to dispose of resources is a common problem.
  • Does your code avoid exceptions?
  • Your code should attempt to avoid exceptions to improve performance because exceptions incur a significant overhead. Use the following approaches:
    • Check for null values.
    • Do not use exceptions to control regular application logic.
    • Do not catch exceptions you cannot handle and obscure useful diagnostic information.
    • Use the overloaded Server.Transfer method Server.Transfer(String,bool) instead of Server.Transfer, Response.Redirect, and Response.End to avoid exceptions.

Have You Optimized Your Web Pages?

Use the following review questions to assess the efficiency of your .aspx pages:
  • Have you taken steps to reduce your page size?
  • Try to keep the page size to a minimum. Large page sizes place increased load on the CPU because of increased processing and a significant increase in network bandwidth utilization, which may lead to network congestion. Both of these factors lead to increased response times for clients. Consider the following guidelines to help reduce page size:
    • Use script includes (script tags rather than interspersing code with HTML).
    • After complete the script compress the script by online tools Script compress[^]
      • Remove redundant white space characters from your HTML.
      Not Good code
      <table>
            <tr>
                 <td> test</td>
            </tr>
      <table>
      Good Code
      <table><tr><td> test</td></tr><table>
      • Disable view state for server controls where it is not needed.
      • Avoid long control names.
      • Minimize the use of graphics, and use compressed images.
      you can compresses image by online tools Image Compress tool[^]
      • Consider using cascading style sheets to avoid sending the same formatting directives to the client repeatedly.
  • Is buffering disabled?
  • Ensure that you have buffering enabled. Buffering causes the server to buffer the output and send it only after it has finished the processing of the page. If buffering is disabled, the worker process needs to continuously stream responses from all concurrent requests; this can be a significant overhead on memory and the processor, especially when you use the ASP.NET process model. To find out if you have buffering disabled, you can search your code base for the following strings: “buffer” and “BufferOutput.” Make sure that the buffer attribute is set to true on the <pages> element in your application's Web.config file.
    <pages buffer="True">
  • Do you use Response.Redirect?
  • Search your code for “Response.Redirect” and consider replacing it with Server.Transfer. This does not incur the cost of a new request because it avoids any client-side redirection.
    You cannot always simply replace Response.Redirect calls with Server.Transfer calls because Server.Transfer uses a new handler during the handler phase of execution. Response.Redirect generates a second request. If you need different authentication and authorization, caching, or other run-time devices on the target, the two mechanisms are not equivalent. Response.Redirect causes an extra request to be sent to the server. Response.Redirect also makes the URL visible to the user. This may be required in some scenarios where you require the user to bookmark the new location.
  • Do you use Page.IsPostBack?
  • Check that the logic in your page uses the Page.IsPostBack property to reduce redundant processing and avoid unnecessary initialization costs. Use the Page.IsPostBack property to conditionally execute code, depending on whether the page is generated in response to a server control event or whether it is loaded for the first time.
  • Do you validate user input?
  • Check that you validate user input on the client to reduce round trips to the server. This also provides better feedback to the user. For security reasons, ensure that any client-side validation is complimented with the equivalent server-side validation.
  • Have you set Explicit and Strict to true?
  • Ensure you use Option Strict and Explicit to reduce inadvertent late binding when using Visual Basic .NET.
    <%@ Page Language="VB" Explicit="true" Strict="true" %>
    This can be easily searched for by using the Findstr.exe file with regular expressions.
    C:\findstr /i /s /r /c:"<%.*@.*page.*%>" *.aspx
    pag\default.aspx:<%@ Page Language="VB" %>
    pag\login.aspx:<%@ page Language="VB" %>
    pag\main.aspx:<%@ Page Language="VB" Explicit="true" Strict="true" %>
    ...
  • Have you disabled debugging?
  • Check your Web.config file and ensure debug is set to false in the <compilation> section and check your .aspx pages to ensure debug is set to false. If debugging is enabled, the compiler does not generate optimized code and pages are not batch compiled. You can check your .aspx pages by using the Findstr.exe file with regular expressions.
    C:\pag>findstr /i /r /c:"<%.*@.*page.*debug=.*true*.*%>" *.aspx
    login.aspx:<%@ page Language="VB" Debug="True" %>
    main.aspx:<%@ Page Language="c#" Debug="True" %>
  • Have you disabled tracing?
  • Check your Web.config file to ensure trace is disabled in the <trace> section. Also check your .aspx pages to ensure trace is set to false. You can check your .aspx pages by using the Findstr.exe file with regular expressions.
    C:\pag>findstr /i /r /c:"<%.*@.*page.*trace=.*true*.*%>" *.aspx
    login.aspx:<%@ page Language="VB" Trace="True" %>
    main.aspx:<%@ Page Language="c#" Trace="True" %>
  • Do you set aggressive timeouts?
  • Set timeouts aggressively and tune accordingly. Evaluate each page and determine a reasonable timeout. The default page timeout is 90 seconds specified by the execution Timeout attribute in Machine.config. Server resources are held up until the request is processed completely or the execution times out, whichever is earlier. In most scenarios, users do not wait for such a long period for the requests to complete. They either abandon the request totally or send a new request which further increases the load on the server.

Do You Use View State?

Use the following review questions to assess how efficiently your applications use view state:
  • Do you disable view state when it is not required?
  • Evaluate each page to determine if you need view state enabled. View state adds overhead to each request. The overhead includes increased page sizes sent to the client as well as a serialization and de-serialization cost. You do not need view state under the following conditions:
    • The page does not post back to itself; the page is only used for output and does not rely on response processing.
    • Your page’s server controls do not handle events and you have no dynamic or data-bound property values (or they are set in code on every request).
    • If you are ignoring old data and repopulating the server control every time the page is refreshed.
  • Have you taken steps to reduce the size of your view state?
  • Evaluate your use of view state for each page. To determine a page’s view state size, you can enable tracing and see each how each control uses it. Disable view state on a control-by-control basis.

Do You Use Server Controls?

Use the following review questions to review how efficiently your ASP.NET applications use server controls:
  • Do you use server controls when you do not need to?
  • Evaluate your use of server controls to determine if you can replace them with light weight HTML controls or possibly static text. You might be able to replace a server control under the following conditions:
    • The data being displayed in the control is static, for example, a label.
    • You do not need programmatic access to the control on the server side.
    • The control is displaying read-only data.
    • The control is not needed during post back processing.
  • Do you have deep hierarchies of server controls?
  • Deeply nested hierarchies of server controls compound the cost of building the control tree. Consider rendering the content yourself by using Response.Write or building a custom control which does the rendering. To determine the number of controls and to see the control hierarchy, enable tracing for the page.

Do You Access Data From Your ASPX Pages?

Some form of data access is required by most ASP.NET applications. Data access is a common area where performance and scalability issues are found. Review the following questions to help improve your application’s page level data access:
  • Do you page large result sets?
  • Identify areas of your application where large result sets are displayed and consider paging the results. Displaying large result sets to users can have a significant impact on performance.
  • Do you use DataSets when you could be using DataReaders?
  • If you do not need to cache data, exchange data between layers or data bind to a control and only need forward-only, read-only access to data, then use DataReader instead.

Do You Use Data Binding?

Use the following review questions to review your code’s use of data binding:
  • Do you use Page.DataBind?
  • Avoid calling Page.DataBind and bind each control individually to optimize your data binding. Calling Page.DataBind recursively calls DataBind on each control on the page.
  • Do you use DataBinder.Eval?
  • DataBinder.Eval uses reflection, which affects performance. In most case DataBinder.Eval is called many times from within a page, so implementing alternative methods provides a good opportunity to improve performance.
    Avoid the following approach:
    <itemtemplate>
    <table><tbody><tr>
    <td><%# DataBinder.Eval(Container.DataItem,"field1") %></td>
    <td><%# DataBinder.Eval(Container.DataItem,"field2") %></td>
    </tr></tbody></table>
    </itemtemplate>
    Use explicit casting. It offers better performance by avoiding the cost of reflection. Cast the Container.DataItem as a DataRowView if the data source is a DataSet.
    <itemtemplate>
    <table><tbody><tr>
    <td><%# ((DataRowView)Container.DataItem)["field1"] %></td>
    <td><%# ((DataRowView)Container.DataItem)["field2"] %></td>
    </tr></tbody></table>
    </itemtemplate>
    Cast the Container.DataItem as a String if the data source is an Array or an ArrayList.
    <itemtemplate>
    <table><tbody><tr>
    <td><%# ((String)Container.DataItem)["field1"] %></td>
    <td><%# ((String)Container.DataItem)["field2"] %></td>
    </tr></tbody></table>
    </itemtemplate>

Do You Call Unmanaged Code From ASPX Pages?

Use the following review questions to review your code’s use of interoperability:
  • Have you enabled AspCompat for calling STA COM components?
  • Make sure that any page that calls an STA COM component sets the AspCompat page level attribute. This instructs ASP.NET to execute the current page request using a thread from the STA thread pool. By default, ASP.NET uses the MTA thread pool to process a request to a page. If you are using STA components, the component is bound to the thread where it was created. This causes a costly thread switch from the thread pool thread to the thread on which the STA object is created.
  • Do you create STA COM components in the page constructor?
  • Check your pages to ensure you are not creating STA COM components in the page constructor. Create STA components in the Page_Load, Page_Init or other events instead. The page constructor always executes on an MTA thread. When an STA COM component is created from an MTA thread, the STA COM component is created on the host STA thread. The same thread (host STA) executes all instances of apartment-threaded components that are created from MTA threads. This means that even though all users have a reference to their own instance of the COM component, all of the calls into these components are serialized to this one thread, and only one call executes at a time. This effectively bottlenecks the page to a single thread and causes substantial performance degradation. If you are using the AspCompat attribute, these events run using a thread from the STA thread pool, which results in a smaller performance hit due to the thread switch.
  • Do you use Server.Create object?
  • Avoid using Server.CreateObject and early bind to your components at compile time wherever possible. Server.CreateObject uses late binding and is primarily provided for backwards compatibility. Search your code base to see if you use this routine and as an alternative, create an interop assembly to take advantage of early binding.

Have You Reviewed the Settings in Machine.config?

Use the following review questions to review your application’s deployment plan:
  • Is the thread pool tuned appropriately?
  • Proper tuning of the CLR thread pool tuned improves performance significantly. Before deploying your application, ensure that the thread pool has been tuned for your application.
  • Is the memory limit configured appropriately?
  • Configuring the ASP.NET memory limit ensures optimal ASP.NET cache performance and server stability. In IIS 5.0 or when you use the ASP.NET process model under IIS 6.0, configure the memory limit in Machine.config. With IIS 6.0, you configure the memory limit by using the IIS MMC snap-in.
  • Have you removed unnecessary HttpModules?
  • Including HttpModules that you do not need adds extra overhead to ASP.NET request processing. Check that you have removed or commented out unused HttpModules inMachine.config.
 

No comments: