Skip to main content

Optimizing ASP.NET/MVC 3.0 Site

I stumbled upon the Google Page Speed Insights tools when testing ASP.MVC 3 site performance few days ago. After running the performance test, I found out that the web page I was testing has a performance index of 50% - i.e.things such as loading JS, CSS, and static files are taking away about half of the website loading time.  Here are some steps I took to optimize the site and recover much needed speed.

Plan of Action

1- Enabling Gzip Compression

Gzip is a compression algorithm that is used by several web servers and browsers to send and receive compressed http responses. By default gzip is disabled on IIS (bummer ...). You could use IIS GUI to change the settings - but for those of you who are XML ninja's here are the few lines of code that will recover some speed for your .NET 4.0 /3.5 app running on IIS7.

Code Snippet
  1. <system.webServer>
  2.   <urlCompression doStaticCompression="true" doDynamicCompression="true" />
  3.   <staticContent>
  4.       <clientCache
  5.           cacheControlMaxAge="7.00:00:00"
  6.           cacheControlMode="UseMaxAge" />
  7.   </staticContent>
  8. </system.webServer>


Google Page Speed Insights tool recommended value for client caching static resources is one week but you can change cacheControlMaxAge="7.00:00:00" to any number of days/hours based on your product development cycle.

Result: Over 25% increase in rating on Google Page Speed Insights tool

2- Minifying your JavaScript and CSS Files

Caveat:  .NET 4.5 supports minifying JS and CSS files out of the box with the new Bundle Class [1] unfortunately the features are not available in ASP.MVC 3.0.

There are several tools that can minify JavaScript and CSS files but the one I used for the my project is Yahoo UI Library compressor.

To add Yahoo UI Library compressor to your solution fire up Package Manager[2]in Visual Studio and type in the following four commands.

Step - 1 Add Yahoo UI Library DLL's to your project

PM> Install-Package YUICompressor.NET
PM> Install-Package YUICompressor.NET.MSBuild
PM> Install-Package YUICompressor.NET.NAnt
PM> Install-Package YUICompressor.NET.Web.Optimization

Step - 2 Create msbuild.xml file and point it to your JS and CSS files

Msbuild allows you to automate pre-build and post-build actions on a Visual Studio Project. To use msbuild:

2.1. Create a file called "Msbuild.xml" [3] at the root level of your project and copy the following xml file code into it. (you might want to modify the paths in the XML file to reflect your development environment)

Code Snippet
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <Project xmlns="http://schemas.microsoft.com/developer/MsBuild/2003">
  3.   <UsingTask TaskName="CssCompressorTask" AssemblyFile="bin\Yahoo.Yui.Compressor.Build.MsBuild.dll" />
  4.   <UsingTask TaskName="JavaScriptCompressorTask" AssemblyFile="bin\Yahoo.Yui.Compressor.Build.MsBuild.dll" />
  5. <!-- You can set the $(Configuration) to "release" once you have YUI compressor working to on your development environment -->
  6.   <Target Name="Minify" Condition="'$(Configuration)'=='Debug'">
  7.     <ItemGroup>
  8.       <!-- You can bundle multiple CSS files by adding them as <CssFiles ..> below -->
  9.         <CssFiles Include="$(MSBuildProjectDirectory)\styles\Site.css"/>
  10.       <!-- You can bundle multiple JS files by adding them as <JavaScriptFiles ..> below -->
  11.       <JavaScriptFiles Include="$(MSBuildProjectDirectory)\scripts\jquery-1.4.1.js"/>
  12.     </ItemGroup>
  13.     <!-- OutputFile is where your minified files will be saved -->
  14.     <CssCompressorTask
  15.              SourceFiles="@(CssFiles)"
  16.              DeleteSourceFiles="false"
  17.              OutputFile="$(MSBuildProjectDirectory)\styles\style-min.css"
  18.              CompressionType="Standard"
  19.              LoggingType="Info"
  20.              PreserveComments="false"
  21.              LineBreakPosition="-1"
  22.        />
  23.     <!-- OutputFile is where your minified files will be saved -->
  24.     <JavaScriptCompressorTask
  25.          SourceFiles="@(JavaScriptFiles)"
  26.          DeleteSourceFiles="false"
  27.           OutputFile="$(MSBuildProjectDirectory)\scripts\jquery-min-1.4.1.js"
  28.          CompressionType="Standard"
  29.          ObfuscateJavaScript="True"
  30.          PreserveAllSemicolons="False"
  31.          DisableOptimizations="No"
  32.          EncodingType="Default"
  33.          LineBreakPosition="-1"
  34.          LoggingType="Info"
  35.          ThreadCulture="en-au"
  36.          IsEvalIgnored="false"
  37.             />
  38.   </Target>
  39. </Project>

2.2. Add the following code to the Post-Build event [4] of your web project.

$(MSBuildBinPath)\msbuild.exe /p:Configuration=$(ConfigurationName) "$(ProjectDir)msbuild.xml" 

After you add the post-build event you can then build your project and get minified versions of your JS and CSS files generated. (Tip of the hat to Black River)


 3- Combile your Icons images into a single CSS Sprite
 
