Click here to Skip to main content
Click here to Skip to main content
Technical Blog

Tagged as

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

, 22 Mar 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
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 when on 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.

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:

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

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:

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

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

Finding a caching strategy for you application can take some time, but with the above techniques you should be able 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)

Share

About the Author

Markus Greuel

Germany Germany
No Biography provided

Comments and Discussions

 
Questionother recommendations to reduce First Byte Time Pinmemberliyujia23-May-13 22:21 
AnswerRe: other recommendations to reduce First Byte Time PinmemberMarkus Greuel23-May-13 23:47 
GeneralRe: other recommendations to reduce First Byte Time Pinmemberliyujia27-May-13 19:39 
GeneralRe: other recommendations to reduce First Byte Time PinmemberMarkus Greuel27-May-13 21:53 
GeneralRe: other recommendations to reduce First Byte Time Pinmemberliyujia28-May-13 15:08 

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

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

| Advertise | Privacy | Mobile
Web03 | 2.8.141015.1 | Last Updated 22 Mar 2013
Article Copyright 2013 by Markus Greuel
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid