
Steve Smith, owner of ASP Alliance and Lake Quincy Media joins us today to teach us about some hidden gems in ASP.NET caching and performance. Steve’s expertise in this area comes from first-hand experience as Lake Quincy’s ad system serves over 60 requests per second and handles over 150 million requests per month. Steve is an ASP Insider, Regional Director, Microsoft MVP, INETA Speaker and fellow book author!
What performance metrics are worth tracking?
- Requests per second
- Time to last byte: How long it takes the user to see the page load
Indicators of performance problems include high requests per
second, but very long page load times. Alternatively you may notice quick load
times, but only for a small number of users. The balance of these two metrics
will help you even out the performance of your website.
How do you find bottlenecks?
- Recognize there is
only one bottleneck at a time: The rule of thumb is that there is always
one bottleneck – and changing anything else before addressing the bottleneck
will not improve performance. Most bottlenecks in web applications are external
resources, like a database, web service or the file system.
- Avoid optimizing too
early. Beware of pre-mature optimization as it may cause performance
problems. Without the benefit of production environment (large number of users
going to your site) you may make implementation mistakes.

- Measure: Run load
tests and profilers to establish benchmarks
Caching Basics
Output Caching
ASP.NET included output caching as a feature from the
beginning. Output caching is easy to use and has many different features
available.
One feature that many people don’t know about is the
control’s ability to vary by browser. This is important because if you have
markup optimized for a particular browser and then a user views your site with
an un-optimized browser version, your site may look broken. Using the output
cache’s vary by browser parameter can help you avoid this problem.
Do you have to be worried about your output cache
getting to big?
Yes, but most servers can handle
the load that caching will create on the server. Caching saves the rendered
markup in memory and then serves it while the cache is still valid, so many
cache entries will only be around 5 to 10KB of markup.
ASP.NET’s caching also
automatically manages memory so the cache will purge when the server detects
memory pressure. Caching uses a least-recently-used (LRU) algorithm to
determine which cache entries to purge first. The worst case scenario is that
your server will begin to "thrash" alternately caching pages and purging pages
upon every request.
Substitution Control
The substitution control gives you a way to display dynamic
content on a cached page. The control uses a callback to generate a string to
substitute in its place before serving the content to the client. For example
if you wanted to display the login information for a user on the screen on a
cached page, the substitution control can display the user’s name in the middle
of cached content.
Even though the control requires a string, you are not
limited to simple strings. You may still use rich UI controls - you would
simply need to render the control’s HTML and pass it up to the substitution
control.
For an example of how to tell a control to render its HTML, Steve has an example that even comes with a nice extension method that makes getting the markup easy for any control.
The only seeming drawback to using the substitution control
is the lack of kernel-level caching support in IIS7.
State Bag Access Pattern Gotchas
Many developers will implement caching by using the state
access pattern. While this approach is acceptable there is a gotcha you must be
aware of: only make a call to the cache only ONCE during a request. If you make
multiple calls to the cache an item may be expired or removed between the first
and subsequent calls.
Naïve State Bag Access Pattern
public List List()
{
List myList;
if(Cache["customers"] == null)
{
myList = DAL.ListCustomers();
Cache.Insert("customers", mList, null,
DateTime.Now.AddHours(1), TimeSpan.Zero);
}
return (List)Cache["customers"];
}
Correct State Bag Access Pattern
public List List()
{
string cacheKey = "customers";
List myList =
Cache[cacheKey] as List;
if(myList == null)
{
myList = DAL.ListCustomers();
Cache.Insert(cacheKey, mList, null,
SiteConfig.CacheDuration, TimeSpan.Zero);
}
return myList;
}
Cache API
The caching API lives in the System.Web namespace, but is not limited to a web application or IIS.
Benefits of using the API include:
- Manages memory itself
- Supports dependencies based on
- key
- time
- file system
- databases
Limitations of ASP.NET Caching
The built-in ASP.NET caching is
app-domain specific, so caching on a web farm is problematic. If you put
something into the cache on server "A" the same cache does not exist on server
"B". A project in its infancy from Microsoft, codenamed Velocity, will help
address this issue. Products available today that supports distributed cache
and session are ScaleOut State Server or Alachisoft NCache.
Write Caching
Write caching is the practice of
filling a buffer of commands and sending them to the server in infrequent
intervals. A Common practice for using write caching is in logging scenarios.
The implementation may include a short-lived session or cache object which is
accessed by a separate process powered by a timer which takes whatever has
built up in the buffer and then executes the commands against the server.
For a code examples read Steve’s
article, Use Write Caching to Optimize High Volume Data Driven Applications.
While Steve’s article is relevant for SQL 2000 environments, Jason Follas posed
an update to the write caching technique that is optimized for SQL 2005 called,
Coding in SQL Server: An Evolution. Jason’s article shows you how to implement
write caching which requires less code and performs even faster than Steve’s
SQL 2000 example.
Finally, you could go even
further and make these write caching reliable by implementing a queuing
solution.
Client-Side Caching
Client-side caching includes:
- Storing data at the browser and then manipulating it with
JavaScript
- Configuring client cache headers will store static resources
at the client and proxy servers
Side Effects of Caching: Logging
Web servers log traffic statistics by counting how many
times a file is served off the file system. When you implement caching pages
are served from memory and not off the server’s file system. Knowing this, you
may need to alter your approach to reporting the traffic on your site.
Most of the caching techniques discussed here include
storing the rendered HTML of a page or a control, so request to images are
still served as normal even on a cached page. Therefore as you begin to make
use caching, you may want to include web beacons (or the infamous clear pixel
image) to help you track statistics for your site.
Further, logging implemented with custom logic is also
affected. Cached pages do not exist as control objects and therefore cannot
execute the code-behind while in cache. If you have logging setup on a page the
logic will only run the first time and when the cache expires.
Debugging Cached Applications
Debugging cached applications can be hard if you are
expecting code to execute that never runs. A few ways you can help debugging
are to:
- Make it easy to turn off caching: Turning off caching in a
development environment will allow you to go straight to problem areas
- An object's rendered HTML is what is cached: Be aware that cached controls exist as the rendered HTML
string in the cache and not the original object. If you try to run a method of
a cached user control you will encounter an exception
Tips and Tricks
Note: Browsers only make two simultaneous requests per
domain. By simply creating extra CNAMEs for images, style sheets and JavaScript
files, you can greatly improve the load speed of a page.
Resources