NGen: Getting Started with NGen in Visual Studio
This is article 1 of 4 in the NGen: Walkthrough Series .
Hey there managed code developer. So you’d like to test drive the NGen technology in the .Net Framework? This article will walk you through how to use NGen for your existing solution in Visual Studio 2010.
To familiarize yourself with the concepts around NGen (how it works and for what style of application/library it makes sense to use), I strongly encourage you to first read through this excellent MSDN CLR Inside Out article around the Performance Benefits of NGen: http://msdn.microsoft.com/en-us/magazine/cc163610.aspx. Now assuming you have some background on when to use NGen, let’s get started. I’m assuming you have identified that NGen will likely benefit your application (perhaps your application has been identified as having poor warm startup due to JIT-ing of method calls OR you are working with a library that gets loaded into several processes on a machine and you would like to reduce the library’s impact on the total working set of the machine.)
For the purposes of this walk-through, I assume you already have a Visual Studio solution for your application. This series of steps will provides guidance on how to invoke NGen for your application from within the Visual Studio solution. Using NGen via the set of steps outlined below will result in native images created only on the specific machine where the steps are run. Note also that this article does not provide instructions on how to invoke NGen in an installer package; a future article will talk about that scenario.
Adding a post-build event to run NGen.exe
Setting this up involves a fairly straightforward set of steps.
In your Visual Studio solution, under the Project menu, go to Properties and then go to the Build Events tab. Under the Post-build event command line, click on Edit Post-Build…
Specify the command line as %WINDIR%\Microsoft.Net\Framework\v4.0.30319\ngen.exe install “$(TargetPath)”
At the time of this writing, there is no Macro in Visual Studio that points to ngen.exe, so you will need to specify the path yourself. An easy way to locate the path to ngen.exe is to open up the Visual Studio Command Prompt (All Programs -> Microsoft Visual Studio 2010 -> Visual Studio Tools -> Visual Studio Command Prompt) and type “where ngen.exe” .
The NGen install command creates native images for the target specified along with any static dependencies that target may have.
Note that multiple post-build events can be specified in the Edit Post Build… window by specifying each command on a separate line.
Under the Project menu go to Properties and in the Debug tab, uncheck the box for Enable the Visual Studio Hosting Process.
Keep in mind that you need to be intentional about the bitness of NGen.exe that you use. If your development environment is on a 64 bit machine, the application in Visual Studio by default will be created as a 32 bit application, and you should use the 32 bit ngen.exe in the Post-Build Event. However, on 64 bit machines, outside the Visual Studio development environment, the application will run against the 64 bit runtime, and so you need to NGen using the 64 bit NGen.exe tool.
(On a related note, during build, if you choose to assign base addresses to the DLLs created in your Visual Studio Solution, the way to do that is under Project menu go to Properties and in the Build tab click on Advanced…
In the Advanced Build Settings window, under Output, the DLL Base Address field lets you specify the base address to assign to the DLL.)
1> How to: Specify Build Events: http://msdn.microsoft.com/en-us/library/ke5z92ks(VS.80).aspx
2> How to install an assembly into the GAC: http://support.microsoft.com/kb/815808
Ensuring the post-build event worked
Now that you’ve added your post-build event to run NGen.exe on the built executables, it’s important to determine 2 things –
1> Whether native images were generated successfully, AND
2> Whether native images got loaded during runtime
Ensuring native images were generated successfully
After building the solution (F6), look at the Error List window to ensure there are no errors. If there are errors during build, look at the Output window to see where the error is coming from and if it is from the NGen.exe tool.
Some common reasons why NGen.exe might have failed are –
1> Visual Studio is not being run with Administrator credentials. NGen.exe is an Admin tool and the post-build step will fail with the following error if you run without the appropriate credentials –
Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
Administrator permissions are needed to use the selected options. Use an administrator command prompt to complete these tasks.
2> The post-build event command line contains the macro $(TargetPath), but this path contains spaces in it. Changing this to be “$(TargetPath)” should fix the problem.
Ensuring native images got loaded during runtime
A native image file has a “.ni.dll” or “.ni.exe” file extension. Note that you do not need to do anything special to run a native image (like providing a path to the native image file). When you run hello.exe (the IL assembly), the Common Language Runtime’s assembly loader will check if a native image file exists for this IL assembly and will just load that instead of the IL.
The first thing to check for is that the native image files were loaded during runtime. The next thing to check for is whether the JIT got used to compile some methods in an assembly even though the native image for the assembly got loaded.
There are a couple different ways to validate that the native image files were loaded during runtime.
1> Use the SDK tool Fusion Log Viewer: Easiest if running outside of Visual Studio
Launch the tool via the Visual Studio Command Prompt. Under Settings, enable Log All Binds to Disk. (You may also need to check Enable custom log path and provide a custom log path).
Run your application.
In the Log Categories box, select Native Images and hit Refresh.
The format of each entry in this log is such that Application column is the name of the executable and Description column contains the name of the assembly being loaded.
If you open the log entry, at the bottom of the text will be a “WRN: No matching native image found.” If the native image for that assembly could not be loaded.
2> Inspect the list of loaded modules in Visual Studio: useful if running within Visual Studio
The list of modules does not contain the native image files by default if your VS Solution was created for managed code. In order to work around this problem, from the Project menu go to Properties and in the Debug tab, mark the check box for Enable Unmanaged Code Debugging.
While debugging, from the Debug menu go to Windows and Modules, to see a list of the modules loaded into the process.
1> Assembly Binding Log Viewer (fuslogvw.exe): http://msdn.microsoft.com/en-us/library/e74a18c4.aspx
2> More tricks on how to tell if the NGen image got loaded: http://blogs.msdn.com/jmstall/archive/2006/10/04/debugging_5F00_ngen_5F00_code.aspx
Ensuring that the JIT did not get used during runtime
You might be wondering what the difference between the previous section “Ensuring native images got loaded during runtime” and this one is. If the native image file got loaded for an assembly, the JIT should not fire for it, right? Well, not quite. Sometimes, even though the native image might have been used for the bulk of the method in that assembly, the JIT does get loaded for some methods, or types of code within the assembly being executed; for example, some generic instantiations, dynamically generated code, multiple AppDomain scenarios etc. So, even though the native image file might have been loaded for an assembly and we may have used the native code from that assembly for most of our method execution needs, for some methods, we may have to fall back to JIT. It is important to know what those methods are for your application.
Moreover, if your application has several DLLs, it may not be a trivial amount of work to ensure that each DLL had a corresponding “.ni.dll” loaded for it, by inspecting either Fusion Log Viewer content or the Modules window content. Using the JitCompilationStart MDA will help in such scenarios as well.
As of this writing, the easiest way to determine this is by using the JitCompilationStart Managed Debugging Assistant. The 2 MSDN articles listed under “Further Reading” below give some excellent background information on what MDAs are, how to use them and how the JitCompilationStart MDA can be used.
Now let’s look at the practical steps for how to set up your Visual Studio configuration correctly so that the JitCompilationStart MDA will fire when it needs to.
1> Disable the Visual Studio hosting process. See http://msdn.microsoft.com/en-us/library/ms185330(VS.80).aspx for instructions.
2> If your application executable name is hello.exe, add a hello.exe.mda.config file to your solution with the contents below and in the Properties set Copy to Output Directory to Copy Always.
<match name="*" />
3> Under the menu Debug go to Exceptions and set Managed Debugging Assistants to Thrown.
4> From the Project menu go to Properties and in the Debug tab, mark the check box for Enable Unmanaged Code Debugging.
5> Finally, in order to enable the MDA infrastructure to look at the hello.exe.mda.config file you created in Step 2, you need to update the registry. Under HKLM\Software\Microsoft\.NetFramework, create a REG_SZ sub-key called “MDA” and set its value to “1”.
Yes I understand this is an unnecessarily large set of steps to go through to enable this particular MDA, we hope to improve this experience in a future release of Visual Studio.
Now that the JitCompilationStart MDA is enabled, all you need to do is hit F5! If a method does get JIT-ed, an unhandled exception dialog box will be generated of the type below.
Select Break in the dialog box to see the line of disassembly for which the JIT got loaded. The Call Stack window will also show the unmanaged call stack at which the break point occurs. This will provide a clue as to what part of your code caused the JIT to load.
1> Diagnosing Errors with Managed Debugging Assistants: http://msdn.microsoft.com/en-us/library/d21c150d(VS.80).aspx
2> JitCompilationStart: http://msdn.microsoft.com/en-us/library/fw872k46(VS.80).aspx
3> Description for how JIT can get loaded for multiple AppDomain scenarios: http://msdn.microsoft.com/en-us/magazine/cc163610.aspx#S6
Wrapping it up!
That summarizes the very basics of using Visual Studio to run NGen.exe for your application. This article hopefully articulates all the various steps needed to get the environment right, and points out potential pitfalls along the way. We’d love to hear what you think! Have you used Visual Studio in the past to enable NGen for your application? If not, what do you use? How was your experience? Please use the comments section below for any feedback and questions.
CLR Codegen team