18.5 Performance

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. We can't
find the word in our dictionary, but it seems like a good word.

18.5.1 ASP.NET-Specific Issues

Correctly using the following features of ASP.NET offers the greatest
performance improvements when an ASP.NET application is running.

18.5.1.1 Session state

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)>

In C#, it looks like this:

[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
later in this chapter) or if the data must not be lost if a server or
process is stopped and restarted.

18.5.1.2 View state

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:

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" %>

18.5.1.3 Caching

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.

18.5.1.4 Server controls

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>

18.5.1.5 Web gardening and web farming

Adding multiple processors to a computer is called web
gardening. The .NET Framework takes advantage of this by
distributing work to several processes, one process 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
scalability.

18.5.1.6 Round trips

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 shows how to
make code execution conditional on the IsPostBack property:

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

In C#, it looks like this:

void Page_Load(Object sender, EventArgs e){if (! IsPostBack)
{
// Do the expensive operations only the
// first time the page is loaded.
}
}

18.5.2 General .NET Issues

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.

18.5.2.1 String concatenation

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-26 (in VB .NET)
and Example 18-27 (in C#). 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.

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.

Figure 18-8. String concatenation benchmark results

18.5.2.2 Minimize exceptions

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.

18.5.2.3 Use early binding

.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:

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,
OptionStrict can be either a
line of code at the beginning of a module:

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.

18.5.2.4 Use managed code

Managed code is more performant than unmanaged code. It
may be worthwhile porting heavily used COM components to managed
code.

18.5.2.5 Disable debug mode

When you deploy your application, remember to disable Debug mode. For a
complete discussion of deployment issues, refer to Chapter 20.

18.5.3 Database Access Issues

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.

18.5.3.1 Stored procedures

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.

18.5.3.2 Use DataReader class

There are two main ways to get data from a database: from a DataReader
object or a DataSet object. The DataReader classes, either
SqlDataReader, OleDbDataReader,
or OracleDataReader is a much faster way of
accessing data if all you need is a forward-only data stream.

18.5.3.3 Use SQL or Oracle classes rather than OleDB classes

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.