Blog Post

Script Combining - What's the big deal?

Microsoft is about to ship Visual Studio 2008 SP1, and .NET Framework 3.5 SP1.  Now service patches aren't always very exciting, but these are because they contain some great new features.  You've probably already heard the buzz around AJAX History support, and Dynamic Data.  Script Combining is also part of SP1, and it's something that you should certainly be interested in.

So why is script combining is important?  ..it's all about network latency.  Conventional thinking was to break apart script files into small chunks so that the browser only needs to download the minimal set required.  The idea was great, and it could even have a positive impact on your application, until the laws of the internet start to interfere.  The problem is that each script file must initiate a connection to the server.  Connections have some degree of overhead involved from the aspect of the server, so splitting a file in 2 and serving it up over 2 requests instead of one, will take slightly longer.  But that's nothing compared to the effects of network latency.

Latency, is the minimum time it takes for a packet to travel from one location to the next.  We're all familiar with the idea of latency, as it's clearly evident when you're speaking on the phone to someone across the world.  Think of the delay before they hear what you said, and vice-versa.  The same laws of physics apply to HTTP connections.  For HTTP, the latency is assessed per connection.  So for every connection your site makes back to the server during a page load (or partial AJAX load), your load time increases by a given number.   If we go back to our phone conversation analogy, there's no noticeable delay while you're talking, but think about what happens each time you stop talking and wait for a response from the other side.  Those delays are all additive, just like in our HTTP request/response pairs. 

To put this to some hard numbers, imagine your site has 30 'objects' on the page.  An object is something that requires making a connection to the server - this could be a javascript file, an Image, a flash video, or even a stylesheet.  Now let's imagine some unfortunate soul on the other side of the world is viewing your site, or running your web application.  If their average latency is 300ms, you're talking about a minimum page load time of 4.5 seconds based on the browsers ability to have 2 simultaneous connections (300ms/roundtrip * 9 connections /2 connections per roundtrip) = 4.5 seconds

If you want to simulate this in an ASP.NET application, add a HTTPModule to your application and insert a System.Threading.Thread.Sleep(300) into the BeginRequest event.  Now for each request made to your application, there will be a 300ms delay representing the latency.

Caching Effect on Latency

So what about the effects of browser caching?  Javascript files along with CSS and Images are all cached resources for a browser.  As long as these items are in the browse's cache, the client does not need to initiate a request (http connection) with the server, which means no latency penalty is incurred.  Tip: When setting cache headers, remember that using a conditional request (If-Modified-Since header) will still require an HTTP connection.  Stick with Expires or Cache-Control headers, which will alleviate the need for any HTTP connection once the item has been added to the cache.  Learn more about caching from w3.org. You can set the cache characteristics of specific filetypes through your webserver, or by adding response headers dynamically. 

A recent study published from yahoo though, shows an astonishing 20% of page views are done with an empty cache.  While that number seems high, it certainly points out that empty cache visits are not edge case, and should be given some attention.  Besides, we all know how important first impressions are, so even if your application only loads slow the first time a user visits, that may also be the last time.

Taking Action

It should be pretty clear now that latency has a significant impact on your web site/application performance.  And the to make matter worse, it's not something that you're likely to notice during testing since your latency will be minimal.  That's why defensive programming is a priority here.  And it's not that difficult either..

