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 to optimize page load times. When asked about performance, the best answer might be to use a
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
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.
If you use any kind of ORM (EF, NHibernate, etc.) I strongly recommend taking a look at ayende’s profiler.
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.
In ySlow, the page gets a C and a lot of 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 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
- 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).