Chapter 11. Tweaking ASP.NET Web Forms performance

This chapter covers

  • Improving HTML
  • Changing your settings
  • Utilizing a code profiler

In the previous chapter, we started looking deeper into the performance of an ASP.NET MVC application. In this chapter, we begin to look at specific techniques for ASP.NET Web Forms performance. (Chapter 10 focused on ASP.NET MVC performance.)

As you’ve progressed through this book, you’ve cut the load time of the Surf Store application in half. That is pretty impressive, considering it was all done using front-end optimization techniques. But what happens when you’ve optimized the front end of your website as much as possible, yet your web pages still load slowly? This is a good indication that you should look at the code that powers the application. Although the performance of a website can be drastically improved by focusing on the front end, there will undoubtedly come a time when a back-end code issue will affect your application.

In this chapter we’re going to look at ASP.NET Web Forms-specific techniques that will help improve your page load times as well as the overall performance of your application. Then we’ll look at a tool that will help you identify bottlenecks or inefficient code in your application. This profiling tool is different from the tools you’ve used so far, because it will integrate with the back-end code and pinpoint exact pieces of code that may be causing bottlenecks.

11.1. HTML improvements

A lot of handy features have been built into ASP.NET that make your life as a developer easier. Although these features are great, they don’t always generate performance-optimized HTML. In previous versions of ASP.NET, the framework modified the client-side IDs to identify each unique control. This tended to create HTML that left you with the ID of each control that you defined in the markup with something that looks like "ctl00_MasterPageBody_ctl01_Textbox1".

At first sight, this doesn’t seem like an issue, but as more nested controls were added to a page, the IDs of these controls became longer. This inevitably led to bloated pages. If you’ve ever tried to write client-side JavaScript against one of these pages, you’ll know the frustration this can cause: each control generated a unique ID at runtime, so you couldn’t identify a control until the page was displayed.

Apart from making JavaScript development easier, these HTML improvements also bring another benefit: performance. In chapter 6 you learned a few of the performance benefits HTML5 can bring to your application, reduced page size being one of them. If you have a large and complex web page, making sure the client-side IDs are shorter and easier to identify also reduces overall page weight.

One of the key concepts we’ve been focusing on in this book is reducing the size of the request a web page makes; this includes the size of the web page itself. Remember, by using less HTML you’re also using fewer bytes when a page loads.

In ASP.NET 4.5 Web Forms, every control contains a property called ClientIDMode that selects the behavior of the client-side ID. You have a choice of four possible values:

  • AutoID— This is the default mode and will generate any client IDs the way that it has in previous versions of ASP.NET. You’ll notice client IDs that are similar to "ctl00_MasterPageBody_ctl01_Textbox1".
  • Predictable— This mode is used when the framework needs to ensure uniqueness in a predictable way. It trims any "ctl00" ID strings from the client ID.
  • Static— This mode puts full control in the developer’s hands: it will generate an ID with the name of your choosing.
  • Inherit— This looks to the control’s parent to get its value for ClientIDMode. It tells the control to defer to the parent container control’s naming behavior mode.

Let’s look at the output the different ClientIDModes generate. The following listing shows the markup in Visual Studio before you’ve rendered the page.

Listing 11.1. Working with ClientIDMode in Visual Studio

As you can see from the code, changing how you render the textbox ID is easy. The HTML in listing 11.1 will render and display like the HTML in figure 11.1.

Figure 11.1. Generating different IDs with ClientIDMode on an ASP.NET control

In figure 11.1, the textbox with the shortest and easiest to understand ID is TextBox4, which was generated with a Static ClientIDMode. Notice the name property on the textbox is still a lot longer than it needs to be. Unfortunately, this is built into ASP.NET Web Forms and it isn’t a good idea to remove it. ASP.NET uses the name property to locate the Postback control and to route Postback data and events.

As it stands, you’ve managed to reduce the length and simplify the ID of a server-side control. Apart from making it a lot easier to read the control’s ID, you have also reduced the number of bytes on the web page.

