Friday, December 19, 2008

Kind Of A Black Box

For the longest time ASP.NET Just In Time compiling has always been a pretty gray matter for me. Word on the street has it, that some things you'll do against a web site cause the site to be re-JIT'd. Other actions will simply cause the application to unload. But which actions precipitate which results?

Just In Time Compiling

As you're probably aware, the code that you put in an ASP.NET web site isn't the actual code that runs. All those assemblies, web pages, user controls etc... all need to be Just In Time Compiled before they can actually be used. Visual Studio creates MSIL (Microsoft Intermediate Language) a bytecode that is highly portable. Before you can run it a .NET runtime, it needs to be compiled again into native code. Native code is specifically targeted to the machine it's going to be running on and is a lot more efficient. You can do this yourself (assuming you don't want your code to Just In Time compiled) by using either the ASP.NET Precompilation tool or Ngen.exe (Native Image Generator) another tool that comes with the .NET SDK.

All that's required for MSIL to be JIT'd is that the destination machine have a compiler that's capable of optimizing your MSIL for the given environment. A common example of this is when a developer builds an application on a 32-bit machine and then goes and deploys that MSIL on to some 64-bit machine. At runtime the MSIL will get natively compiled into 64-bit assemblies by a compiler that's sensitive to the needs of the destination 64-bit machine.

Optimize to the targeted CPU and the operating system model where the application runs. For example JIT can choose SSE2 CPU instructions when it detects that the CPU supports them. With a static compiler one must write two versions of the code, possibly using inline assemblies.

The system is able to collect statistics about how the program is actually running in the environment it is in, and it can rearrange and recompile for optimum performance. However, some static compilers can also take profile information as input.

The system can do global code optimizations (e.g. inlining of library functions) without losing the advantages of dynamic linking and without the overheads inherent to static compilers and linkers. Specifically, when doing global inline substitutions, a static compiler must insert run-time checks and ensure that a virtual call would occur if the actual class of the object overrides the inlined method.

Although this is possible with statically compiled garbage collected languages, a bytecode system can more easily rearrange memory for better cache utilization.

That's pretty much the just of it. Just In Time compiling adds a lot of flexibility to .NET development. Even though you've built a very generic application (can run anywhere with a CLR), what really get's run is a highly optimized version of that code that is targeted to the destination machine's architecture, operating system, etc...

How Does This Happen (in ASP.NET)

When you first visit a web application it needs to at least JIT the important stuff. JIT'ing starts with what's called top-level items. The following items are compiled first (pulled from here).

Item

Description

App_GlobalResources

The application's global resources are compiled and a resource assembly is built. Any assemblies in the application's Bin folder are linked to the resource assembly.

App_WebResources

Proxy types for Web services are created and compiled. The resulting Web references assembly is linked to the resource assembly if it exists.

Profile properties defined in the Web.config file

If profile properties are defined in the application's Web.config file, an assembly is generated that contains a profile object.

App_Code

Source code files are built and one or more assemblies are created. All code assemblies and the profile assembly are linked to the resources and Web references assemblies if any.

Global.asax

The application object is compiled and linked to all of the previously generated assemblies.

After the application's top level items have been compiled, ASP.NET compiles folders, pages, and other items as needed. The following table describes the order in which ASP.NET folders and items are compiled.

Item

Description

App_LocalResources

If the folder containing the requested item contains an App_LocalResources folder, the contents of the local resources folder are compiled and linked to the global resources assembly.

Adding, modifying, or deleting Web service references in the App_WebReferences directory.

Adding, modifying, or deleting the application's Web.config file.

Whenever one of the above conditions is met the old application domain is "drain stopped", which means that new requests are allowed to finish executing and once they're finished the Application Domain hosting those assemblies unloads. As soon as the recompile is finished a new Application Domain starts up with the new code and starts to handle all new requests.

Exodus

Believe it or not, having this kind of knowledge somewhere on hand will actually help you when you need it the most...when you're troubleshooting. This kind of stuff doesn't need to be kept at the tip of your tongue, but it should be at least semi-salvageable from the recesses of memory. The worst kind of trouble is the kind that doesn't make any sense. Having sophisticated tools that do a lot of leg work for you can also put bullet holes in your feet if you haven't read the instruction manual.

Hope that provided some value. I swear this stuff comes up more than you'd think.

About Me

Tyler Holmes is a Solutions Architect working in Portland, Oregon. He lives mostly in the MS tech stack and is currently treading the waters of Communication/Collaboration and Business Intelligence with off the shelf/open source technologies.