|
JIT stands for Just-in-Time compilation. You can read more about JIT here. However, the idea is that the code is not fully compiled, it is pre-compiled. The first time your method is executed, the .NET runtime checks if the method has been compiled for the target machine; if not, then it compiles it on the fly. This is called JIT. This behaviour also causes a delay when executing methods for the first time. The bigger the method, the bigger the delay. I have noticed this delay the most when doing UI applications; the generated code would take a long time to JIT. You can also add performance counters to see how much time your application is JITting. In fact, most classes that use generated code can be good candidates for JITting (typed dataset, UI screens, EDMX...).
NGen is the alternative solution to runtime JITting. Everything is compiled by the NGen tool and a compiled image is placed within the GAC. At runtime, when loading the assembly, the runtime will check for a compiled image based on the assembly name and version; if it finds one, it will load it without JITting. Still, NGen needs to run on the target machine before running the application. NGen is also not as efficient as JIT; that's because JIT executes at runtime, and has much more information about the method and how to optimize it. You can read more about NGen here.
I always wanted to have something to control the JITting at runtime. Similar to the GC class, I can control when the GC is executed at runtime; there is not built in class called JIT that will allow me to JIT certain methods. However, it is possible. The method that allows for this type of functionality isRuntimeHelpers.PrepareMethod(RuntimeMethodHandle)
. However, here is a little warning for you all, in the MSDN article it indicates: The classes in System.Runtime.CompilerServices
are for compiler writers' use only.Still, I decided to use it; there are times that I would like to control when JITting happens.
The idea is very simple. I want to have a helper class that will JIT any method that is marked with the PreJit
attribute. Normally, the coder knows which all are the heavy methods; this way, the coder has the control to mark methods that can take longer to JIT. For example:
[PreJit] private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Text = "Form1"; }
Notice the PreJit
attribute on the method. PreJit
is just a marker attribute. Here is the code for it.
[AttributeUsage(AttributeTargets.Method)] public class PreJitAttribute : Attribute { public PreJitAttribute() { } }
Now, all that is left to do is JIT the methods that are marked with the [PreJit]
attribute. Let's take a look at a method that handles JITting based on the type of the CLR class.
private static void PreJitMarkedMethods(Type type) { // get the type of all the methods within this instance var methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); // for each time, jit methods marked with prejit attribute foreach (var method in methods) { // checks if the [PreJit] Attribute is present if (ContainsPreJitAttribute(method)) { // jitting of the method happends here. RuntimeHelpers.PrepareMethod(method.MethodHandle); } } } // (helper method) checks if the [PreJit] attribute is present on a method private static bool ContainsPreJitAttribute(MethodInfo methodInfo) { var attributes = methodInfo.GetCustomAttributes(typeof(PreJitAttribute), false); if (attributes != null) if (attributes.Length > 0) { // attribute found return true return true; } return false; }
Let's note the important stuff. This method is able to JIT all marked methods with the PreJit
attribute based on the CLR type.
[PreJit]
.MethodInfo
object.MethodInfo.MethodHandle
to PrepareMethod
allows us to JIT a method.Now that it is possible for me to JIT every method within a type, all I have to do is create high level methods that will allow me to do the following:
PreJit
attributeLet's see the full class code for the JITter:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.Threading; using System.Runtime.CompilerServices; namespace JitHelper { public class Jitter { public static void PreJit(object instance) { PreJitMarkedMethods(instance.GetType()); } public static void PreJitAll(object instance) { PreJitAllMethods(instance.GetType()); } public static void BeginPreJitAll(object instance) { Thread preJitThread = new Thread(() => { PreJitAllMethods(instance.GetType()); }); preJitThread.Name = "PreJittingThread"; preJitThread.Priority = ThreadPriority.Lowest; preJitThread.Start(); } public static void PreJit() where T : class { PreJitMarkedMethods(typeof(T)); } public static void PreJitAll () where T : class { PreJitAllMethods(typeof(T)); } public static void BeginPreJitAll () where T : class { Thread preJitThread = new Thread(() => { PreJitAllMethods(typeof(T)); }); preJitThread.Name = "PreJittingThread"; preJitThread.Priority = ThreadPriority.Lowest; preJitThread.Start(); } public static void PreJitAll(Assembly assembly) { var classes = assembly.GetTypes(); foreach (var classType in classes) { PreJitAllMethods(classType); } } public static void BeginPreJitAll(Assembly assembly) { Thread preJitThread = new Thread(() => { PreJitAll(assembly); }); preJitThread.Name = "PreJittingThread"; preJitThread.Priority = ThreadPriority.Lowest; preJitThread.Start(); } public static void PreJit(Assembly assembly) { var classes = assembly.GetTypes(); foreach (var classType in classes) { PreJitMarkedMethods(classType); } } public static void BeginPreJit(Assembly assembly) { Thread preJitThread = new Thread(() => { PreJit(assembly); }); preJitThread.Name = "PreJittingThread"; preJitThread.Priority = ThreadPriority.Lowest; preJitThread.Start(); } public static void BeginPreJit(object instance) { Thread preJitThread = new Thread(() => { PreJit(instance); }); preJitThread.Name = "PreJittingThread"; preJitThread.Priority = ThreadPriority.Lowest; preJitThread.Start(); } private static void PreJitMarkedMethods(Type type) { // get the type of all the methods within this instance var methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); // for each time, jit methods marked with prejit attribute foreach (var method in methods) { if (ContainsPreJitAttribute(method)) { // jitting of the method happends here. RuntimeHelpers.PrepareMethod(method.MethodHandle); } } } private static void PreJitAllMethods(Type type) { // get the type of all the methods within this instance var methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); // Jit all methods foreach (var method in methods) { // jitting of the method happends here. RuntimeHelpers.PrepareMethod(method.MethodHandle); } } private static bool ContainsPreJitAttribute(MethodInfo methodInfo) { var attributes = methodInfo.GetCustomAttributes(typeof(PreJitAttribute), false); if (attributes != null) if (attributes.Length > 0) { return true; } return false; } } }
A few notes about the code:
PreJit
attribute.DllExport
are not .NET methods, so be careful with the All feature. For performance reasons, I have not checked for the DllExport
attribute, but you can add it if needed.typeof
operator.I will show a simple example. In the following example, everything within Form1
is pre-JITted.
[STAThread] static void Main() { // Jitting all the methods within Form1 class. Jitter.PreJitAll(); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); }
If you want to pre-JIT within a thread, then simply use:
[STAThread] static void Main() { Jitter.BeginPreJitAll(); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); }
Let me first say that I coded this class mostly because I wanted to have some control over JITting. But overall, the .NET runtime does a very good job JITting when needed. The same way that we should not call GC.Collect()
, we should not call JITting functions either. Having said that, there are times it would be nice to control JITting and have an alternative to NGen. Use this tool only if you have a performance issue, or a startup timing issue. If you do not have a performance issue - do not use this. This class also uses Reflection to find out the methods to JIT. Reflection is slow, which is another reason to avoid using this class unless required. You should consider trying pre-JITting your classes when you are using generated code classes, such as typed datasets, LINQ to Entities, or WinForms UI classes.
Even if you are not planning to use the JITter class, it is better to have the JITter class and not need it, than need it and not have it. Thank you for reading. Have a nice day, and happy .NETting.
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)
|
mikeperetz
Web Developer
Canada
Member
|