Thursday, September 01, 2005 1:53 AM bart

To NGen or not to NGen

Introduction

Today I'm going to present you an overview of a tool called "ngen" in both .NET v1.x and .NET v2.0. Simply put, ngen (Native Image Generator) is a tool that comes with .NET v1.x and higher and is used to create a "native image" for a given assembly. As you probably know, the CLR executes IL-code which cannot be understood by a processor. Instead, it needs to be translated into native code which can run on the computer's physical processor(s). The CLR execution engine contains a so-called JIT (just in time) compiler to accomplish this IL-to-native-code translation at runtime. However, this compiled (native) code isn't stored, it's just kept in memory during the containing process' lifetime. When the application is restarted, the JIT compiler comes into play again to compile the same code to native code again. The ngen tool lets you create a native image and store it physically on disk in a "native image cache" (aka NGen cache). Let's give a simple usage sample:

  1. Open up the Visual Studio .NET 2003 Command Prompt.
  2. Open Notepad and create a simple "Hello NGen" (I guess I'll never win a prize for originality :-)) like this:

    class Test
    {
       public static void Main()
       {
          System.Console.WriteLine("Hello NGen");
       }
    }

  3. Compile the application using csc.exe and run it. Nothing exciting yet:

    C:\temp>csc test.cs
    Microsoft (R) Visual C# .NET Compiler version 7.10.6310.4
    for Microsoft (R) .NET Framework version 1.1.4322
    Copyright (C) Microsoft Corporation 2001-2002. All rights reserved.

    C:\temp>test
    Hello NGen

  4. Open another Visual Studio .NET 2003 Command Prompt window and go to %windir%\assembly and show the contents of the directory:

    C:\WINDOWS\assembly>dir
     Volume in drive C has no label.
     Volume Serial Number is DC7C-7836

     Directory of C:\WINDOWS\assembly

    08/31/2005  12:52 AM    <DIR>          GAC
    08/31/2005  12:57 AM    <DIR>          GAC_32
    08/31/2005  12:46 PM    <DIR>          GAC_MSIL
    08/31/2005  12:11 AM    <DIR>          NativeImages1_v1.1.4322
    08/31/2005  02:08 PM    <DIR>          NativeImages_v2.0.50215_32
    08/31/2005  01:14 PM    <DIR>          temp
    08/31/2005  01:15 PM    <DIR>          tmp
                   0 File(s)              0 bytes
                   7 Dir(s)  119,380,508,672 bytes free

  5. The NativeImages1_v1.1.4322 is where native images live. Cd into this directory and check that no "test" subdirectory exists over there.
  6. Switch back to the first command prompt and make a native image of the test.exe file using ngen and run it again, as follows:

    C:\temp>ngen test.exe
    Microsoft (R) CLR Native Image Generator - Version 1.1.4322.573
    Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.
    test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

    C:\temp>test
    Hello NGen
  7. Next, open the test.exe file using ildasm.exe. You should see the manifest as well as the IL code definition of the class Test.
  8. Switch to the second command prompt, the NGen cache. Now, there should be a directory called test:

    C:\WINDOWS\assembly\NativeImages1_v1.1.4322>dir test
     Volume in drive C is W2K3ENT
     Volume Serial Number is 1C71-360F

     Directory of C:\WINDOWS\assembly\NativeImages1_v1.1.4322\test

    08/31/2005  04:00 PM    <DIR>          .
    08/31/2005  04:00 PM    <DIR>          ..
    08/31/2005  04:00 PM    <DIR>          0.0.0.0___2b518c8d
                   0 File(s)              0 bytes
                   3 Dir(s)     619,991,040 bytes free

  9. Cd into that directory and display the contents:

    C:\WINDOWS\assembly\NativeImages1_v1.1.4322\test\0.0.0.0___2b518c8d>dir
     Volume in drive C is W2K3ENT
     Volume Serial Number is 1C71-360F

     Directory of C:\WINDOWS\assembly\NativeImages1_v1.1.4322\test\0.0.0.0___2b518c8d

    08/31/2005  04:00 PM    <DIR>          .
    08/31/2005  04:00 PM    <DIR>          ..
    08/31/2005  04:00 PM             5,120 test.exe
    08/31/2005  04:00 PM                90 __AssemblyInfo__.ini
                   2 File(s)          5,210 bytes
                   2 Dir(s)     619,991,040 bytes free

  10. You'll notice the test.exe file being larger than the original one (the original one should be 3,072 bytes in size). Again, run ildasm.exe on the test.exe file. Now you won't find any IL, there's just a manifest left.

<GeeksWantMore AlternativeMethodNumber="1">

Geeks can run ildasm /Adv (which is a hidden option in v1.x) to take a look at the COR Header (View, COR header). The original test.exe file should have the following CLR header:

 CLR Header:
 72       Header Size
 2        Major Runtime Version
 0        Minor Runtime Version
 1        Flags
 6000001  Entrypoint Token
 207c     [200     ] address [size] of Metadata Directory:       
 0        [0       ] address [size] of Resources Directory:      
 0        [0       ] address [size] of Strong Name Signature:    
 0        [0       ] address [size] of CodeManager Table:        
 0        [0       ] address [size] of VTableFixups Directory:   
 0        [0       ] address [size] of Export Address Table:     
 0        [0       ] address [size] of Precompile Header:        

whileas the ngen-ed one looks like this:

 CLR Header:
 72       Header Size
 2        Major Runtime Version
 0        Minor Runtime Version
 6        Flags
 0        Entrypoint Token
 2200     [398     ] address [size] of Metadata Directory:       
 0        [0       ] address [size] of Resources Directory:      
 0        [0       ] address [size] of Strong Name Signature:    
 0        [0       ] address [size] of CodeManager Table:        
 0        [0       ] address [size] of VTableFixups Directory:   
 0        [0       ] address [size] of Export Address Table:     
 2050     [40      ] address [size] of Precompile Header:        

Basically, the runtime will check whether a managed code file has an associated native image. If that's the case, the native image is used instead of the file containing the (non-JITted) IL code.

</GeeksWantMore>

<GeeksWantMore AlternativeMethodNumber="2">

If you want to see more evidence of the native image being used instead of the original (non-pre-jitted) one, you can use "Fusion", otherwise known as "Assembly Binding Log Viewer". Open the Registry Editor (regedit.exe) and locate HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Fusion. In there, add a REG_DWORD value with name "ForceLog" (without quotes) and data 0x00000001 (1). Go to the first command prompt window and execute the test.exe file. Then, start fuslogvw.exe:

C:\temp>test
Hello NGen

C:\temp>fuslogvw

You should see something like this:

Select the test.exe log entry and click "View Log". At the bottom of the Assembly Binder Log Entry you should find the following:

LOG: Not processing DEVPATH because this is a pre-jit assembly bind.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Post-policy reference: test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
LOG: Found assembly by looking in the cache.

After playing this trick, disable the ForceLog again in the registry by removing the value you added previously.

</GeeksWantMore>

 

Shortcomings in v1.x

In the introduction we took a look at the ngen.exe tool of the .NET Framework v1.x. Although the principle is great (and it's being used by dotnetfx.exe during installation of the BCL assemblies), it has some serious drawbacks. An overview:

  • Native images cannot be shared across application domains, which renders ngen.exe useless for ASP.NET and other scenarios where more than one application domain is involved.
  • Managed assemblies have dependencies (at least mscorlib.dll is required). When an assembly is ngen-ed, stuctural information about those dependencies is introduced in the native image. A change of such a dependency will invalidate the native image which was generated by ngen, falling back on JIT compilation instead. Imagine what would happen if System.dll changes (e.g. by applying a service pack).
  • Ngen is invoked on a single assembly, not on an entire application. It's up to the developer to make sure dependencies are ngen-ed as well.
  • The ngen.exe tool doesn't use the standard assembly probing rules to locate the specified assembly. Instead, it just looks in the current folder.
  • NGen can only run synchronously. You have to wait till the compilation to native code has completed.
  • There is no support for a thing called "hardbinding". Although only handy in somewhat rare situations, hardbinding can be useful.

    Hardbinding eliminates additional costs associated with maintaining "call stubs" which are introduced in code to call methods etc in another assembly. Because the target is not known at compile-time, the virtual address at runtime is unknown. Instead, a little stub function is created which is responsible to calculate that virtual address of the call target. Once the address is found, the call is "backpatched" in order to bypass the stub and call the target directly. However, because of this a code page which was read-only in the past, now needs to be changed to writable (to apply the backpatch) and thus it cannot be shared anymore. Reduction of sharable pages can hurt performance.

 

Whidbey's new NGen technology

Overview of ngen.exe

NGen 2.0 tries to solve a series of the aforementioned shortcomings. Start by opening a Visual Studio 2005 Command Prompt and launch the new ngen.exe tool. This is the output:

C:\>ngen
Microsoft (R) CLR Native Image Generator - Version 2.0.50215.44
Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.

Usage: ngen <action> [args] [/nologo] [/silent] [/verbose]
       ngen /? or /help

    /nologo    - Prevents displaying of logo
    /silent    - Prevents displaying of success messages
    /verbose   - Displays verbose output for debugging

Actions:
    ngen install <assembly name> [scenarios] [config] [/queue[:[1|2|3]]
        Generate native images for an assembly and its dependencies
        and install them in the Native Images Cache

NGen now works on applications, not on stand-alone assemblies.

        If /queue is specified compilation job is queued up.  If a priority
        is not specified, the default priority used is 3.

Can run asynchronously by using a queue. More info follows later.

    ngen uninstall <assembly name> [scenarios] [config]
        Delete the native images of an assembly and its dependencies from
        the Native Images Cache.
    ngen update [/queue]
        Update native images that have become invalid
        If /queue is specified compilation jobs are queued up.
    ngen display [assembly name]
        Display the ngen state
    ngen executeQueuedItems [1|2|3]
        Executes queued compilation jobs.
        If priority is not specified all queued compilation jobs are done.
        If priority is specified compilation jobs with greater or equal.
        priority than the specified are done.
    ngen queue [pause|continue|status]
        Allows the user to pause and continue the NGen Service Queue, and to
        query its status.

Scenarios:
    /Debug          - Generate images that can be used under a debugger
    /Profile        - Generate images that can be used under a profiler
    /NoDependencies - Generate the minimal number of native images
                      required by this scenario

Config:
    /ExeConfig:<path to exe> - Use the configuration of the specified
                 executable assembly
    /AppBase:<path to appbase directory> - Use the specified directory as
                 the appbase

I've annotated the output above with red text, pointing to some of the shortcomings addressed in the new version. First of all, dependencies are also ngen-ed automatically because the tool detects all dependencies automatically. An easy sample will show this:

  1. Open a Visual Studio 2005 Command Prompt.
  2. Create a new file testhelper.cs using Notepad:

    public class TestHelper
    {
       public string GetMessage()
       {
          return "Test";
       }
    }
  3. Create a new file test.cs using Notepad:

    class Test
    {
       public static void Main()
       {
          System.Console.WriteLine(new TestHelper().GetMessage());
       }
    }
  4. Compile both files and run:

    C:\temp>csc -t:library testhelper.cs
    Microsoft (R) Visual C# 2005 Compiler version 8.00.50215.44
    for Microsoft (R) Windows (R) 2005 Framework version 2.0.50215
    Copyright (C) Microsoft Corporation 2001-2005. All rights reserved.

    C:\temp>csc -r:testhelper.dll test.cs
    Microsoft (R) Visual C# 2005 Compiler version 8.00.50215.44
    for Microsoft (R) Windows (R) 2005 Framework version 2.0.50215
    Copyright (C) Microsoft Corporation 2001-2005. All rights reserved.

    C:\temp>test
    Test

  5. Open %windir%\assembly\NativeImages_v2.0.50215_32 in another Visual Studio 2005 Command Prompt window. You should find no folder called test or testhelper.
  6. Switch back to the first command prompt window and execute ngen as follows (new syntax!):

    C:\temp>ngen install test.exe
    Microsoft (R) CLR Native Image Generator - Version 2.0.50215.44
    Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.
    Installing assembly C:\temp\test.exe
    Compiling 2 assemblies:
        Compiling assembly C:\temp\test.exe ...
    test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
        Compiling assembly testhelper, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null ...
    testhelper, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

  7. Now native images for both test and testhelper should be present in the assembly\NativeImages_v2.0.50215_32 folder. The new filename of native images contains .ni. in the middle between the original filename and the extension. So, the following dir command shows us all of the native images for test* assemblies:

    C:\WINDOWS\assembly\NativeImages_v2.0.50215_32>dir test*.ni.* /s
     Volume in drive C has no label.
     Volume Serial Number is DC7C-7836

     Directory of C:\WINDOWS\assembly\NativeImages_v2.0.50215_32\test\8714e29e626e8e3e8323436ee93dd387

    08/31/2005  05:54 PM            12,800 test.ni.exe
                   1 File(s)         12,800 bytes

     Directory of C:\WINDOWS\assembly\NativeImages_v2.0.50215_32\testhelper\b6c46520e8eb8c37b273b092ff53c2fa

    08/31/2005  05:54 PM            12,288 testhelper.ni.dll
                   1 File(s)         12,288 bytes

         Total Files Listed:
                   2 File(s)         25,088 bytes
                   0 Dir(s)  119,786,254,336 bytes free

  8. Again you can use ildasm and/or fuslogvw to find out more:
    • ildasm.exe should only show the manifest in the native image files test.ni.exe and testhelper.ni.dll (note: the /Adv flag for ildasm.exe has been removed and is on by default; use View, Headers to view the COR Header)
    • fuslogvw.exe now has a Settings button to eliminate the need to go to the registry to change the logging level (select "Log all binds to disk") and also has a "Log Categories" section where you can choose "Native Images" from

Now, how does ngen.exe determine the dependencies of a given assembly? The answer is the IL header which contains all static "imports" in the assembly:

// Metadata version: v2.0.50215
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 2:0:0:0
}
.assembly extern testhelper
{
  .ver 0:0:0:0
}

Now, what happens if we want to uninstall the native images for our test.exe application. Let's run it to get a clue about this (warning: make sure to cd out of the NativeImages_v2.0.50215_32\test* directories if these are still open in some command prompt window):

C:\temp>ngen uninstall test.exe
Microsoft (R) CLR Native Image Generator - Version 2.0.50215.44
Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.
Uninstalling assembly C:\temp\test.exe

and

C:\WINDOWS\assembly\NativeImages_v2.0.50215_32>dir test*
 Volume in drive C has no label.
 Volume Serial Number is DC7C-7836

 Directory of C:\WINDOWS\assembly\NativeImages_v2.0.50215_32

File Not Found

As you can see, also the testhelper.dll native image has disappeared. There has to be some kind of dependency tracking, right? Let's do another test:

  1. In the folder where testhelper.dll resides, create a new file called test2.cs with the following code in it:

    class Test2
    {
       public static void Main()
       {
          System.Console.WriteLine(new TestHelper().GetMessage());
       }
    }

  2. Compile the test2.cs file:

    C:\temp>csc -r:testhelper.dll test2.cs
    Microsoft (R) Visual C# 2005 Compiler version 8.00.50215.44
    for Microsoft (R) Windows (R) 2005 Framework version 2.0.50215
    Copyright (C) Microsoft Corporation 2001-2005. All rights reserved.

  3. Now send both apps to the NGen cache:

    C:\temp>ngen install test.exe
    Microsoft (R) CLR Native Image Generator - Version 2.0.50215.44
    Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.
    Installing assembly C:\temp\test.exe
    Compiling 2 assemblies:
        Compiling assembly C:\temp\test.exe ...
    test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
        Compiling assembly testhelper, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null ...
    testhelper, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

    C:\temp>ngen install test2.exe
    Microsoft (R) CLR Native Image Generator - Version 2.0.50215.44
    Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.
    Installing assembly C:\temp\test2.exe
    Compiling 1 assembly:
        Compiling assembly C:\temp\test2.exe ...
    test2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

  4. In the other command prompt window, check the existence of the expected folders:

    C:\WINDOWS\assembly\NativeImages_v2.0.50215_32>dir test*
     Volume in drive C has no label.
     Volume Serial Number is DC7C-7836

     Directory of C:\WINDOWS\assembly\NativeImages_v2.0.50215_32

    08/31/2005  06:12 PM    <DIR>          test
    08/31/2005  06:12 PM    <DIR>          test2
    08/31/2005  06:12 PM    <DIR>          testhelper
                   0 File(s)              0 bytes
                   3 Dir(s)  119,785,574,400 bytes free

  5. Now uninstall test.exe using ngen:

    C:\temp>ngen uninstall test.exe
    Microsoft (R) CLR Native Image Generator - Version 2.0.50215.44
    Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.
    Uninstalling assembly C:\temp\test.exe

  6. Check the NGen cache again:

    C:\WINDOWS\assembly\NativeImages_v2.0.50215_32>dir test*
     Volume in drive C has no label.
     Volume Serial Number is DC7C-7836

     Directory of C:\WINDOWS\assembly\NativeImages_v2.0.50215_32

    08/31/2005  06:12 PM    <DIR>          test2
    08/31/2005  06:12 PM    <DIR>          testhelper
                   0 File(s)              0 bytes
                   2 Dir(s)  119,785,574,400 bytes free

  7. Cool, testhelper is still alive and kicking out there. Dependency tracking does its job.

So, now the question you'll be asking yourself probably: where does the CLR keep track of these dependencies? The answer is the registry, more specifically in the following key: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Fusion\NativeImagesIndex\v2.0.50215_32. In the Registry Editor, search for "test" starting from the specified key and you should find both the test2 and testhelper entries. For example, I have a key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Fusion\NativeImagesIndex\v2.0.50215_32\IL\2988d581\392c9598\f1over here for the testhelper assembly. The subkey InvertDependencies contains links to the assemblies depending on the current one. An overview of the local situation:

  • IL\2988d581\392c9598\f1
    • DisplayName: testhelper,0.0.0.0,,
    • InvertDependencies: 1793d581\fe08a65\d2, 5b607b80\66bc3064\a1
  • IL\6897b81\795befb3\f8
    • DisplayName: test2,0.0.0.0,,
    • InvertDependencies: 5b607b80\66bc3064\a1
  • NI\1793d581\fe08a65\d2
    • DisplayName: testhelper,0.0.0.0,,
    • MVID: 6B DA A9 0D 8E 31 A3 38 96 05 C7 87 17 C8 65 98
  • NI\5b607b80\66bc3064\a1
    • DisplayName: test2,0.0.0.0,,
    • MVID: 2C 25 C4 D5 6F 30 FB 3F B3 CA AC 5F 2D 46 5B 52

The NI keys contain information about the native images, i.e. testhelper and test2. The MVID points to the subdirectory in the NativeImages_v2.0.50215_32 folder where the native image resides. E.g. testhelper lives on my system in C:\WINDOWS\assembly\NativeImages_v2.0.50215_32\testhelper\6bdaa90d8e31a3389605c78717c86598. In the IL part you'll find the InvertDependencies list for each assembly in the NGen cache. As you can see, testhelper is needed for testhelper itself (the native image) and test2. This means that a change to testhelper will cause an ngen to occur to testhelper and test2, because these native images would be invalidated by changing testhelper.

Note: Do not confuse these mechanisms with the old COM registration mechanism. COM suffers from the infamous DLL Hell, whileas the .NET Framework does not suffer from this problem (cf. versioning). Furthermore, the use of the registry for "registration" and "dependency tracking" is a ngen-only thing in the world of .NET, assemblies can be xcopy deployed. Also notice that the assemblies act as a kind of stubs to the native images when such a native image exists. You can't delete the testhelper.dll file in order to run test.exe or test2.exe although it resides in the NGen cache. If you'd like to do this, you'll need to register the assembly in the GAC (thus requiring strong naming). Thus, ngen.exe is not a .NET look-alike for regsvr32.exe.

So far, we've seen the ngen install and ngen uninstall actions. It's worth to mention that a fully-qualified assembly name can be used to perform the install action. Notice that such a fully-qualified name in .NET v2.0 has now 5 components: name (test), version (0.0.0.0), culture (neutral), public key token (...) and processor architecture (MSIL). Two other useful features (flags at the command line) are:

  • Scenarios: used to generate native images that can be used by a debugger (/debug) or a profiler (/profile) or to generate a minimum number of native images by not ngen-ing dependencies (/nodependencies).
  • Config: /ExeConfig to point to a configuration file (.exe.config) that contains additional information used by ngen and /AppBase to override assembly probing settings by specifying an "appbase directory" to search for assemblies in.

Another useful action is the ngen display action. An example is shown below:

C:\temp>ngen display test.exe
Microsoft (R) CLR Native Image Generator - Version 2.0.50215.44
Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.

NGEN Roots:

C:\temp\test.exe

NGEN Roots that depend on "C:\temp\test.exe":

C:\temp\test.exe

Native Images:

test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

Using verbose output (add /verbose) you get a lot more information however:

Microsoft (R) CLR Native Image Generator - Version 2.0.50215.44
Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.

NGEN Roots:

C:\temp\test.exe
    ScenarioDefault  --no debug or profile scenario present
        C:\temp\test.exe
            DisplayName = test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
            Native image = {CC9FE28A-004D-38EB-82F9-3D97AB21A051}
            Hard Dependencies:
                mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
            Soft Dependencies:
                testhelper, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null --what we were looking for; "forward dependency"
        mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
            DisplayName = mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
            Native image = {DAAB7D0D-ABB0-394D-82EF-037B571B1032}
            Hard Dependencies:
            Soft Dependencies:
        testhelper, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
            DisplayName = testhelper, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
            Native image = {346E10F6-8E71-3605-91A2-C732FC2D5866} --the location of testhelper can be derived from the GUID: F6106E34718EA29166582DFC32C7 by a bytewise revert of each "GUID group"
            Hard Dependencies:
                mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
            Soft Dependencies:

--Note: In the end, the information shown above forms a tree of dependencies. Try doing the same on an application that uses e.g. System.Data.DataSet.

NGEN Roots that depend on "C:\temp\test.exe":

C:\temp\test.exe
    ScenarioDefault
        C:\temp\test.exe
            DisplayName = test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
            Native image = {CC9FE28A-004D-38EB-82F9-3D97AB21A051}
            Hard Dependencies:
                mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
            Soft Dependencies:
                testhelper, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
        mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
            DisplayName = mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
            Native image = {DAAB7D0D-ABB0-394D-82EF-037B571B1032}
            Hard Dependencies:
            Soft Dependencies:
        testhelper, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
            DisplayName = testhelper, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
            Native image = {346E10F6-8E71-3605-91A2-C732FC2D5866} --the location of testhelper can be derived from the GUID: F6106E34718EA29166582DFC32C7 by a bytewise revert of each "GUID group"
            Hard Dependencies:
                mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
            Soft Dependencies:

Native Images:

test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
 Source MVID: {C456F353-332D-401F-B9FC-93495C519EB1}
 Source HASH: f9149373069fb3e81111bb24db59018bc2d48d98
 NGen GUID sign: {CC9FE28A-004D-38EB-82F9-3D97AB21A051}
 OS:  WinNT
 Processor: x86(Pentium 4) (features: 00008001)
 Runtime: 2.0.50215.44
 mscorwks.dll: TimeStamp=42578752, CheckSum=0053D2C1
 Flags:  
 Scenarios:  <no debug info> <no debugger> <no profiler> <no instrumentation> --try to add other scenarios as explained earlier
 Granted set: <PermissionSet class="System.Security.PermissionSet"
version="1"
Unrestricted="true"/>

 File:  C:\WINDOWS\assembly\NativeImages_v2.0.50215_32\test\8ae29fcc4d00eb3882f93d97ab21a051\test.ni.exe --the location of the native image
 Dependencies:
  mscorlib, Version=2.0.0.0, PublicKeyToken=b77a5c561934e089:
   Guid:{7125636A-C067-4930-9DA0-DCD6DC6A10A9}
   Sign:66c33e7153140d9397fd66ecb5d9c4b19f00e8e3
   Hardbound Guid:{DAAB7D0D-ABB0-394D-82EF-037B571B1032}
  testhelper, Version=0.0.0.0, PublicKeyToken=null:
   Guid:{58EDC827-7258-43EF-BD02-B694FA4C806C}
   Sign:0cd07a972e90d8faf86ec768d0705a17211306d3

The NGen service

Time to move on to another piece of the renewed NGen technology, being the ".NET Runtime Optimization Service". What's in a name? It's a service and it's used to optimize the .NET Runtime. So, what about looking at the SCM MMC using services.msc? The first service in the list that you should see is the following:

  • Service Name: clr_optimization_v2.0.50215_32
  • Display Name: .NET Runtime Optimization Service v2.0.50215_X86
  • Description: Provides support for optimizing managed assemblies using NGEN technology.
  • Path to executable: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50215\mscorsvw.exe

People who are somewhat familiar with services will notice that the recovery settings are rather "exceptional": on every failure, the service is restarted and there's a pretty agressive recovery policy. By default, the service will have a Manual startup type, but the service itself changes this when needed (see further). Now, what's this service all about?

Imagine an application you want to ngen when it's installed but you don't want to wait for the entire ngen compilation to complete at setup time. In other words, some asynchronous triggering of the ngen compilation mechanism for some application. This is the scenario for which this service was made: to perform IL-to-native code compilations in the background when the system is idle or when ngen compilation is enforced through ngen.exe.

Let's go through a end-to-end scenario using the NGen service:

  1. Make sure all test.exe, test2.exe and testhelper.dll native images are deleted from the system by running ngen uninstall on each of these assemblies. When doing so, make sure no other command prompt is active in the NGen cache folders for these assemblies' native images.
  2. First, query the service status. In my case this is the output:

    C:\temp>sc queryex clr_optimization_v2.0.50215_32

    SERVICE_NAME: clr_optimization_v2.0.50215_32
            TYPE               : 10  WIN32_OWN_PROCESS
            STATE              : 1  STOPPED
                                    (NOT_STOPPABLE,NOT_PAUSABLE,IGNORES_SHUTDOWN)
            WIN32_EXIT_CODE    : 0  (0x0)
            SERVICE_EXIT_CODE  : 0  (0x0)
            CHECKPOINT         : 0x0
            WAIT_HINT          : 0x0
            PID                : 0
            FLAGS              :


    If the service is running, it has work to do. In that case, execute the following command to "purge" the queue of outstanding work (note this can take a while and will cause high CPU usage):

    C:\temp>ngen executeQueuedItems 3
    Microsoft (R) CLR Native Image Generator - Version 2.0.50215.44
    Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.
    (...)

    If there was work to do and the service is still running, reboot the machine (just for sake of the demo). Next time the service should be stopped and have a startup type of Manual.
  3. Now we'll ngen the test.exe file, not directly but deferred. In order to do this, we need to execute the ngen install action but with the /queue flag:

    C:\temp>ngen install /queue test.exe
    Microsoft (R) CLR Native Image Generator - Version 2.0.50215.44
    Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.
    Installing assembly C:\temp\test.exe

    Compare this with earlier executions of ngen install without the /queue flag. The assembly is not being compiled directly right now.
  4. Go to the C:\WINDOWS\assembly\NativeImages_v2.0.50215_32 folder in the another command prompt window. You should find no subfolder called "test" in there. Native image compilation has not been done yet...

    C:\WINDOWS\assembly\NativeImages_v2.0.50215_32>dir test
     Volume in drive C has no label.
     Volume Serial Number is DC7C-7836

     Directory of C:\WINDOWS\assembly\NativeImages_v2.0.50215_32

    File Not Found

  5. Now run the ngen display action for the test.exe assembly:

    C:\temp>ngen display test.exe
    Microsoft (R) CLR Native Image Generator - Version 2.0.50215.44
    Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.

    NGEN Roots:

    C:\temp\test.exe (StatusPending) (Pri 3)

    Native Images:

    This output indicates that only the root of test.exe is known to the system and the status of its compilation is pending with priority 3 (see further).
  6. Query the service status again. It's now alive (and kicking?):

    C:\temp>sc queryex clr_optimization_v2.0.50215_32

    SERVICE_NAME: clr_optimization_v2.0.50215_32
            TYPE               : 10  WIN32_OWN_PROCESS
            STATE              : 4  RUNNING
                                    (STOPPABLE,PAUSABLE,IGNORES_SHUTDOWN)
            WIN32_EXIT_CODE    : 0  (0x0)
            SERVICE_EXIT_CODE  : 0  (0x0)
            CHECKPOINT         : 0x0
            WAIT_HINT          : 0x0
            PID                : 3040
            FLAGS              :

  7. When you investigate the task list, you'll find one instance of mscorsvw.exe, as shown below:

    C:\temp>tasklist /FI "IMAGENAME eq mscorsvw.exe"

    Image Name                   PID Session Name     Session#    Mem Usage
    ========================= ====== ================ ======== ============
    mscorsvw.exe                3040 Console                 0      3,064 K

    This is the service which is running. You can also check the state of the service as follows, using ngen.exe:

    C:\temp>ngen queue status
    Microsoft (R) CLR Native Image Generator - Version 2.0.50215.44
    Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.

    Service name is: clr_optimization_v2.0.50215_32
    The .NET Runtime Optimization Service is running.

  8. In step 5, I've shown that the test.exe compilation is pending and has a priority of 3. This means the service is waiting till the computer is idle (which is measured based on the time of the last user input and the state of a screensaver, but this "idleness detection mechanism" is still to be finetuned towards RTM of .NET v2.0) in order to start working on priority 3 jobs that are in the queue. However, you can force this work to be done immediately, using the following command:

    C:\temp>ngen executeQueuedItems 3
    Microsoft (R) CLR Native Image Generator - Version 2.0.50215.44
    Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.
    Compiling 2 assemblies:
        Compiling assembly C:\temp\test.exe ...
    test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
        Compiling assembly testhelper, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null ...
    testhelper, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null


    Note: While this command is running (you need to be quick to see), another instance of mscorsvw.exe will be created (watch your Task Manager task list, sorted by Image Name).
  9. When you inspect the NGen cache now, you should find the following entries:

    C:\WINDOWS\assembly\NativeImages_v2.0.50215_32>dir test*
     Volume in drive C has no label.
     Volume Serial Number is DC7C-7836

     Directory of C:\WINDOWS\assembly\NativeImages_v2.0.50215_32

    09/01/2005  01:24 AM    <DIR>          test
    09/01/2005  01:24 AM    <DIR>          testhelper
                   0 File(s)              0 bytes
                   2 Dir(s)  119,775,739,904 bytes free

  10. In order to visualize the difference between priority 1, 2 and 3 perform now the following:

    C:\temp>ngen install test2.exe /queue:1
    Microsoft (R) CLR Native Image Generator - Version 2.0.50215.44
    Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.
    Installing assembly C:\temp\test2.exe

  11. Go to the other command prompt window. Because the priority was set to 1, the service is performing the compilation immediately, as you can see by showing the test* contents of the NGen cache:

    C:\WINDOWS\assembly\NativeImages_v2.0.50215_32>dir test*
     Volume in drive C has no label.
     Volume Serial Number is DC7C-7836

     Directory of C:\WINDOWS\assembly\NativeImages_v2.0.50215_32

    09/01/2005  01:24 AM    <DIR>          test
    09/01/2005  01:29 AM    <DIR>          test2
    09/01/2005  01:24 AM    <DIR>          testhelper
                   0 File(s)              0 bytes
                   3 Dir(s)  119,775,760,384 bytes free

I won't cover the other ngen.exe capabilities (such as the update action) over here for now. However, based on what I've shown above, you should have a good clue about the power the renewed ngen technollogy is offering in .NET v2.0. A classic scenario is queuing up precompilation work for a set of assemblies associated with your application at installation time. You'll queue compilation of the most critical files using priority 1 or 2 whileas you'll queue up compilation for the other executables (e.g. less used tools) using the default priority of 3 (compile during idle time). At installation time, you'll benefit from both queue-ups because the installer does not need to wait for the ngen tasks to complete (the service does the work in the background for you). The general skeleton looks like this:

ngen queue pause --keep the NGen image cache stable during setup (recommended)
ngen install <...> /queue --default priority = 3; recommended for less-used apps
ngen install <...> /queue:1 --enforce high priority compilation
ngen queue continue --okay, the NGen service can continue its mission

 

NGen: use it or leave it?

The official answer: it depends. The correct answer: it depends. You should measure the performance of both the JITted application and the NGen-ed application and compare the results in order to get a clue about the performance gains for your application. Purely theoritically, the following statements hold:

  • Precompiled applications require less memory because the JIT compiler doesn't need to be loaded into memory.
  • Precompiled applications can benefit from sharing memory pages for shared code across applications (i.e. DLLs). This also reduces memory consumption.
  • (Warm) startups of precompiled applications are faster than their non-JITted equivalents.

Furthermore, in v2.0 NGen has support for sharing of native images across application domains and all of the reflection stuff on native images. For a discussion on "hardbinding", I want to refer to the MSDN Article mentioned below in the "Useful links" section.

 

Useful links

 

Conclusion

Visual Studio 2005 is great, but don't forget about the .NET Framework SDK tools in .NET v2.0. There are just so many improvements in there (in the end, these tools are the core of the .NET Framework), it would be a pitty for you to miss any of these improvements. I'll try to point out things like this on a regular basis on my blog.

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under:

Comments

# re: To NGen or not to NGen

Wednesday, September 21, 2005 7:21 PM by bart

Great post! Just what I was looking for.

Thanks a bunch.

# re: To NGen or not to NGen

Wednesday, September 28, 2005 8:52 AM by bart

This is a great post but what happens if at installation time I NGEN my assemblies and then the CLR gets serviced. If I understand it correctly, my NGEN images will become invalidated. Is there a mechanism that the NGEN 2.0 provides to automatically NGEN those assemblies again that are part of my application or am I out of luck?

# The difference between readonly variables and constants

Tuesday, April 04, 2006 5:08 PM by B# .NET Blog

Just saw a little piece of code where one has been messing up those two concepts of readonly variables...

# The difference between readonly variables and constants

Tuesday, April 04, 2006 5:09 PM by B# .NET Blog

Just saw a little piece of code where one has been messing up those two concepts of readonly variables...

# The difference between readonly variables and constants

Tuesday, April 04, 2006 5:12 PM by B# .NET Blog

Just saw a little piece of code where one has been messing up those two concepts of readonly variables...

# Improving DotNet performance with Ngen: The Native Image Generator

Wednesday, March 26, 2008 10:49 AM by Rick Minerich's Development Wonderland

One of the most often cited reasons to not use .NET is that it is initially compiled to an intermediary

# Haytham El-Fadeel &raquo; Blog Archive

Sunday, June 22, 2008 10:15 PM by Haytham El-Fadeel » Blog Archive

Pingback from  Haytham El-Fadeel  &raquo; Blog Archive