I know this may seem like a trivial amount, but it can add up if you have a lot of server-side controls on a web page. I took a popular e-commerce website that uses ASP.NET Web Forms, looked at the HTML from a standard product display page, and compared the results before and after shortening the client IDs. The original page weight was 163 KB; it was reduced to 137 KB once the updates were made. Shortening the names of the input fields saved 26 KB. I changed nothing else!

Making changes to existing HTML sounds easy in theory, but it’s a lot harder in practice because there can be loads of dependencies to the controls’ IDs. If you’re about to embark on a new project, do it the right way from the start. If you’re developing new areas on an existing site, remember that the weight of the client IDs can quickly add up.

11.2. Web.config settings

There are a few simple changes you can make in the Web.config file of your ASP.NET Web Forms application. A wide range of settings can be finely tuned and tweaked in the Web.config file to improve the performance of your application. Knowing when and where to apply these changes is what’s important.

11.2.1. Publishing your application in Release mode

If you are about to deploy your website, one of the most important things you can do is make sure it’s been compiled and deployed in Release mode. You may be familiar with the Configuration Manager drop-down available in Visual Studio, shown in figure 11.2 and repeated from chapter 10.

Figure 11.2. Visual Studio allows you to publish your code in two different modes: Debug and Release.

As the name states, Debug mode is for debugging and is meant to make your life as a developer a lot easier. Visual Studio’s built-in debugger allows you to pause and step through code in order to debug and visualize the values in your application. Unfortunately, Debug mode isn’t ideal for code performance. When your code is running in Debug mode a number of nonoptimal things are happening:

  • Code executes more slowly because additional debug paths are enabled.
  • Much more memory is used by the application at runtime.
  • Certain timeouts are disabled, so you’re blind to any long-running operations that should have timed out.
  • The bundling and minification code you wrote in chapter 5 won’t be enabled because it runs in Release mode.

A quick way to see if your application is running in Debug mode is to look at the Web.config file. You might notice something similar to the next listing.

Listing 11.2. A Web.config file with debug=true attribute

The listing contains a snippet of settings from a typical ASP.NET Web Forms Web.config file. The debug=true setting indicates the code is running in Debug mode. It’s not ideal for your application to be running in Debug mode in a production environment. The preferred way to switch to Release mode is to deploy your application using Visual Studio and publish in Release mode. You can also remove debug=true from your Web.config or replace it with debug=false. The application pool in IIS will recycle and the application will then run in Release mode.

This setting is important! If you’re running your application in a production environment in Debug mode, it’s almost definitely running slower than necessary, so make sure you publish your application in Release mode.

11.2.2. Disable tracing if it’s not used

Tracing is a great feature that’s been built into the ASP.NET framework. It enables you to track the diagnostics of an application to the page’s output by sending information to the requesting browser. Optionally, you can view this information from a separate trace viewer that displays trace information for every page in an ASP.NET web application. Tracing comes in handy when you need to investigate unwanted errors or results while ASP.NET processes a page request, but you’ll pay a price.

Tracing adds performance overhead and might expose private information, so you should enable it only while an application is being analyzed.

To disable tracing, turn it off in your Web.config file. The code in the next listing shows how simple it is to disable tracing in your application.

Listing 11.3. Disabling tracing in Web.config

After setting enabled=false on the <trace> tag, you won’t be affected by the negative performance hits that tracing can cause.

11.2.3. Disable session state

A handy built-in feature of the ASP.NET framework is session state which allows you to store and retrieve values into a session for a user. As the user navigates through the pages in your web application, you can easily retrieve these values from the session. This feature is handy because HTTP is a stateless protocol, meaning a web server treats each HTTP request for a page as an independent request. By default, the server retains no knowledge of variable values used during previous requests. You may find yourself in a situation where it’s necessary to store certain values for the duration of a user’s session. I personally have a bit of a love/hate relationship with this feature. It’s useful, but you can find yourself dumping values into the session state unnecessarily, causing it to become bloated.

ASP.NET session state is enabled by default for all ASP.NET applications. Although this is useful and you can start using it right away, you’ll pay the price in memory, even if you don’t use it. Session state requires memory to store the values and it can also be time consuming when you store or retrieve values from memory.

