When is a method eligible to be inlined by the CLR?

Hans Passant's answer notwithstanding, here first a couple of hints as of 2004, and further down some more up to date information. They are subject to change, but they do give you an idea on what to look for if you want to make a method eligible for inlining:

the JIT won’t inline:

  • Methods marked with MethodImplOptions.NoInlining
  • Methods larger than 32 bytes of IL
  • Virtual methods
  • Methods that take a large value type as a parameter
  • Methods on MarshalByRef classes
  • Methods with complicated flowgraphs
  • Methods meeting other, more exotic criteria

In particular, there is MethodImplOptions.AggressiveInlining, which is supposed to lift the 32 bytes limit (or whatever it happens to be these days and for your platform).

.Net 3.5 added heuristics that help it determine whether To Inline or not to Inline, which is probably a good thing, although it makes it harder for the developer to predict the jitter's decision:

A quote from the article:

  1. If inlining makes code smaller then the call it replaces, it is ALWAYS good. Note that we are talking about the NATIVE code size, not the IL code size (which can be quite different).

  2. The more a particular call site is executed, the more it will benefit from inlning. Thus code in loops deserves to be inlined more than code that is not in loops.

  3. If inlining exposes important optimizations, then inlining is more desirable. In particular methods with value types arguments benefit more than normal because of optimizations like this and thus having a bias to inline these methods is good.

Thus the heuristic the X86 JIT compiler uses is, given an inline candidate.

  1. Estimate the size of the call site if the method were not inlined.

  2. Estimate the size of the call site if it were inlined (this is an estimate based on the IL, we employ a simple state machine (Markov Model), created using lots of real data to form this estimator logic)

  3. Compute a multiplier. By default it is 1

  4. Increase the multiplier if the code is in a loop (the current heuristic bumps it to 5 in a loop)

  5. Increase the multiplier if it looks like struct optimizations will kick in.

  6. If InlineSize <= NonInlineSize * Multiplier do the inlining.


It is a jitter implementation detail, the x86 and x64 jitters have subtly different rules. This is casually documented in blog posts of team members that worked on the jitter but the teams certainly reserve the right to alter the rules. Looks like you already found them.

Inlining methods from other assemblies is most certainly supported, a lot of the .NET classes would work quite miserably if that wasn't the case. You can see it at work when you look at the machine code generated for Console.WriteLine(), it often gets inlined when you pass a simple string. To see this for yourself, you need to switch to the Release build and change a debugger option. Tools + Options, Debugging, General, untick "Suppress JIT optimization on module load".

There is otherwise no good reason to consider MethodImpOptions.NoInlining maligned, it's pretty much why it exists in the first place. It is in fact used intentionally in the .NET framework on lots of small public methods that call an internal helper method. It makes exception stack traces easier to diagnose.