Nico Vuyge's blog

Taking the blue AND the red pill

URL Deployment performance issues

Nico Vuyge

Some performance issues with URL deployment

It is very tempting to use URL deployment for a (100% managed) WinForms application. Basically you put all your application assemblies on a web server and you're done. Or maybe not. You're done in the sense that your deployment scenario works. Your not done in the sense that you may not be happy with the startup performance of your application. I've recently been asked by a client to improve the startup speed of his application (see also When is an assembly loaded), in particular in the URL deployment scenario. In this case, the startup performance was severely impacted by the cost of probing, and by the lack of assembly caching. To illustrate the issues, I've build a sample application that you can start here (don't click the link yet): UrlDeployedApp. The sample application just gives you the opportunity to show two simple forms. These forms are localizable, and are intended to be localized in your language of choice using satellite assemblies. It turns out that there's a difference in load behaviour depending on whether your assemblies have a strong name or not, so that's why we have two forms (one in a signed assembly, the other one in an unsigned assembly). Start up your favorite HTTP sniffer (I use EffeTech's HTTPDetect) and take a look at the HTTP requests done while starting up the application from the URL. Yes, you can click on UrlDeployedApp now. Or just read on if you don't want to trust this application.

First startup

This simple application consisting of 3 assemblies does more than 40 round trips to the web server to just to load its assemblies. The biggest cost is the .NET satellite assembly probing cost:

 http://www.i-constructions.com/samples/urldeployment/en-US/UnsignedForm.resources.DLL FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/en-US/UnsignedForm.resources/UnsignedForm.resources.DLL FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/bin/en-US/UnsignedForm.resources.DLL FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/bin/en-US/UnsignedForm.resources/UnsignedForm.resources.DLL FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/en-US/UnsignedForm.resources.EXE FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/en-US/UnsignedForm.resources/UnsignedForm.resources.EXE FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/bin/en-US/UnsignedForm.resources.EXE FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/bin/en-US/UnsignedForm.resources/UnsignedForm.resources.EXE FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/en/UnsignedForm.resources.DLL FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/en/UnsignedForm.resources/UnsignedForm.resources.DLL FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/bin/en/UnsignedForm.resources.DLL FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/bin/en/UnsignedForm.resources/UnsignedForm.resources.DLL FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/en/UnsignedForm.resources.EXE FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/en/UnsignedForm.resources/UnsignedForm.resources.EXE FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/bin/en/UnsignedForm.resources.EXE FIN, 404
 http://www.i-constructions.com/samples/urldeployment/bin/en/UnsignedForm.resources/UnsignedForm.resources.EXE FIN, 404

If your user's locale doesn't match one of the supported locales, probing for satellite assemblies in your user's language will cost you 16 round trips to the web server! The standard probing of .NET will cost you 8 round trips, but IEExec (the application that acts as a host for your application when launching from Internet Explorer) adds 'bin' as extra probing directory, which doubles the probing cost. Also note that you should always deploy satellite assemblies for the major (e.g. 'en') locale, even if you have a satellite assembly for your minor (e.g. 'en-US') locale that contains all translations. If your user's locale does match a supported locale, less probing will occur because the files will be found and downloaded:

 http://www.i-constructions.com/samples/urldeployment/nl-BE/UnsignedForm.resources.DLL 3072 FIN, 200 
 http://www.i-constructions.com/samples/urldeployment/nl/UnsignedForm.resources.DLL 3072 FIN, 200 
 http://www.i-constructions.com/samples/urldeployment/SignedForm.DLL 28672 FIN, 200 
 

Visual Studio will always create the major locale assemblies, but this may not be the case if you use a third party translation tool.

Second startup

Will I always have to download these assemblies every time I startup my application from this URL? No, assembly caching takes care of this. Here we see a difference between strong named assemblies and unsigned assemblies. Take a look at the log for the second startup:

 http://www.i-constructions.com/samples/urldeployment/nl-BE/UnsignedForm.resources.DLL FIN, 304 
 http://www.i-constructions.com/samples/urldeployment/nl/UnsignedForm.resources.DLL FIN, 304 
 

We see round trips for the unsigned assemblies, but not for the signed assemblies. The round trip for an unsigned assembly returns a 304 ('Not Modified') response. We see the HTTP 1.1 caching protocol in action here. This caching mechanism will work as long as your client (Internet Explorer) and your web server is correctly configured to enable caching. Again, we see that even though the 'nl-BE' version is found, .NET still requests the 'nl' version. We see no round trips for the signed assemblies. This is because signed assemblies get cached in the .NET assembly download cache. The .NET runtime will not even bother to request the assembly from the web server since, due to the strong name, it is absolutely sure that the assembly in the .NET assembly download cache is exactly what you want, and hasn't been tampered with. For signed assemblies, the second load will be about as fast using URL deployment as if the application was started from the local hard disk. Note that you don't clear the download cache by using the 'Clear cache' button in the Internet Explorer configuration dialog box, but by calling gacutil:

 gacutil /cdl

Also note that gacutil.exe is not included in the .NET runtime, but is installed as part of the Visual Studio installation.

Switching UICulture

We see something strange when the UICulture is changed. Our sample application gives the user the opportunity to select a specific locale before the forms are shown. This is done just by calling a line like

System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");
    

In this case we see .NET probing the web server for system assemblies! See the following log:

 http://www.i-constructions.com/samples/urldeployment/nl-BE/mscorlib.resources.DLL FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/nl-BE/mscorlib.resources/mscorlib.resources.DLL FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/bin/nl-BE/mscorlib.resources.DLL FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/bin/nl-BE/mscorlib.resources/mscorlib.resources.DLL FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/nl-BE/mscorlib.resources.EXE FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/nl-BE/mscorlib.resources/mscorlib.resources.EXE FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/bin/nl-BE/mscorlib.resources.EXE FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/bin/nl-BE/mscorlib.resources/mscorlib.resources.EXE FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/nl/mscorlib.resources.DLL FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/nl/mscorlib.resources/mscorlib.resources.DLL FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/bin/nl/mscorlib.resources.DLL FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/bin/nl/mscorlib.resources/mscorlib.resources.DLL FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/nl/mscorlib.resources.EXE FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/nl/mscorlib.resources/mscorlib.resources.EXE FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/bin/nl/mscorlib.resources.EXE FIN, 404 
 http://www.i-constructions.com/samples/urldeployment/bin/nl/mscorlib.resources/mscorlib.resources.EXE FIN, 404 

Conclusion and recommendations

From this experiment, we can conclude with the following recommendations:

  • Always provide satellite assemblies for all locales you care about performance. At least make dummy satellite assemblies to prevent excessive probing. Since you most likely wont create satellite assemblies for every possible locale, switch the CurrentUICulture to a locale for which you do have satellite assemblies, just in case somebody tries to use your application with an unavailable locale.
  • Sign all assemblies. This will increase the startup performance because of better caching, and the second startup will come purely from the local disk.
  • Try not to change CurrentUICulture, as it will cause unnecessary probing round trips for system assemblies.
  • Try to limit the number of assemblies, since each assembly will have its probing overhead.

Coming up next: the bloopers.