Click here to Skip to main content
15,868,141 members
Articles / Programming Languages / C#

JIT methods at runtime

Rate me:
Please Sign up or sign in to vote.
4.95/5 (50 votes)
15 Mar 2009CPOL5 min read 56.1K   504   77   8
How to JIT methods at runtime.

What is JIT

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

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.

Is it possible to control the JITting

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 is RuntimeHelpers.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.

JitHelper and the PreJit attribute

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:

C#
[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.

C#
[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.

C#
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.

  • Getting all methods, private, public and even static.
  • Only JIT methods that have an attribute of [PreJit].
  • On each method, we get the MethodInfo object.
  • Passing MethodInfo.MethodHandle to PrepareMethod allows us to JIT a method.

The JITter

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:

  • JIT only methods that are marked with the PreJit attribute
  • JIT on a different thread
  • JIT based on an instance (JIT methods within the instance)
  • JIT based on a Type (JIT methods for a given class type)
  • JIT based on an assembly (JIT methods within all the classes in an assembly)

Let's see the full class code for the JITter:

C#
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<T>() where T : class
        {
            PreJitMarkedMethods(typeof(T));
        }


        public static void PreJitAll<T>() where T : class
        {
            PreJitAllMethods(typeof(T));
        }

        public static void BeginPreJitAll<T>() 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:

  • Methods beginning with "Begin" are executed on a low priority thread. This allows for background JITting. I have not done a callback feature to know when JITting ends; I don't think it is required, but you are free to modify the class and add it.
  • Methods ending with "All" will JIT all methods, and not just the ones marked with the PreJit attribute.
  • Not all methods can be PreJit; for example, methods marked as 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.
  • Notice that one method uses Generics; this is simply so the caller can pass the type via generics without doing the typeof operator.

Using the JITter

I will show a simple example. In the following example, everything within Form1 is pre-JITted.

C#
[STAThread]
static void Main()
{
    // Jitting all the methods within Form1 class.
    Jitter.PreJitAll<Form1>();
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form1());
}

If you want to pre-JIT within a thread, then simply use:

C#
[STAThread]
static void Main()
{
    Jitter.BeginPreJitAll<Form1>();
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form1());
}

Conclusion

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Canada Canada
I am currently working as a team leader with a group of amazing .NET programmers. I love coding with .NET, and I love to apply design patterns into my work. Lately I had some free time, so I decided to write some articles, hoping I will spare someone frustration and anxiety.

Comments and Discussions

 
SuggestionAdd this to nuget Pin
Member 29617712-Jul-17 2:00
Member 29617712-Jul-17 2:00 
QuestionMy vote of 5 Pin
Yevgeni Zolotko13-Nov-13 15:38
Yevgeni Zolotko13-Nov-13 15:38 
Thank you!
GeneralClickOnce Pin
roberto lapolli9-Oct-09 8:28
professionalroberto lapolli9-Oct-09 8:28 
GeneralNice one.. Pin
Bala Rajesh7-Apr-09 0:45
Bala Rajesh7-Apr-09 0:45 
GeneralSimilar article Pin
Giorgi Dalakishvili15-Mar-09 21:52
mentorGiorgi Dalakishvili15-Mar-09 21:52 
GeneralGreat idea. Pin
stano15-Mar-09 18:17
stano15-Mar-09 18:17 
GeneralGood one Pin
N a v a n e e t h15-Mar-09 7:01
N a v a n e e t h15-Mar-09 7:01 
GeneralRe: Good one Pin
mikeperetz15-Mar-09 7:51
mikeperetz15-Mar-09 7:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.