You can disable session state in your application in a number of ways. If you aren’t using session state on certain pages, you can and should disable it on your web form with the following code:

<%@ Page EnableSessionState="False" . . . . . %>

You may also find yourself in a situation where you may be reading from session state, but not writing to it. You can also set session state to ReadOnly, shown in the following code:

<%@ Page EnableSessionState="ReadOnly" . . . . . %>

The preceding code sets the session state for a page to ReadOnly, but if you aren’t using session state, you may want to disable it across the entire application. You can disable it easily in your Web.config file:

<sessionState mode="Off" />

Minimizing the use of session state increases the overall performance of your application, particularly under high traffic. Although it may not increase the load time of a web page immediately, it will definitely improve the health of your application as a whole.

11.2.4. Disable ViewState when it’s not needed

ViewState is a technique used by an ASP.NET web page to persist changes to the state of a Web Form across Postbacks. The ASP.NET framework will encode a serialized object as a binary Base64-encoded string and add it to the page. The HTML in the next listing might look familiar.

Listing 11.4. ViewState inside an HTML page

The HTML markup in the listing shows how a hidden field is added to each page in a web application. This technique has been around since the early days of ASP.NET and continues to be a powerful and simple way to persist small pieces of data. ViewState allows state to be persisted with the client and it doesn’t require cookies or server memory to save this state. However, the Base64-encoded string that is included in a web page can sometimes add an overhead of about 30 percent to your web pages. The time it takes for the object to get serialized also comes at a small cost.

ASP.NET ViewState is an important part of maintaining state in your application. Without it, certain pages and controls on your website won’t function properly. Proceed cautiously and disable it only when it’s absolutely not needed.

A page that might not need ViewState might be one that displays information and does not post back to itself. To disable ViewState, include the following code in your page directive:

<%@ Page EnableViewState="false" . . . . %>

Disabling ViewState on pages that don’t need it improves the performance of those pages significantly. The extra data added to the page will also add to the weight of the HTML returned from the server, and by removing it, you’re making sure your web page stays light.

11.3. Response.Redirect vs. Server.Transfer

In chapter 2 we discussed the HTTP response codes a server can return to the browser. When you use Response.Redirect() in your code to redirect a user between pages on your website, a 302 HTTP response code is returned from the server. A 302 HTTP status code indicates to the browser that it should perform a temporary redirect. A Response.Redirect() will send you to a new page and update the address bar with the new page. Unfortunately, Response.Redirect() sends an extra request to the server, which could be avoided.

An option is to use Server.Transfer() instead. Server.Transfer() helps reduce server requests because it happens without the browser knowing anything. When the browser requests a web page, the content of another page is returned. Instead of telling the browser to redirect, it changes the focus on the web server and transfers the request. You don’t get as many HTTP requests coming through, which eases the pressure on your web server and makes your applications run faster.

With different methods come different challenges. Using Server.Transfer() doesn’t change the URL in the address bar of the browser. This can be quite confusing during debugging, and because the URL doesn’t change, a user might bookmark the wrong page. You also can’t use Server.Transfer() to redirect to a page on an external site, it will only redirect within the same application.

If used in the right circumstances, Server.Transfer() can be quite powerful and will reduce the number of HTTP requests coming to and from your server. This will definitely lighten the load on your server and improve the overall performance of your web application.

11.4. Utilizing a code profiler

In parts 1 and 2, we concentrated solely on front-end performance and improving the page load times in a web application. In those chapters, you learned that the biggest gains in terms of page load times can be made on the front end because the changes are scalable, easy to implement, and relatively fast—wait time is how long it takes for the components in a page to download. You took an ASP.NET Web Forms application from a dismal page load time to a speedy, high-performance load time.

But what happens when you’ve optimized the front end of your website as much as possible, but you still experience a long delay when waiting for the page to load? It may be time to take a closer look at your website’s back-end code. Figure 11.3 shows a waterfall chart for a web page with a bottleneck in the back-end code.

Figure 11.3. A waterfall chart shows that of all the components, the HTML takes the longest to return. This is an indication that you need to look at the back-end code to improve page load times.

