Performance is often a vitally important issue in computer applications, especially in web applications receiving a large number of requests. One obvious way to improve performance is to buy faster hardware with more memory. But you can also tune your code to enhance performance in many ways, some of them significant. We’ll begin by examining some of the areas specific to ASP.NET which offer the greatest performance improvements and then examine some of the general .NET topics related to improving performance.
Several Microsofties involved with actually writing the .NET Framework used the word performant to mean that something is delivering higher performance. I can’t find the word in any dictionary, but it seems like a good word to me.
Correctly using the following features of ASP.NET offers the greatest performance improvements when an ASP.NET application is running.
Session state is a wonderful thing, but not all applications or pages require it. For any that do not, disable it.
Session state can be disabled for an entire application by setting
the
EnableSessionState
attribute in the
Page
directive to false
, as in:
<%@ Page Language="VB" EnableSessionState="false"%>
If a page will not be creating or modifying session variables but still needs to access them, set the session state to read-only:
<%@ Page Language="VB" EnableSessionState="ReadOnly"%>
By default, web services do not have session state enabled. They only
have access to session state if the EnableSession
property of the WebMethod
attribute is set to
true
. In VB.NET this looks like:
<WebMethod(EnableSession:=true)>
and in C#:
[WebMethod(EnableSession=true)]
Session state can be disabled for an
entire application by editing the sessionState
section of the application’s
web.config
file:
<sessionState mode="off" />
Session state can be stored in one of three ways:
In-process
Out-of-process, as a Windows service
Out-of-process, in a SQL Server database
Each has advantages and disadvantages. Storing session state in-process is by far the most performant. The out-of-process stores are necessary in web farm or web garden scenarios (see Section 18.5.1.5,) or if the data must not be lost if a server or process is stopped and restarted.
For a complete discussion of session state, see Chapter 6.
Automatic view state management is another great feature of ASP.NET server controls that enables the controls to correctly show property values after a round trip with no work on the part of the developer. However, there is a performance penalty. This information is passed back and forth via a hidden field, which consumes bandwidth and takes time to process. To see the amount of data used in view state, enable tracing and look at the Viewstate column of the Control Hierarchy table.
By default, view state is enabled for all server controls. To disable
view state for a server control, set the
EnableViewState
attribute to
false
, as in the following example:
<asp:TextBox id="txtBookName" text="Enter book name." toolTip="Enter book name here." EnableViewState="false" runat="server" />
You can also disable view state for an entire page by setting the
EnableViewState
attribute of the
Page
directive to false
, as in:
<%@ Page Language="C#" EnableViewState="false" %>
Use output and data caching whenever possible. This is especially valuable for database queries that either return relatively static data or have a limited range of query parameters. Effective use of caching can have a profound effect on the performance of a web site.
Server controls are very convenient and offer many advantages. In Visual Studio .NET, they are practically the default type of control. However, they have a certain amount of overhead and are sometimes not the optimal type of control to use.
In general, if you do not need to programmatically manipulate a control, do not use a server control. Use a classic HTML control instead. For example, if placing a simple label on a page, there is no need to use a server control unless you need to read or change the value of the label’s Text property.
If you need to substitute values into HTML sent to the client browser, you can achieve the desired result without using a server control, instead using data binding or a simple rendering. For example, the following VB.NET example shows three ways of displaying a hyperlink in a browser:
<script language="VB" runat="server"> Public strLink As String = "www.anysite.com" Sub Page_Load(sender As Object, e As EventArgs) '..retrieve data for strLink here ' Call the DataBind method for the page. DataBind( ) End Sub </script> <%--the server control is not necessary...--%> <a href='<%# strLink %>' runat="server"> The Name of the Link</a> <br><br> <%-- use DataBinding to substitute literals instead...--%> <a href='<%# strLink %>' > The Name of the Link</a> <br><br> <%-- or a simple rendering expression...--%> <a href='<%= strLink %>' > The Name of the Link</a>
Adding multiple processors to a computer is called web gardening. The .NET Framework takes advantage of this by distributing work to several processes, one per CPU.
For truly high-traffic sites, multiple web server machines can work together to serve the same application. This is referred to as a web farm.
At the least, locating the web server on one machine and the database server on another will buy a large degree of stability and performance.
Round trips to the server are very expensive. In low bandwidth situations, they are slow for the client, and in high-volume applications, they bog down the server and inhibit scaling. You should design your applications to minimize round trips.
The only truly essential round trips to the server are those that read or write data. Most validation and data manipulations can occur on the client browser. ASP.NET server controls do this automatically for validation with uplevel browsers (i.e., IE 4 and IE 5, or any browser that supports ECMAScript).
When developing custom server controls, having the controls render client-side code for uplevel browsers will substantially reduce the number of round trips.
Another way to minimize round trips is to use the IsPostBack property in the Page_Load method. Often, you will want the page to perform some process the first time the page loads, but not on subsequent postbacks. For example, the following code in VB.NET:
sub Page_Load(ByVal Sender as Object, _
ByVal e as EventArgs)
if not IsPostBack then
' Do the expensive operations only the
' first time the page is loaded.
end if
end sub
and in C#:
void Page_Load(Object sender, EventArgs e)
{if (! IsPostBack)
{ // Do the expensive operations only the // first time the page is loaded. } }
shows how to make code execution conditional on the IsPostBack property. For a complete discussion of the IsPostBack property, see Chapter 3.
Many of the performance enhancements that affect an ASP.NET application are general ones that apply to any .NET application. This section lists some of the major .NET-related areas to consider when developing your ASP.NET applications.
Strings are immutable in the .NET Framework. This means that methods and operators that appear to change the string are actually returning a modified copy of the string. This has huge performance implications. When doing a lot of string manipulation, it is much better to use the StringBuilder class.
Consider the code shown in Example 18-23 (in C# only). It measures the time to create a string from 10,000 substrings in two different ways. The first time, a simple string concatenation is used, and the second time the StringBuilder class is used. If you want to see the resulting string, uncomment the two commented lines in the code.
Example 18-23. String concatenation benchmark in C#, csStringConcat.aspx
<%@ Page Language="C#" %> <script runat="server"> void Page_Load(Object Source, EventArgs E) { int intLimit = 10000; DateTime startTime; DateTime endTime; TimeSpan elapsedTime; string strSub; string strWhole = ""; // Do string concat first startTime = DateTime.Now; for (int i=0; i < intLimit; i++) { strSub = i.ToString( ); strWhole = strWhole + " " + strSub; } endTime = DateTime.Now; elapsedTime = endTime - startTime; lblConcat.Text = elapsedTime.ToString( ); // lblConcatString.Text = strWhole; // Do stringBuilder next startTime = DateTime.Now; StringBuilder sb = new StringBuilder( ); for (int i=0; i < intLimit; i++) { strSub = i.ToString( ); sb.Append(" "); sb.Append(strSub); } endTime = DateTime.Now; elapsedTime = endTime - startTime; lblBuild.Text = elapsedTime.ToString( ); // lblBuildString.Text = sb.ToString( ); } </script> <html> <body> <form runat="server"> <h1>String Concatenation Benchmark</h1> Concatenation: <asp:Label id="lblConcat" runat="server"/> <br/> <asp:Label id="lblConcatString" runat="server"/> <br/> <br/> StringBuilder: <asp:Label id="lblBuild" runat="server"/> <br/> <asp:Label id="lblBuildString" runat="server"/> </form> </body> </html>
When this page is run, you should see something like Figure 18-8. The difference between the two techniques is fairly dramatic: the StringBuilder’s Append method is nearly 200 times faster than string concatenation.
It is possible to use
try
...catch
blocks to control
program flow. However, this coding technique is a serious impediment
to performance. You will do much better if you first test whether
some condition will cause a failure, and if so, code around it.
For example, rather than dividing two integers inside a
try
...catch
block and catching
any Divide By Zero exceptions thrown, it is much better to first test
whether the divisor is zero, and if it is, not do the operation.
.NET languages allow both early and late binding. Early binding occurs when all objects are declared and the object type known at compile time. Late binding occurs when the object type is not determined until runtime, at which point the CLR figures out, as best it can, what object type it is dealing with.
Early binding is much faster than late binding, although the latter
can be very convenient to the developer. In VB.NET, it is perfectly
legal to not declare your variables before they are used, to declare
them but not assign a data type (in which case they will be of type
Object), or to explicitly declare them as type Object. All these
cases constitute late binding. Including an Option Explicit On
statement in your code
(analogous to the Option Explicit
statement in
VB6) helps impose discipline by requiring that all variables be
declared before they are used, although you do not have to declare
the type. This line should appear before any other lines of code
except for page directives; for example:
<%@ WebService Language="VB" Class="ProgAspNet.vbStockTicker" %> Option Explicit On
Alternatively, you can include an
Explicit
attribute for the Page directive, as in:
<%@ Page Language="VB" Explicit="true" %>
There is also an Option Strict
available, which, if
enabled, prevents data conversions from happening implicitly if there
is any possibility of lost data due to type incompatibility. This
imposes type-safe behavior on the code, but does not eliminate late
binding. As with Explicit
,
Option
Strict
can be either a
line of code at the beginning of a module:
<%@ WebService Language="VB" Class="ProgAspNet.vbStockTicker" %>
Option Explicit On
Option Strict On
or a page directive:
<%@ Page Language="VB" Explicit="true" Strict="true"
%>
Jscript.NET also supports early binding, although there are no compiler directives to enforce its use. C# supports early binding by default; you achieve late binding in C# using reflection.
Managed code is more performant than unmanaged code. It may be worthwhile porting heavily used COM components to managed code.
When you deploy your application, remember to disable Debug mode. For a complete discussion of deployment issues, refer to Chapter 20.
Almost all applications involve some form of database access, and accessing data from a database is necessarily an expensive operation. Data access can be made more efficient, however, by focusing on several areas.
When interacting with a database, using stored procedures is always much faster than the same operation passed in as a command string. This is because stored procedures are compiled and optimized by the database engine. Use stored procedures whenever possible.
There are two main ways to get data from a
database: from a DataReader object or a DataSet object. The
DataReader classes, either
SqlDataReader
or
OleDbDataReader
, is a much faster way of accessing data
if all you need is a forward-only data stream.
Some database engines have managed classes specifically designed for
interacting with that database. It is much better to use the
database-specific classes rather than the generic OleDB classes. So,
for example, it is faster to use a SqlDataReader
rather than a OleDbDataReader
.
18.218.31.165