You are currently reviewing an older revision of this page.
At least once per week I get the question - “How can I make this page run faster”. We can break down ASP.NET performance into two main categories, size and speed. Not surprisingly, size has a direct correlation with speed. But the total time to last byte (page load time) is more involved than just the size of the HTML. So the focus is generally broken up into separate categories – server-side performance, and client-side HTML.
Even with the advent of AJAX, many ASP.NET applications still suffer from too many postbacks. In an AJAX model, a postback is done silently, rather than causing the entire page to refresh. But that doesn’t mean the postback isn’t happening. If your page_load takes a long time to execute on the serer, your AJAX callbacks are going to take a long time to execute as well. The solution? Well, for starters limit the number of postbacks (including AJAX callbacks) in your application. And for the times that you do need to postback, make sure your server-side code can execute quickly and efficiently. Here are some tips:
Cache. You’ve probably heard this a hundred times, but caching is a huge performance booster. Whether you’re using output caching on your page or user-control, or you’re sticking some data in the Application cache, you’re shortening the access time, which improves performance. Just be cautious. If you’re caching using session state, you could blow up the memory consumption on your server.
Data Access. Everyone knows about paging results in a grid, but only a small minority are actually paging results through to the backend database. By chunking data up into pages and sending it down to the browser you can cut the size of the HTML, and hence cut the time to load the page. The same is true for chunking data between your web server and your database server (even if it’s the same hardware). The LINQDataSource and the ObjectDataSource are both Pageable datasources in ASP.NET. In other words, when you ask the LINQDataSource to return the second page of results, it reaches into the Database and pulls out only the second page of results. This can speed up your server-side processing tremendously depending on the size of your database. And while we’re talking about databases, you might want to run a profiler against your DB and verify that your indexes are set up to ensure speedy results. Indexes really become important as you surpass the 500k record mark. It could mean the difference between a session time-out and a sub-second response.
One of my favorite things to do is take a look at the HTML markup generated from an ASP.NET web application, and look for ways to improve the performance. Searching through the HTML, looking for clues as to what’s going on and what can be improved is like going on a treasure hunt. Here are some of the items I find on a recurring basis.
Cache. Are you caching your images and script files? Chances are the answer is yes, since these items are set to be cached in IIS by default. But sometimes a quick sanity check can drive you insane. Running a utility like Fiddler or FireBug will show you the cache policy for all of the items loaded when your page loads. If you notice that your webresource.axd files aren’t being cached, that’s a problem, and it almost always points to a “Debug=True” attribute in your web.config file. To verify that the files are being cached, you want a result code of 304 for the HTTP Request as shown in Figure 2 below. If you see a result code of 200 and “Caching” shows up as “Private” as shown in Figure 1 below, that’s a good indicator that your application is running in Debug via the Web.Config setting. The behavior is there to prevent you from looking at stale javascript files when debugging your application, but it can slow down your site performance if that setting leaks out onto your production or test environment. In addition to forcing the JS resources to be downloaded for each page, Debug=True also slows down the server-side execution of code since it enables logging and instrumentation. One word of caution, don’t try to make sense of caching with the ASP.NET WebDevServer (file system project). The caching behavior of that simple web server doesn’t appear to follow the same rules as IIS. And here’s a quick tip that will prevent you from ever deploying a web site in debug mode. Add retail=true to your machine.config on your web server as shown in listing 1 below.
<configuration> <system.web> <deployment retail="true"/> </system.web> </configuration>
Listing 1: Using the retail=true attribute to prevent running a production site in debug mode.
Figure 1: Fiddler result of a non-cached response
Figure 2: Fiddler result of a cached response
ReallyLongIdStringsBecauseWebFormsIsUserFriendly. ASP.NET WebForms makes controls addressable through an ID property. The ID’s are then ensured to be unique through the UniqueID property by prepending each control’s ID with the ID of it’s Parent or Container. This is a very nice convenience for developers, especially when using UserControls where you may not have direct control over the ID of the child control. The problem though, is that the ID’s can become ridiculously long. The average WebForms application starts out with a MasterPage with ContentTemplates. So you’re already starting out one level deep in a control hierarchy for any content controls. But most content controls are then placed in panels, which house UserControls. You get the point. Before you know it, the UniqueID of a control consists of the ID of 4 other controls. Want to reduce the size of the rendered HTML? Reduce the length of the ID’s of the controls. This is one case where you may have to sacrifice readability for performance. Alternatively, you can try changing the ID of the controls in the Page_Load event, just be cautious. Changing ID’s of controls partway through the page lifecycle may disrupt some controls, you should certainly test this one before diving in. On the bright side, ASP.NET 4.0 promises to fix this problem, by adding a ClientIdMode property.
Viewstate. Though many times thought of as the enemy, Viewstate is your best friend, of the high maintenance variety. Viewstate is the glue that makes a series of disconnected postbacks feel like a continuous application. Viewstate can reduce the number of times you have to hit your database, and is the reason behind the TextBox ValueChanged event. But with great power comes great responsibility. Since Viewstate is passed between the client and server as a hidden input field, it acts as a double whammy during a postback. First the data must be sent up to the server in the form Post (including an AJAX callback), and then the data must be sent back down to the client browser as part of the HTML response content. Keep in mind that on most internet connections, upload speed is much slower than download speeds, even in today’s broadband world. The form post is an upload, which means the Viewstate data will take longer to post up to the server than to download to the client browser. Viewstate isn’t a bad thing, but it should be used in moderation. In my experience, Viewstate isn’t necessary the majority of the time, which means unless you need to use it, you should disable it. If you’re used to using Viewstate, the one big change you’ll need to make is the check for if(!IsPostback) in your page_load. With Viewstate disabled, changes you make in the page_load will no longer be saved from one post to the next. But just thin about the overhead you incurred by taking a value, encoding it, sending it to the client, posting it back to the server, decoding it, all just to set a property on a control? That’s an example of where Viewstate just isn’t necessary, and can easily be turned off.
Compression. As we’ve seen, making the page load faster usually boils down to reducing the size of the HTML content. One quick and easy way to shrink the HTTP response is to enable compression on your web server. HTTP Compression, also sometimes referred to as gzip compression is used to compress the response from the server, which is then decompressed when it reaches the client browser. All modern browsers (IE 4 and up, Every version of Firefox, Netscape 6.02 and up, Opera 5.12 via http://www.http-compression.com/) support http compression. Compression also works wonders on JavaScript files, which are usually full of whitespace characters.
Inline CSS, JavaScript. Above we talked about the importance and benefits of caching. Every character of inline CSS and JavaScript that you put in an ASPX page, is a character that could have been cached but isn’t. Luckily, this is one of the easiest fixes out there – just move the CSS and JavaScript code off to an external file.
Improving performance of a web application doesn’t have to be mad science, and doesn’t need to be done with expensive profilers. There are a number of steps you can take before going down to that level. The ideas above should give you a good start, and hopefully a better understanding of what to look for the next time you’re tweaking a page for performance. Keep in mind that each of the items listed above need to be examined to understand the value. If turning off Viewstate is going to take you 2 weeks to rework your code, and you’re only going to trim 1kb from a page that’s 800kb in size, the effort is far larger than the reward. As a general rule, I try to aim for <500kb for a web page. If you’ve tried all of the above, and still can’t get your page size under control, look at what the major constituents of that size is. Sometimes it means reworking your UI, and splitting one page up into two. Sometimes it means turning on Load On Demand functionality for data rich items like Trees, Drop Down lists, or Grids. Whatever the case may be, your first step to discovering the problem is a simple “View Source” away.