.Net framework Assembly Native Image is one of the biggest investments of CLR in .Net framework 2.0, if not the biggest. For client side .Net framework application performance, NGEN is the answer.
In .Net framework 2.0, Assembly Native Image is typically generated during assembly installation time. .Net framework 2.0 redist includes a tool NGEN.exe. When install a .Net framework assembly, you can create a custom action, and ask NGEN.exe to generate a native image for your assembly. NGEN.exe will send a request to CLR optimization service. CLR optimization service will compile the assembly to native image at appropriate time.
At runtime, when an assembly is requested by an application, CLR searches for its native image. When CLR finds one, CLR will evaluate the native image to make sure it can be used in the application. If the evaluation succeeds, CLR will use the assembly’s native image without recompiling the assembly. This process is typically called native image loading.
CLR’s code generation is highly susceptible to execution environment. For example, depending on the machine policy, the code generated for the same application may be different at different time. The native image is usually generated during application installation time. It is generated against a snapshot of the machine environment. The environment may not be the same when the application is executing. Thus, it may not be possible to always use a native image if available.
Not every change in the execution environment will affect native image loading. At minimum, CLR will check the following things:
1. Key CLR binaries
The native image generated .Net framework 2.0 has tight connection with CLR vm and codegen logic. Thus when key CLR binaries are changed, the existing native images can’t be used.
The list of key CLR binaries includes mscorjit.dll, mscorpe.dll, and mscorwks.dll. When any of them changes, all the existing native image have to be re-compiled.
Of course not every change from those binaries should invalidate existing native images. This may improve in the future.
2. Changes in assembly, and its dependencies
It is obvious that when the assembly changes, its native image will have to be re-compiled.
When the native image is generated, due to optimization introduced by the native image compiler, the native image may have to include information about other assemblies. For example, cross assembly method inlining, readonly data inlining, etc.
When that happens(and usually does happen for most native images), if any of the native image’s dependencies has changed, the native image can no longer be used until recompiled.
An important optimization by NGEN is called native image hard-binding. That is, a native image that depends on other native images. For this reason, a change in a simple assembly may cause a cascading effect in invalidating native images.
Just like the CLR binaries case, not every change in the dependencies should invalidate the native image. This is very hard to detect though. Maybe this will improve in the future.
3. Security Policies
If an application has a special security policy in its appdomain, the policy may not be the same as the one when the native image is generated. Since different security policy typically will cause different code to be generated, CLR will not use the native image when the appdomain has a custom security policy.
There is one exception to this. In .Net framework 2.0, CLR introduces a break change, that all the assemblies in GAC will be granted fulltrust, regardless of the security policy. Thus custom appdomain security policy won’t affect native image loading for assemblies in GAC.
4. Domain Neutrality
Code generated for domain neutral assemblies have to be different from domain bound assemblies. Since the assembly will be shared among multiple appdomains, access to domain specific objects has to use another layer of indirection.
For this reason, in .Net framework v1.x, domain neutral assemblies can’t use native image, as the native images generated does not understand domain neutral. In .Net framework 2.0, CLR changes the codegen in native images to understand domain neutral. That is, in .Net framework 2.0, it is possible to use native image for domain neutral assemblies.
But there is one restriction of the native images in .Net framework 2.0: they can be loaded either as domain neutral, or as domain bound, but not simultaneously. What this means is, if there are multiple appdomains in the application, and the application does not enable domain neutral sharing, then only one of the appdomains will use the native image, and the rest will JIT the assembly.
To workaround the restriction, the application must enable domain neutral sharing.
There are certainly more cases when a native image can not be used in an application. To diagnose why a native image cannot be used in an application, CLR has a native image loading log, integrated with the normal fusion binding log. You enable the log the same way you enable fusion binding log, and view them from the same familiar fuslogvw.exe. You just need to select the “Native Images” in the log categories, as seen below.
The first two categories of changes will permanently invalidate the native images. It will be a huge inconvenience if you have to manually re-generate all the invalidated native images. Fortunately CLR takes care of all of it for you. When there is a change to your assembly, after you install the update, you just need to simply call “ngen.exe update“, and CLR will automatically regenerate all the native images affected by the change to your assembly. Nice and easy.
NGen Revs Up Your Performance with Powerful New Features
What is mscorsvw.exe and why is it eating up my CPU? What is this new CLR Optimization Service?
Domain Neutral Assemblies
Sharing NGEN image in Multi-Domain Host