If you have multiple Icon files that are heavily used in your application, you can combine them into one file (CSS Sprite) and use a single file in your application for all your icons. There are several and efficient ways of doing sprites but if you have only few icons you can simply fire up your favorite image editor (I used Paint.Net) to create an image with transparent background and use it to consolidate your icons.

To create the a css sprite file:

1- Create a transparent image background
2- Add your icons onto the transparent image (Space them evenly to easily find the icons for your CSS)
3- Once you have consolidated your icons into a single image file, save the file as [yourimage].png (I am using PNG to further compress the icon file - you can skip step 4 if you save your file in a format other then PNG)
4- If you save your icon collection file as .PNG you can use online services (such as tinypng.org / or other solutions) to further compress your file
5- Modify your CSS file and point it to the new image file

Note: When modifying your CSS files use background-position:right top; to pinpoint and attach your new icons to HTML elements.

E.g. Before consolidating icons



( three separate Requests go out to the server to fetch the image files i.e.- 781 X 3 = 2.1KB)

After consolidation



(One Request goes out to the server to fetch a single CSS Sprite - Only 1.5KB)


CSS before Consolidation

Code Snippet
  1. .check
  2. {
  3.     background-image: url('images/check.png');
  4.     
  5. }

CSS after consolidation

Code Snippet
  1. .check
  2. {
  3.     background-image: url('images/sprite.png');
  4.     background-position:0px 20px; /* the exact position of the image on the CSS sprite */
  5. }

In the above example with only 3 icon files there is over  29% file size reduction which will translate into performance increase for the application.

 4- Miscellaneous Items
 4.1.  Running debug="True" in production environment is also one way of slowing down your application. You might want to double check on your compilation mode and make sure you don't have any development setting in web.config that made it through to your production box. (Obvious, right,  but it is very easy to forget after four publish jobs and six Xcopies)

 4.2. Having external CSS links between the header tags and external JS links at the bottom of an web page also improves performance.

4.3. Last but not least ... a healthy dose of daily prayer :)
 
[1] Bundling CSS and JS files is streamlined in ASP.MVC 4 (.NET 4.5) using the new Bundle Class.
[2]. In Visual Studio "View" > "Other Windows" > "Package Manager Console"
[3] I have used the name "msbuild.xml" for simplicity- you can use a different file name. If you choose a different file name you might also want to tweak the settings on step 2.2.
[4]. In Visual Studio Right click project from "Solution Explorer" > "Build Events Tab" > "Post Build Event Command Line"

Comments

Popular posts from this blog

Turning WCF Service into .asmx for debugging

Even though .asmx web services are becoming dinosaurs of the new .NET world of WCF. I missed the simplicity of debugging code right in visual studio without: Creating a client to consume WCF service Attaching w3p.exe process and Adding break points  One quick solution: Turn WCF service into .asmx service with few lines of code, debug your code with asmx, and turn .asmx off during deployment.  Detail steps: 1- First take your WCF class and add WebService attribute to it Code Snippet /// <summary> /// Dual mode with .ASMX and WCF /// </summary> [ WebService (Namespace = "http://www.yourdomain.com/" )] 2- Then add WebMethod attribute to a function you want to expose in .asmx Code Snippet [ WebMethod ] public List < PageController . Page > DualFunction() { 3- Take the .svc file from your solution - copy and rename the copied file [YourOriginalWCFFile.asmx]. Open up the copied file and rename "ServiceHost...

Processing ASP MVC Web API Requests in Multi-threaded JS Web Worker

Unlike an asynchronous Ajax call, HTML5 Web workers provide an opportunity to run a Multi-threaded JavaScript code in modern browsers that support them . Each worker spawns an isolated thread with dedicated JavaScript Event Loop, Stack and Heap memory. For example a regular Ajax Call to MVC Web API service call goes through the following asynchronous call cycle. The JavaScript Event Loop in this case could be interrupted by events that are being executed on the UI; for instance, a "window.alert", could possibly stop all scripts on the page from executing until a user responds. Replacing the Ajax Call with HTML5 web worker provides a great way to run long running scripts in separate threads so that asynchronous code execution is not interrupted by UI events. Here is the a JavaScript worker implementation of the same MVC Web API call using a JavaScript web worker. Despite the advantages of using a web worker, implementing one requires working with some constr...

ASP.MVC Real-time data processing with Sharded RavenDB and SignalR

In a move that some call "Radical” The Weather Channel recently swapped their enterprise Oracle and MySql Databases for NoSQL  MongoDB .  One word that could describe the rapid adoption of NoSQL databases compared to relational once is “ sharding .” Unlike relational databases, NoSQL systems allow developers to be active participants in the data storage process by providing a way to create logic (sharding strategy) that outlines how and where data could be stored, resulting in better scalability and improved performance. RavenDB , an open source NoSQL system for .NET that is easy to deploy (both as a standalone/embedded system) for an ASP.MVC application. SingalR is a tool that one can use in a .NET environment to add real-time client-server processing in an application. SignalR allows a developer to broadcast messages to all (or some) clients from a centralized client and/or server call. I was looking for a quick data-sharding example with embedded Rave...