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