Optimizing page speed performance for my MVC 4 web application

In preparation for publicizing my latest web app (http://howcanidecide.com), I’ve been running it through some performance tests.

Some of the performance changes are a bit of overkill for my site (at least, with the current amount of traffic it gets). However, this gave me a chance to try out some things that will come in useful on other projects. Plus, this site may actually become popular and need to handle a heavier traffic load.

Here are the sites I used, and the changes I’ve made, in case you want to do the same.

Sites to analyze performance

The initial thing I’m looking at is how quickly a first-time user sees the home page. To find potentially slow areas, I used these sites to evaluate my site.

Pingdom shows all the requests made when accessing your page. With this information, you may spot problems such as having multiple JavaScript or CSS files. Most browsers only attempt to download a few objects at a time (from each domain). So, if you have many files to download, that may serially, instead of in parallel. That means your users need to wait longer for the complete page to load. This tool gives you some performance tips, based on problems it sees on your site. Currently, my site’s grade from here is 97/100.

WebPageTest is another site that evaluates potential performance problems on your site. It does two loads of the page, in order to see how files cached in your user’s browser will influence performance. It will give you a detailed list of things to improve when you click on the “PageSpeed score” link after you run your test. My site’s current score is 93/100. I may make a few improvements later, but I believe it’s good for now.

Google has a performance analysis tool named PageSpeed Insights. It breaks the results into three categories: high, medium, and low priority. Right now, I only have three low priority items listed, and my site has a score of 93/100.

I also use UptimeRobot to monitor the site’s uptime. Strictly speaking, that doesn’t improve performance; however, I do like knowing if the site suddenly stops working. Since it checks my site every five minutes, the front page may also be a bit fresher in IIS’s memory, and not needed to reload it if no one visits the site for a while.

Server changes to improve performance

The first thing I did was to put the site behind a CDN (Content Delivery Network).  Cloudflare (https://www.cloudflare.com) offers a free CDN service that also helps prevent things like denial of service attacks and common script attacks on your website. They also have some paid features, but I don’t really need them yet.

I recently added a video of how/why to use the website on the home page. It’s a 1.9 megabyte mp4 screen capture of me entering a sample decision (done with Camtasia). I started out by hosting it on my site, but decided to move it to an Azure storage BLOB. My expectation with Cloudflare is that it “should” cache the file in their CDN network, but I wanted to be extra certain. Plus, I wanted an excuse to play with Azure a bit, and they have a free three-month trial.

Solution changes to improve performance

I built the site using ASP.Net with MVC 4. If you create a standard MVC 4 application in MVC 4, many things are included in the project that you may not need. So, I started by removing anything I didn’t use for this project.

Removed unused JavaScript files

By default, and MVC 4 project includes many jQuery files and themes that you may not use. I didn’t need them for my site, but they were being included in the standard bundle, which was being loaded in the default _Layout.cshtml view. So I removed them from the project and the BundleConfig file.

Moved JavaScript inline and minified it

Since I only have a few hundred bytes of JavaScript, I included it directly in the _Layout page. Initially I had it in a separate file, so it would cache in the users’ browsers. However, when running the page speed tests, there were suggestions to move it inline.

In order to keep it easy to work with, I don’t have it minified in my views. In order to minify it when it goes to the user’s browser, I added a NuGet package named BundleMinifyInlineJsCss. I’m not 100% happy that this runs with every page view. However, I doubt the processor demand will be excessive.

To do the same thing, see post http://weblogs.asp.net/imranbaloch/archive/2012/07/25/bundling-and-minifying-inline-css-and-js.aspx.

Removed unused CSS classes

While writing the web app, I made changes to the CSS several times. Because of that, I ended up with a few classes I don’t use anymore – so I removed them.

Since my CSS file is small, I just manually went through it, searching for each class within the rest of the project. An automated tool, or Visual Studio plugin, would be nice to find.

Removed CSS file bundling

I only needed one CSS file for the site, so I also included the reference to it directly in the _Layout page, instead of using the bundles.

Removed unneeded BundleConfig and WebApiConfig

Since I’m not using any bundles, I don’t need the BundleConfig file. So I deleted it from the App_Start directory. Then I went into the Application_Start method, in Global.asax.cs, and removed the line with BundleConfig.RegisterBundles

I did the same thing with the WebApiConfig, since this site doesn’t really need a web API.

Removed excess references

You may not need all the default DLLs included in your project. Removing them doesn’t improve your site’s performance. However, it does give you less to upload when you make a site update. So you’ll end up with a little less downtime.

I used ReSharper (from the ReSharper menu in Visual Studio, select “Find”, then “Optimize References”) to find these references. However, if you do this too, you need to beware. Some of the references that were shown as not needed, were actually needed. Even though the site ran fine locally after I removed them, when I deployed it to my production server I got errors about missing references. So, if you do this, test it in your production environment after you remove each references.

Asynchronous logging

When a user logs in, I record an entry in a log table. This gives me a way to identify when the site is use, how often a user returns, etc.

I don’t want to have the user wait while the log record is inserted (even though it only takes a tiny fraction of a second), so I have the insert query run asynchronously. Here is how I did it:

Include System.Threading.Tasks in the class where you do the asynchronous SQL command. Then, I wrap it like this, which will call my ExecuteNonQuery function asynchronously, passing in my sqlCommandObjectCreator parameter – which builds the SqlCommand object to execute. By wrapping this with “using()”, I ensure the task object gets disposed.

Configure items to compress in the HTML response

A common way to improve the performance of your site is to compress what you send to your user’s browser. However, by default, not all common file types are compressed.

Open your Web.config file and find the system.webServer section. I added this, since it covers the file types in my app:

Set items for the user’s browser to cache

While you’re in the system.webServer section of your Web.config, set your client cache to a long time (assuming the objects your app generates are static). I added this to set the cache expiration to 30 days.

Summary

That’s what I changed for optimum server performance.

For a first-time visitor, the home page usually loads in somewhere between .8 and 1.4 seconds, which seems good to me. With that, and the fact that my scores for the performance analysis tools are all in the 90s, I think it’s ready.

Of course, I’ll monitor the site, look for any performance problems, and make improvements as needed. But at a certain point, you need to say, “That’s enough – for now.”

Leave a Reply

Your email address will not be published. Required fields are marked *