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.