JIT Compilation and Performance - To NGen or Not to NGen?

Jeffrey Richter is easily one of my favorite authors. His book “Programming Applications for Microsoft Windows” is still the most comprehensive Win32 programming guide book. And his book “Applied Microsoft .Net Framework programming” is certainly considered as one of the bibles for .Net framework programming.

 

Jeffrey wrote a short article in codeguru.com (https://www.codeguru.com/Csharp/.NET/net_general/toolsand3rdparty/article.php/c4651) discussing NGEN. He pointed out six reasons why NGEN should not be considered. I do not agree with Jeffrey. And I will rebut them one by one:

 

No Intellectual Property Protection

 

Yes, I understand NGEN is not to replace my IL assemblies. But I am looking for startup performance gain. I am not looking for protecting my IP. I’ll use an obfuscator for that.

 

NGen’d Files Can Get Out-Of-Sync

 

Yes, NGEN’d file can get out-of-sync. But it is not like if I don’t change anything, NGEN’d file will magically get out-of-sync. Today NGEN’d file will be out-of-sync because 1. Servicing of .Net Framework. 2. Servicing of my assemblies. Both of them are controllable. And I can re-ngen my assemblies if necessary.

 

Plus, not be able to use ngen’d file when environment changes is not an argument to not use NGEN. It just means NGEN is not perfect.

 

Poor Administration

 

Yes, NGEN’s file needs to be manually removed by calling “ngen /delete”. But why is this the reason to not use NGEN at all? I can write a simple batch file to call NGEN on install time, and “NGEN /delete” on uninstall time. Beyond that, there is no other reason why it will affect XCOPY deployment scenario.

 

Inferior Load-Time Performance (Rebasing)

 

This is interesting. NGEN is all about startup performance. And Jeffrey said NGEN has worse startup performance?

 

Oh wait, Jeffrey said there are times NGEN’d file could be rebased, and once that happen, performance would be bad.

 

Yes, true. But how is this different from normal native dlls rebasing? So because native dlls could be rebasd, so we should not use native dlls at all? Now what we do? Ship an exe containing everything?

 

Of course, when rebasing happens, the running performance will not be very good. CLR loader actually detects it, and will reject the NGEN’d file if it is rebased, and fall back to normal JIT.

 

Inferior Execution-Time Performance

 

This is another never ending debate. Jitted code can be faster than native code, because it has more knowledge about the running environment, and can make smart decision. This is most true. But in CLR jitter can take less advantage of knowledge about running environment because CLR jitter essentially will only jit once, and will never jit the same code again. So what the jitter has is, the environment before a piece of code is jitted. It is not as smart as Java HotSpt jitter, who will moniter a piece of jitted code, and re-jit if necessary. So in CLR, the running environment knowledge is not at its best use.

 

Another important factor is, NGEN can do large scale optimization. NGEN can compile several assemblies together, under an application’s context. So NGEN can make better optimization decision based on how a piece of code is used in the application context (though statically). This is something runtime jitter can’t have given its memory and timing constrains.

 

And statistics shows, NGEN’d file at worst performs 5-10% worse than runtime jitter. And it has a big win in startup time since you don’t have to compile the IL assembly. I will certainly accept the very small percentage runtime performance decrease, in trade of the startup performance gain.

 

Ignored NGen'd File in Some Domain Load Scenarios

 

NGEN’d file has some constrains. And it is not loadable in some scenarios. But again, this is not the reason to not use NGEN. It just means NGEN is not perfect. Yes, it does not help all scenarios. But it helps scenarios it can help.

 

But I am glad to see that Jeffrey said at the end of the article that he recommends NGEN for client applications. So he did not totally dismiss NGEN.

 

Another very important advantage of NGEN is, since it is a native image, it can be shared by many processes. For normal IL assemblies, jitter has to jit the code in the process’s private address, which means the jitted code can’t be shared across processes. This is huge if many applications are using the same assemblies. This is the reason why some commonly used framework assemblies are NGEN’d. And for a terminal services server, it is unimaginable to not have NGEN.

 

For server applications, NGEN makes less sense since they tend to run only one managed application (usually ASP.NET), and server applications tend to think about raw steady state runtime performance, and care less about startup time.

 

And of course, follow RicoM’s performance rule, whatever you do, measure it.