Trying to diagnose the bottleneck can be extremely frustrating and you may find yourself looking in the wrong place. The only way to find the bottlenecks in your back-end code is to use a profiling tool. There are many profiling tools that will help you profile and locate the source of your performance issue or memory leak.

These are a few of the better-known tools:

One of my favorite tools to use when profiling an ASP.NET website is the MiniProfiler tool. In chapter 10, you saw how it can be used to profile an ASP.NET MVC application. The tool is flexible and built to be used against ASP.NET Web Forms applications, too. It’s a free download and allows you to profile your Web Forms application as well as any database queries, Entity Framework queries, Linq2SQL queries, and individual pieces of code.

In this chapter, you’re going to set up the Surf Store application so you can profile the code with MiniProfiler. The sample code for this chapter is located in the chapter 11 folder. The code for chapter 11 has changed slightly because I’ve updated the sample application to use a local database instead of retrieving the images from disk. As you’re focusing on the back-end code now, I wanted to get as close to a real-world scenario as possible. To create a challenge for the profiler, I purposely injected code to make the website perform slowly when hitting the database.

Let’s start profiling. First, you need to add the MiniProfiler library to the sample application. You can download the library from miniprofiler.com, or by using the NuGet package manager in Visual Studio 2012. NuGet is a free, open source package management system for the .NET platform. Instead of searching for an open source library on a website, NuGet contains a list of thousands of free libraries that easily integrate and download into your application. It’s handy because it allows you to add a library to your application without having to visit multiple websites; the libraries are all in one place and are easily searchable. Navigate to your Solution Explorer and right-click References. Next, click Manage NuGet Packages. Figure 11.4 shows this in action.

Figure 11.4. To add a NuGet package to your application, right-click References in the Solution Explorer.

Next, search for MiniProfiler in the search bar and the NuGet package manager will locate the MiniProfiler package for you. Click Install and the required dependencies will be added to your application. Figure 11.5 shows the NuGet package manager and the interface that allows you to easily locate and download the libraries you want to add to your application.

Figure 11.5. The NuGet package manager UI

Open the Master page and add a piece of code that allows you to see the code profile results. Add the code in the next listing to the <head> tag of your HTML.

Listing 11.5. Add UI components to the Master page

The example has been shortened to keep it simple, but you’re including code that will write out the CSS and JavaScript required to output the profiling results to the web page.

Now you need to update the Global.asax file and initialize the MiniProfiler so it starts profiling when your application fires up. This listing shows the code you need to add to the Global.asax file.

Listing 11.6. Start the profiler in the Global.asax

The code you’ve added makes sure the profiler is executed only if the application is running locally. If it detects a local HTTP request, it starts the MiniProfiler. This check is added for security reasons, in case you deployed your application to a production environment with the profiling code still enabled; you wouldn’t want anyone to see this sensitive information.

MiniProfiler is lightweight and lets you choose which parts of your application you’d like to profile. Instead of profiling the entire application, you can profile a particular piece of code. The following code snippet shows a profiling block wrapped around a piece of code.

var profiler = MiniProfiler.Current;
using (profiler.Step("Important code"))
{
    // Some important code goes here
}

The code snippet contains a reference to the current instance of the MiniProfiler. It’s then implemented in a using statement to profile a section of the code. You can have as many of these profiling blocks as you wish, and you can even use them in downstream methods. It’s also important to label the profiling block so you know where to point your code during profiling. Without labels you can easily become lost in the numerous profiling blocks!

With regards to the Surf Store application, a good page to profile would be the Products page. It’s retrieving a list of products based on a category from the database. This bit of code might be inefficient and cause the page to load slowly. Start by implementing the profiling blocks around the code you wish to examine further.

Listing 11.7. Profiling the Product.aspx page

The sample code is from the Surf Store application that reads a list of images from a database. It might not be running as efficiently as it should, and the MiniProfiler will help you identify any inefficient code. You’ll notice the code in the previous listing contains profiling blocks that are wrapped around methods and individual lines of code. They’re particularly useful if you want to manually investigate specific pieces of code. MiniProfiler will automatically tell you how long it takes for actions to execute and views to render.

