Sunday, November 1, 2009

CompilationMode=Never: What does it actually do?

I wanted to take CompilationMode=Never for a test-drive on an existing application that’s plagued by too many control/page assemblies – the goal was to see if this would help improve scalability (by reducing unrecoverable memory consumed by assemblies) and to observe its impact on a high and fluctuating ‘% time in JIT’. It turns out that it’s not as easy as throwing it into web.config.

Yes, you can apply this en masse to an existing website but doing so on pre-existing sites could generate:

  1. The attribute 'codefile' is not allowed in this page.
  2. The attribute 'autoeventwireup' is not allowed in this page.  

Why would CompilationMode interfere with AutoEventWireup? In automatically wiring up events, it looks like the framework looks for suitable signatures at runtime using reflection [1] – crazy, I know; and not having assemblies would presumably make this impossible to do. Scott Allen has a couple of good writes-ups on this and you can have a look for yourself in the auto-generated assemblies.

Can you think of any reasons why the events wouldn’t be wired up at compile time?

I tried to debug the framework to see this in action but threw in the towel after a few hours of fighting the symbol server – hours on this is pretty ridiculous, I know, and in complete defiance of Oscar’s #1 rule: stops are in, emotions are out! It was just a bit shocking to see how brittle the entire setup around source server is and how many continue to struggle with it [2] – anyone out there actually using it successfully for mscorlib and System.*.dlls? Have you you applied any service packs? Would love to hear from you. With VS2008 SP1 (9.0.307279.1), Vista SP2, the latest source code component (Dotnetfx_4016_VistaSP2), and following every instruction to a tee, I can step into most assemblies (e.g. System.Web, 2.0.50727.4016) but can’t step into mscorlib (2.0.50727.4200) which contains the reflection calls in question – for reference, the symbols for mscorlib are downloaded from /download/symbols/mscorlib.pdb/4D0B2695F5144B4D8F24004284FE26191/mscorlib.pd_.

So once you’ve manually resolved #2, code-behind files must also be pushed out to a fully-qualified class that can be reference by the Inherits attribute alone. 

After that, it works as expected and you see the runtime behaviour of CompilationMode=Never:

   1: // IWebObjectFactory.CreateInstance 
   2: public virtual object CreateInstance() {
   3:  
   4:     // Create the object that the aspx/ascx 'inherits' from
   5:     TemplateControl templateControl = (TemplateControl) HttpRuntime.FastCreatePublicInstance(_baseType);
   6:  
   7:     // Set the virtual path and TemplateSourceDirectory in the control 
   8:     templateControl.TemplateControlVirtualPath = VirtualPath;
   9:     templateControl.TemplateControlVirtualDirectory = VirtualPath.Parent; 
  10:  
  11:     // Give the TemplateControl a pointer to us, so it can call us back during FrameworkInitialize
  12:     templateControl.SetNoCompileBuildResult(this); 
  13:  
  14:     return templateControl;
  15: }

What’s a little surprising, if I’m reading this right, is it seems to generate the type on demand every time without any caching of the instance for re-use across different requests:

   1: /*
   2:  * Faster implementation of CreatePublicInstance.  It generates bits of IL
   3:  * on the fly to achieve the improve performance.  this should only be used 
   4:  * in cases where the number of different types to be created is well bounded.
   5:  * Otherwise, we would create too much IL, which can bloat the process. 
   6:  */ 
   7: internal static Object FastCreatePublicInstance(Type type) {
   8:  
   9:     // Only use the factory logic if the assembly is in the GAC, to avoid getting
  10:     // assembly conflicts (VSWhidbey 405086) 
  11:     if (!type.Assembly.GlobalAssemblyCache) { 
  12:         return CreatePublicInstance(type);
  13:     } 
  14:  
  15:     // Create the factory generator on demand
  16:     if (!s_initializedFactory) {
  17:  
  18:         // Devdiv 90810 - Synchronize to avoid race condition
  19:         lock (s_factoryLock) { 
  20:             if (!s_initializedFactory) { 
  21:                 s_factoryGenerator = new FactoryGenerator();
  22:  
  23:                 // Create the factory cache
  24:                 s_factoryCache = Hashtable.Synchronized(new Hashtable());
  25:  
  26:                 s_initializedFactory = true; 
  27:             }
  28:         } 
  29:     } 
  30:  
  31:     // First, check if it's cached 
  32:     IWebObjectFactory factory = (IWebObjectFactory)s_factoryCache[type];
  33:  
  34:     if (factory == null) {
  35:  
  36:         Debug.Trace("FastCreatePublicInstance", "Creating generator for type " + type.FullName);
  37:  
  38:         // Create the object factory 
  39:         factory = s_factoryGenerator.CreateFactory(type);
  40:  
  41:         // Cache the factory
  42:         s_factoryCache[type] = factory;
  43:     }
  44:  
  45:     return factory.CreateInstance();
  46: } 

Anyway, if SharePoint uses it successfully, I’m sure it can work for your CMS/cutting-edge-ASP.NET-stuff too.

Performance benchmarks though will have to wait another day.

References:

[1] – AutoEventWireup & Reflection
http://stackoverflow.com/questions/275965/asp-net-mvc-autoeventwireup-required/276385#276385
http://odetocode.com/articles/406.aspx

[2] – Troubleshooting Framework Debugging
http://stackoverflow.com/questions/1095202/why-cant-i-step-into-this-line
http://social.msdn.microsoft.com/Forums/en-US/refsourceserver/thread/1b74f60c-e961-425c-a38e-362406dd4cfe
http://social.msdn.microsoft.com/Forums/en-US/vsdebug/thread/a8441b7b-017b-4094-8788-6005aa8e69a3
http://social.msdn.microsoft.com/Forums/en/refsourceserver/thread/b22e044c-be8f-4650-98d6-b426193b7b2c
http://social.msdn.microsoft.com/Forums/en-US/refsourceserver/thread/e20ad5f3-3071-4ff6-9f2b-6f4ec22661b8
http://social.msdn.microsoft.com/Forums/en-US/refsourceserver/thread/ceb17913-a983-47a8-b15e-655a65c5f001/

[3] – Microsoft Reference Source Server
http://referencesource.microsoft.com/netframework.aspx
http://referencesource.microsoft.com/serversetup.aspx

[4] – KB944899 (This hotfix addresses a performance problem when stepping through Source Code downloaded via a Microsoft Reference Source Server)
https://connect.microsoft.com/VisualStudio/Downloads/DownloadDetails.aspx?DownloadID=10443&wa=wsignin1.0

0 comments: