Quite a few people have been advocating for some time that
performance is a feature. Still, a lot of ASP.NET developers do not seem to know how optimize page load times. When asked about performance, the best answer might be to use a StringBuilder for string concatenation (or even to use string.Concat instead of +)
If you really want to improve a website’s performance, there is a little more you should consider.
- Client (Browser) performance (e.g. css/js)
- Communication between the client and the web server
- Web server performance
- Communication between web server and storage
- Storage performance
I won’t cover client performance because I’m no javascript or rendering engine expert. Storage performance is also out of scope as there a too many possible data stores (sql, nosql, file system etc.) to consider. For now I will focus on the areas which are directly effected by an ASP.NET application.
Tools for Measuring
It’s always better to measure (or let a smart tool guess) than to guess yourself when trying to optimize. There are countless tools for all possible performance concerns, both free and commercial. Here are some that I have used so far:
Client
Server
ORM
If you use any kind of ORM (EF, NHibernate etc.) I strongly recommend taking a look at
ayende’s profiler
Production Profiling
If you want to measure the performance of your pages on the production system, the
MiniProfiler, is probably the easiest tool to profile single requests. It even offers ADO.NET/EF/RavenDb live profiling (almost) without impacting the performance of your site.
No interest in checking out all of these tools? Then get at least yslow. It’s easy to use, free and includes a lot of useful guidelines. For this series I’ll use webpagetest.org, yslow and the jetbrains performance profiler.
Getting a baseline
To be sure your changes actually improved the performance, the first thing you need is a baseline. To have a simple but not trivial sample, I forked
nostore, a small sample project my colleague
Andreas Koch and I created. After removing a few “performance best practices” which were already implemented, I called it
nostore-slow.
In order to have a reliable and universally accessible demo for load test, I uploaded it to
Appharbor. The baseline was created using the
“unoptimized” branch.
ySlow Score

In ySlow the page gets a C and a lot tips how the page load time could be improved. I will ignore the ETags suggestion because it is
no longer valid with IIS 7. But all the other points are worth looking into.
Single Request Waterfall

A single user test with
WebPagetest shows that even a lonely user (i.e. no load on the server) experiences a poor performance. 6 seconds for the first view and 3.3 seconds for the repeat view are rather slow. The first waterfall shows not only long first byte time but also a lot of requests (and some of them are quite big). The second waterfall contains almost exclusively 304 requests, i.e. the content did not change, but the requests were made anyway.
Load Test

Load Tests are not really the main measurement and most of the numbers aren't very meaningful without a comparison. Nevertheless the metrics can easily be compared and an average page load time of 10 seconds should make clear that the performance of the unoptimized version is miserable.
List of Measures
Given these results it’s easy to compose a list of concrete measures:
- Reduce the time to first byte (Improve performance on the web server)
- Output Caching
- Data Caching
- Code Optimization
- Use a CDN
- at least for components like jQuery which are available on multiple public CDNs
- maybe even for custom content
- Make fewer requests
- combine js/css requests
- load less images
- Add Expires headers to allow browser (or proxy) caching
- automatically by using the output cache
- statically using IIS configuration
- dynamically using code
- Move CSS references to the header
- Move Javascript (-references) to the footer
- Minify js/css
- Compress html/js/css
In the next posts I will show you how to implement these measure using ASP.NET MVC (most of it is equally valid for WebForms).