That’s it. You’re now ready to fire up the application and begin profiling. If we navigate to the Products page of the Surf Store application, you’ll notice profiling details in the top-left corner of the screen. Click the millisecond duration message in the upper-left corner and you’ll see MiniProfiler profiling details (figure 11.6).

Figure 11.6. MiniProfiler results. They capture the full page cycle as well as internal code.

Notice how the profiler also details the complete page lifecycle, so if any intensive JavaScript runs on the front end, you’ll be able to identify that, too. The results also show the profiling blocks we added in listing 11.7.

You’ll notice in figure 11.6 that the Retrieve Products profiling block took over four seconds to execute. The front-end code is quite efficient in comparison and took no time at all to respond. Before starting the profiling, I purposely injected a piece of code in the SurfStoreApp.Data project that will block the current thread for about five seconds.

If you concentrate on this bottleneck and improve any inefficient code, you should be able to improve the page load time considerably.

Note

It’s important to disable HTTP caching and output caching when you profile your website with MiniProfiler. You want to identify back-end code bottlenecks, not front-end performance bottlenecks. HTTP caching will only skew the results when you reload the page!

After you remove the inefficient code, MiniProfiler immediately reflects these changes. Figure 11.7 shows how the Retrieve Products profiling block is running a lot quicker now, and your overall page load time has been reduced significantly.

Figure 11.7. MiniProfiler results after the code update

You were able to identify a problem area in the code easily with MiniProfiler. The profiling blocks can be chained together and used in downstream methods, so you should be able to drill down until you find the source of your code problems. By using a code profiler, you’re able to take any guesswork out of your performance issues.

11.5. Fixing the issue

The most important part of finding your website’s code bottlenecks is being able to fix the problem. Using a profiler allows you to narrow the list of possibilities and identify the root cause of the bottlenecks, but it doesn’t show you how to fix them.

Each performance issue requires its own solution. Because of the breadth of issues developers face, you’ll be required to call on your own problem-solving skills and expertise.

In this book, I’ve concentrated on front-end performance issues and given you the tools to identify problem code in your application. The best way to fix an issue buried deep in code is to keep going back to the performance cycle. It may seem like an overwhelming task, but the process of improving your website’s load times and performance can be broken down into four key stages. The performance cycle is a summary of the entire website improvement process. Its four stages are a guide to a process which can be applied to any website, regardless of the specific rules and techniques you’ll apply, helping you realize performance potential and faster load times.

As you go through the four key stages of the performance cycle, you’ll notice the improvement each adjustment to your code will make. Experimenting with different code techniques is often the best way to improve a slow running part of your website. You’ll be able to easily decide if you’re heading in the right or wrong direction by monitoring and tracking for any signs of change.

11.6. Summary

In this chapter you shifted your focus toward the back-end code of a web application. In the first two parts of this book we concentrated on the front-end performance of web applications, but there may come a time when your application runs slower than expected even after you’ve performed front-end optimization.

By default, ASP.NET Web Forms projects created in Visual Studio are not optimized for performance. A lot of features enabled by default might not be necessary and could cause extra overhead in your application. Since ASP.NET 4, there have been HTML improvements that allow you to provide cleaner HTML that will reduce the overall weight of your web pages.

This chapter highlighted the importance of publishing your applications in Release mode. If your web application is running in Debug mode, your code will execute slower and your application will use more memory. When you deploy your site to a production environment, make sure it’s running in Release mode.

We investigated different settings in the Web.config file and examined the pros and cons of disabling certain features to improve performance. Under the correct circumstances, disabling tracing, session state, or ViewState can speed up your application’s page load times significantly. By fine-tuning certain settings, you can make sure your Web Forms application is running at its peak performance.

When looking to optimize your back-end code, you will often need to go one step further and use a profiling tool to pinpoint exact bottlenecks in your code. Implementing tools such as MiniProfiler will allow you to profile your code and identify any performance issues.

This free tool integrates easily with ASP.NET Web Forms and can provide you with useful information that will allow you to pinpoint the exact source of your performance problem.

In the next and final chapter of this book, we take a look at object caching. It involves caching frequently used data in order to speed up your application’s page load times.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.229.33