Step 1 - Analyze.  Download an HTTP monitor like Fiddler (http://www.fiddlertool.com/fiddler/version.asp)  Use the tool to examine how many connections are made during an empty and full cache request. 

Step 2 - Minimize Connections.  Minimize the total number of HTTP requests by combining separate script or css files into a single file.  There are utilities out there to combine CSS files and even shorten class names.  If you're using ASP.NET 3.5 SP1 you can combine Javascript resources by using the ScriptManager's CompositeScript element.  Simply add each script element as part of a "CompositeScript" and the ScriptManager will do the combining for you.  This is especially useful when you're using 3rd party tools which may contain embedded resources, and there's no actual script file to work with.  If for instance you're using NetAdvantage for ASP.NET and have made use of the WebGrid, you can combine the multiple script files into a single through the following CompositeScript block:

<asp:ScriptManager runat="server" ID="sm1">
    <CompositeScript>
        <Scripts>
            <asp:ScriptReference Name="MicrosoftAjax.js" />
            <asp:ScriptReference Name="MicrosoftAjaxWebForms.js" />
            <asp:ScriptReference Name="Infragistics.WebUI.Shared.JS.ig_shared.js" Assembly="Infragistics35.WebUI.Shared.v8.3, Version=8.3.20083.1, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb" />
            <asp:ScriptReference Name="Infragistics.WebUI.UltraWebGrid.js.ig_WebGrid_dom.js"
                Assembly="Infragistics35.WebUI.UltraWebGrid.v8.3, Version=8.3.20083.1, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb" />
            <asp:ScriptReference Name="Infragistics.WebUI.UltraWebGrid.js.ig_WebGrid_cb.js" Assembly="Infragistics35.WebUI.UltraWebGrid.v8.3, Version=8.3.20083.1, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb" />
            <asp:ScriptReference Name="Infragistics.WebUI.UltraWebGrid.js.ig_WebGrid_an.js" Assembly="Infragistics35.WebUI.UltraWebGrid.v8.3, Version=8.3.20083.1, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb" />
            <asp:ScriptReference Name="Infragistics.WebUI.UltraWebGrid.js.ig_WebGrid_kb.js" Assembly="Infragistics35.WebUI.UltraWebGrid.v8.3, Version=8.3.20083.1, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb" />
            <asp:ScriptReference Name="Infragistics.WebUI.UltraWebGrid.js.ig_WebGrid_xml.js"
                Assembly="Infragistics35.WebUI.UltraWebGrid.v8.3, Version=8.3.20083.1, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb" />
            <asp:ScriptReference Name="Infragistics.WebUI.UltraWebGrid.js.ig_WebGrid.js" Assembly="Infragistics35.WebUI.UltraWebGrid.v8.3, Version=8.3.20083.1, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb" />
        </Scripts>
    </CompositeScript>
</asp:ScriptManager>

The best part about this type of performance tuning is that you can figure out the exact performance gains you will achieve before you begin making any changes.  Even better, by reducing the number of requests for each user, you're reducing the load on your webserver as well.  If you run Fiddler again at this point, you will see that in the place of these 9 separate javascript files, is a single script file with whitespace removed.  In our example above with a 300ms latency, you've just shaved almost 2 seconds off of your page load time.

As one final note, if you're using NetAdvantage, be sure to get the latest hotfix once SP1 has been released (RTM) in order to take advantage of Script Combining.


Posted 07-28-2008 7:40 PM by [Infragistics] Tony Lombardo

Comments

http:// wrote re: Script Combining - What's the big deal?
on 08-12-2008 3:14 PM

I was attempting to implement this script combining feature in one of my pages but I ended up getting an error, "The resource URL cannot be longer than 1024 characters. If using a CompositeScriptReference, reduce the number of ScriptReferences it contains, or combine them into a single static file and set the Path property to the location of it. ".  Apparntly I have too many scripts which exceeds the limit.  Are you aware of any way to have the ScriptManager break up the script references once the limit has been reached?

http:// wrote re: Script Combining - What's the big deal?
on 08-12-2008 3:21 PM

@Wallace - that error looks a lot like the one reported here - www.codeplex.com/.../View.aspx  Bertrand notes that it is fixed in the release, but that may just be the bug in the profiler, and not the 1024 limit for the URL. I don't know of any way to automatically work around it - you may be stuck with manually combining files.

TrackBack wrote Hidden Gems - Not the same old 3.5 SP1 post
on 08-14-2008 12:58 AM

Hidden Gems - Not the same old 3.5 SP1 post

http:// wrote re: Script Combining - What's the big deal?
on 09-03-2008 9:04 PM

Tony, what version hotfix will the script combining work for?  The version numbers in your example above look like 2008 volume 3 which isn't available yet.  So will the August hotfix for 2008 volume 2 work with the script combining?

http:// wrote re: Script Combining - What's the big deal?
on 09-04-2008 7:01 PM

Fix should be in the August hotfix for 2008.2 CLR3.5

http:// wrote re: Script Combining - What's the big deal?
on 09-05-2008 3:35 PM

Ok, one more question. Is there any chance that the project upgrade utility will also fix the versions within the composite script blocks throughout the application when upgrading versions?

http:// wrote re: Script Combining - What's the big deal?
on 09-05-2008 3:50 PM

@Mike - great question.. It's not specifically set up to do that, but it works mainly on pattern matching, so it might work, unless it's being strictly confined to <%@ Register %> tags. I'll submit this as a feature request either way.

http:// wrote re: Script Combining - What's the big deal?
on 11-25-2008 3:39 PM

Tony, I've now upgraded to both that v2 hotfix and also to 2008 v3, and I think that the incorporation of the compositescript stuff has introduced an issue.  Now whenever I drop a webdatechooser on a form, and don't add it's necessary script references to a composite script, Then it will not function correctly.  Shouldn't it just generate a script resource automatically if the scripts aren't in the composite script block?  I had even gone so far to remove the composite script sections from all my pages altogether, and I still get the same issue.  It will say Can't Init script for webdatechooser when the page is rendered.  I was just curious, have you come across this problem?

http:// wrote re: Script Combining - What's the big deal?
on 11-25-2008 3:55 PM

@Mike - That error usually indicates that an error is being thrown from inside of the WebDateChooser's InitializeDateChoose client-side event.  Check the DateChooser's ClientSideEvents property, and see if you have set a function name set for the InitializeDateChooser property.  If you do, try removing that and re-test your application.  If that doesn't work, I'd recommend contacting the Developer Support group (www.infragistics.com/gethelp)

http:// wrote re: Script Combining - What's the big deal?
on 11-25-2008 5:55 PM

Thanks Tony. I downloaded the latest hotfix for 2008.2 that came out last week, and that seemed to fix the problem.  But the hotfix hasn't come out for 2008.3 yet, so it is still an issue in that version.  I wasn't using the InitializeDateChooser client-side event, so I think it may have had something to do with the composite script functionality in the earlier hotfix.