Click here to Skip to main content
15,886,032 members
Articles / Web Development / ASP.NET

Website Performance with ASP.NET - Part 2 - Reduce Time to First Byte

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
22 Mar 2013CPOL4 min read 16.5K   7   5
This post shows how the time to first byte of ASP.NET pages can be reduced by profiling the code and implementing data and output caching.

In the previous post, we learned that the performance of the example application could use some improvements and already identified measures which might help. Now, it’s time to start exploring the options we have implementing these.

Profiling the Server Side Code

The server side performance is not the focus of this series. It is often the first (or even only) part which many ASP.NET developers focus on when thinking about performance. When looking at page load times, it is often overrated as it is only one of many factors to consider. Nevertheless, should you take a look at your time to fist byte (i.e. execution time on the server + transport). Especially your HTML should be delivered as fast as possible as all dependent resources (images, css, js) are not loaded until the HTML has been parsed by the browser.

From the tests performed in the previous post, we already know that the time to first byte in our example application is bad. Now we need to find out what is causing this. Tools like dotTrace, ANTS Profiler or the Visual Studio Performance Profiler can help you to identify the performance hot spots of you code. I used dotTrace to profile the sample application and it pointed to a single method consuming almost 90% of the request time.

Image 1

Ok, I have to admit, for demonstration purposes, I cheated a little bit. If you take a look into the source code of the SampleProductDataSource in the unoptimized branch, you will find these lines:

JavaScript
private static Product[] GetProducts(CultureInfo culture)
{
	// Simulate slow DB Access
	Thread.Sleep(500);
	...
}

That’s obviously nothing you should find in production code, but a slow databases and/or inefficient data access code are unfortunately not uncommon. You can, of course, tune the data access code and/or the database. It is, however often times more efficient to prevent the data access rather than optimizing it. You usually don’t need to get the latest data for every requests. Stale data is often acceptable. Take the shop example: There is no reason to load static product information hundred or thousand times per hour, if the information is updated only once per day.

Data Caching

A simple fix for relatively static data like the product information is to use a data/object cache. The .NET Framework includes the System.Runtime.Caching.MemoryCache which implements a simple and thread-safe in-memory cache. There are many more sophisticated cache implementations available, both free and commercial. But the MemoryCache is a good choice for simple scenarios. You can add items with a sliding expiration policy (e.g. 30 minutes after the last usage) or an absolute expiration policy (e.g., expire today at 11:59 pm). The optimized version of the SampleProductDataSource uses the MemoryCache to store the product information for one hour.

JavaScript
private static readonly MemoryCache Cache = new MemoryCache("storeProductData");

private static Product[] GetProducts(CultureInfo culture)
{
	var cachedProducts = (Product[])Cache.Get(culture.ToString());

	if (cachedProducts != null)
	{
		return cachedProducts;
	}

	...

	Cache.Add(culture.ToString(), productsArray, new CacheItemPolicy {
  AbsoluteExpiration = DateTimeOffset.UtcNow.AddHours(1) });
	
	return productsArray;
}

Output Caching

If not only some data but (almost) complete pages are relatively static (e.g. a product details page) the ASP.NET Output Cache offers an easy to use mechanism to cache the HTML output of a page. For ASP.NET MVC, you can add the OutputCacheAttribute to an action method to use the output cache. For ASP.NET WebForms pages, use the @OutputCache declaration. The output cache can be configured with parameters, to set the duration for which the content should be cached, as well as the storage location (browser, proxy, server). Those parameters can be set for every action/page or can be stored in profiles within the web.config.

The optimized version of the example application allows caching of the product details action for 2 hours on the server, proxies and browsers:

JavaScript
[OutputCache(Duration = 7200)]
public virtual ActionResult ShowProductDetails(CultureInfo culture, string productName)
{
	ProductDetailsViewModel productDetailsViewModel = 
           this.productService.GetProductDetails(culture, productName);
	return View(productDetailsViewModel);
}

The output cache automatically sets the corresponding cache-control headers to inform clients and proxies how long the results may be served from cache without issuing another request to the server (I will provide more details on cache-control header later in the series).

Before implementing caching, it is important to separate static content from dynamic content. Using the output cache only makes sense if the dynamic portion of the content is relatively small. Then you can render the static content within the main action and cache the result. The dynamic information can then be added by using a separate Ajax request and some JavaScript. The example application caches the content pages themselves and adds only information about the current shopping cart content using JavaScript.

JavaScript
$.getJSON(cartServiceUrl, null, function(cartSummary) {
	$(uiConfigValues.itemNumberSelector).text(cartSummary.NumberOfCartItems);
	$(uiConfigValues.totalAmountSelector).text(cartSummary.FormattedTotalCartAmount);
});

Finding a caching strategy for your application can take some time, but with the above techniques, you should be able to cover most scenarios.

In the next post, we will start digging into more front-end specific topics by looking at ways to reduce the number of HTTP requests.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionother recommendations to reduce First Byte Time Pin
liyujia23-May-13 22:21
liyujia23-May-13 22:21 
AnswerRe: other recommendations to reduce First Byte Time Pin
Markus Greuel23-May-13 23:47
Markus Greuel23-May-13 23:47 
GeneralRe: other recommendations to reduce First Byte Time Pin
liyujia27-May-13 19:39
liyujia27-May-13 19:39 
Thanks, my website is running low on time, do you have any recommentdations fro the first byte load time imprvements, the server side code improvement?
GeneralRe: other recommendations to reduce First Byte Time Pin
Markus Greuel27-May-13 21:53
Markus Greuel27-May-13 21:53 
GeneralRe: other recommendations to reduce First Byte Time Pin
liyujia28-May-13 15:08
liyujia28-May-13 15:08 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.