Tuesday, April 22, 2008

Performance of Web Applications

There are a lot of things that can be done to a web application, both on the front end and on the back end, to make it run faster, use less bandwidth and utilise less server processing power. In some cases the results can be dramatic to the end user, and in other cases the results can be dramatic to the number of concurrent users a server/bandwidth will support. In all cases it is a very useful and productive exercise. We spent quite a lot of time trawling around the web and various forums and user groups trying to put together a standard list of performance enhancements that have a low overhead for implementation, are repeatable and easy for developers to put into general practice without headache, and which produce measurable improvements. I’ll summarise the pared down list here, but for anyone keen to extract every last processing cycle from their servers, I’ve listed a “top 10” set of links at the end of this article to some of the performance articles we found most illuminating, all of which contain very useful information. And although it’s not aimed at .NET applications specifically, if you just pick one to explore, I really recommend the yahoo performance best practices list (http://developer.yahoo.com/performance/rules.html )

JavaScript single file combination/minification
Replace multiple JavaScript files on a web page with one large (minified) file. Copy every JavaScript file that is used for a particular webpage into one single master JavaScript file called say “AllScript.js”. Then replace the "script" tags that reference these files with one single script reference for the “AllScript.js” file.

This script reference should be placed as close as possible to the bottom of the webpage so that visual content is loaded first without js files slowing down the loading of content such as css files and images/media. All JavaScript within a page should be transferred to the external JS file.
The master JavaScript file can be further minified be using a handy program called JSMIN (http://www.crockford.com/javascript/jsmin.html) which removes whitespace from input file and returns minified file on output. This minified version can be referenced just the same by referencing it like “AllScript_min.js” at bottom of aspx file. This file can also be referenced in the asp:ScriptManager/ToolScriptManager control by setting the ScriptReference tag's Path attribute to the path of the file. If the attribute LoadScriptsBeforeUI is set to false then any referenced JS files are placed at the bottom of the webpage when rendered.

Using the Firebug tool for Firefox ([link]) we can inspect all the JS files that are requested and downloaded to the browser as the page runs. When the AjaxControlToolkit is used the client-side JS files that it uses are named “ScriptResource.axd” are dynamically referenced and downloaded to the browser. This results in a large number of separate requests (which we want to avoid) so an option exists where these files can be combined into one single HTTP request. This can be done by setting the CombineScripts attribute on the ToolScriptManager control to true. ToolScriptmanager inherits from the ScriptManager control so it is fine to substitute for the ScriptManager control in aspx pages. (link)

CSS single file combination/minification
CSS files should be referenced in the head section of the HTML/aspx page as we want the visual to load before the script files. Similar to the above JS single file combination/minification process, we can combine all referenced CSS files required for a particular page into a single master CSS file called say “AllCSS_min.css” and just reference this in the "link" tag inside the header. The CSS files can be simply copy/pasted into master CSS file and a tool called CSSMIN (http://weblogs.asp.net/zowens/archive/2008/02/15/improve-asp-net-performance-cssmin.aspx) minifies these into one single CSS file.

IIS 6.0 compression
Enabling compression is a must. On IIS 6.0 (e.g. windows 2003 server) compression of files are on by default for static compression. To allow for dynamic compression this can be activated by running a script or activating it through IIS 6.0. The below reference explains the procedure.
http://www.microsoft.com/technet/prodtechnol/WindowsServer2003/Library/IIS/502ef631-3695-4616-b268-cbe7cf1351ce.mspx?mfr=true

CSS sprites/multiple image combination
For each image referenced for a particular page in your website a separate HTTP request is issued to download it to the browser. If you have a large number of images this can use a lot of bandwidth and is not so efficient.

Images can instead be combined into one larger image and this can be downloaded as one HTTP request. On the browser sections of the webpage that want to display an image such as elements can all reference the same master images by setting the src attribute to for example “sprites1.png” and supply offsets for the background-position property to “pick out” the image required from the master image. This process is made easier by a 3rd party tool called CSS Sprites Generator http://www.csssprites.com/. Simply upload all the images used on a particular webpage and hit generate and this website automatically combines all images into one single image supplying offsets as shown:

.info {background-image:url(sprites1.png);
background-position:-66px -66px;
}
.lightning {
background-image:url(sprites1.png);
background-position:-66px -246px;
}
.magnify {
background-image:url(sprites1.png);
background-position:-66px -510px;
}

NOTE: Elements that use the CSS attribute repeat for images cannot be used in this process. Also animated .gifs will not work either.

And to add new images to the master image you need to upload all the previous images in the master image in the same order again to keep same offset values. It is important to keep a record of the offsets for each image within the master image for reference.

Web.config/Machine.config optimal settings
For production websites, it’s important to remember to set the setting in Web.config. This ensures no unnecessary debug code is generated for release version of website.
If you are not using some of the asp.net modules such as Windows Authentication or Passport Authentication etc then these can be removed from the asp.net processing pipeline as they will be unnecessarily loaded otherwise. Below is an example of some modules that could be removed from the pipeline:

ASP.NET Process Model configuration defines some process level properties like how many number of threads ASP.NET uses, how long it blocks a thread before timing out, how many requests to keep waiting for IO works to complete and so on. With fast servers with a lot of RAM, the process model configuration can be tweaked to make ASP.NET process consume more system resources and provide better scalability from each server.

The below settings can help performance:

Caching 3rd Party data & generated images
If you are acquiring data from 3rd party sites (e.g. RSS feeds, mashup data, web services, etc) then it can be a good idea to cache this data for short periods (depending on how “real-time” the data needs to be). It can make a significant difference in page loading time when there are many remote requests for this sort of data. In our case for example, we allow users to specify RSS feeds that they are interested in monitoring. Since many users can specify the same popular feeds, we can cache the RSS data returned from remote site as XML and store it in the Database for a short period (e.g. 10mins). By doing this only the first person to request the RSS feed will have to experience the delay whereby our server has to send off a request to the remote server where the RSS data resides. All subsequent users during the cache period will receive their data directly from our Cache, negating the latency and bandwidth requirements associated with contacting the remote server.

We also use a 3rd party charting control that generates an image (.png/.jpeg) on the server when it creates a chart. We cannot cache these images where users specify user-specific parameters to generate them, but when images are generated which are the same for each user, (e.g. default chart images that only update on a daily basis), then we can cache them for 1 day and avoid the expensive process of recreating this chart image every time a user requests one of these “default” type images.

Further Reading:
1) http://developer.yahoo.com/performance/rules.html
2)
http://msdn2.microsoft.com/en-ie/magazine/cc163901(en-us).aspx
3)
http://msdn2.microsoft.com/en-ie/magazine/cc163854(en-us).aspx
4)
http://msmvps.com/blogs/omar/archive/2007/03/16/asp-net-ajax-in-depth-performance-analysis.aspx
5)
http://msdn2.microsoft.com/en-us/library/ms998549.aspx
6)
http://www.codeproject.com/KB/aspnet/10ASPNetPerformance.aspx
7)
http://www.maxkiesler.com/index.php/comments/decrease_load_time_and_increase_roi_in_web_20_and_ajax_sites/
8)
http://www.testingfaqs.org/t-load.html
9)
http://visualstudiomagazine.com/features/article.aspx?editorialsid=1748
10)
http://msdn2.microsoft.com/en-us/library/ms973839.aspx

Scott Tattersall is lead developer of stock alerts, stock charts, and market sentiment for Zignals

0 comments: