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

5

42

I've observed a lot of "stack-introspective" code in applications, which often implicitly rely on their containing methods not being inlined for their correctness. Such methods commonly involve calls to:

  • MethodBase.GetCurrentMethod
  • Assembly.GetCallingAssembly
  • Assembly.GetExecutingAssembly

Now, I find the information surrounding these methods to be very confusing. I've heard that the run-time will not inline a method that calls GetCurrentMethod, but I can't find any documentation to that effect. I've seen posts on StackOverflow on several occasions, such as this one, indicating the CLR does not inline cross-assembly calls, but the GetCallingAssembly documentation strongly indicates otherwise.

There's also the much-maligned [MethodImpl(MethodImplOptions.NoInlining)], but I am unsure if the CLR considers this to be a "request" or a "command."

Note that I am asking about inlining eligibility from the standpoint of contract, not about when current implementations of the JITter decline to consider methods because of implementation difficulties, or about when the JITter finally ends up choosing to inline an eligible method after assessing the trade-offs. I have read this and this, but they seem to be more focused on the last two points (there are passing mentions of MethodImpOptions.NoInlining and "exotic IL instructions", but these seem to be presented as heuristics rather than as obligations).

When is the CLR allowed to inline?

Hodgkin answered 11/1, 2011 at 16:34 Comment(5)
"When is the CLR allowed to inline?" I'd guess whenever that attribute isn't present.Upsweep
@CodeInChaos: So you're saying that Assembly.GetExecutingAssembly is broken unless we decorate the method with [MethodImpl(MethodImpOptions.NoInlining)] ?Hodgkin
Here is a good link (does not answers all your questions thought...): blogs.msdn.com/b/vancem/archive/2008/08/19/…Untune
Hmm, sounds like a spec-bug: I understand the technical ease in doing it otherwise, but obviously GetCallingAssembly and friends should never have been specced as inline-sensitive... sigh - and GetExecutingAssembly's docs don't mention inlining at all!Pelorus
Not an answer to your question, but from Visual Studio 2012 onwards, getting the caller method/property name, its file location and line number can now be done with the compiler's help using the Caller Information attributes, i.e. unaffected by JIT inlining. Plus it is blazingly fast, in contrast to stack (frame creation and subsequent) inspection.Hospodar
K
25

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.

Kapoor answered 11/1, 2011 at 18:6 Comment(2)
From what you have said, can I infer that void M()``{Console.WriteLine(MethodBase.GetCurrentMethod().Name);} is not guaranteed to output M but is guaranteed to output M after I decorate it with NoInlining? What about Assembly.GetCallingAssembly and Assembly.GetExecutingAssembly? I very rarely see code calling Assembly.GetExecutingAssembly being decorated with the attribute although the "this assembly" intent is there.Hodgkin
That is correct. Yes, the method that contains code like this should be decorated with the attribute if it is public. It probably survives due to the size of the method.Kapoor
H
8

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.

Hospodar answered 17/7, 2015 at 1:14 Comment(0)
N
3

While Hans' answer is correct, there is one omission, not necessarily about when a method is eligible for inlining, but when a method is not.

Abstract and virtual methods are not eligible for inlining in the CLR.

It's important to note as it whittles down the conditions under which a method may be inlined.

Nichrome answered 26/1, 2011 at 0:24 Comment(2)
I don't agree; this is an implementation detail that happens to currently be true. ["We could potentially do better here (for example, if 99% of calls end up in the same target, you can generate code that does a check on the method table of the object the virtual call is going to execute on, if it's not the 99% case, you do a call, else you just execute the inlined code)"]. blogs.msdn.com/b/davidnotario/archive/2004/11/01/250398.aspxHodgkin
Randomly found this -- and discovered that links back to my question. :-)Granicus
D
1

There's more information on inlining of MethodBase.GetCurrentMethod on this thread http://prdlxvm0001.codify.net/pipermail/ozdotnet/2011-March/009085.html

Paraphrasing heavily, it states that the RefCrawlMark does NOT stop the calling method being inlined. However, RequireSecObject does have the side affect of stopping the caller being inlined.

In addition, the Assembly.GetCallingAssembly and Assembly.GetExecutingAssembly methods do NOT have this attribute.

Donatist answered 9/6, 2012 at 0:3 Comment(0)
O
0

There was an article posted on MSDN in 2003 called Writing High-Performance Managed Apps that covers the outlines several criteria quite clearly:

  • Methods that are greater than 32 bytes of IL will not be inlined.
  • Virtual functions are not inlined.
  • Methods that have complex flow control will not be in-lined. Complex flow control is any flow control other than if/then/else; in this case, switch or while.
  • Methods that contain exception-handling blocks are not inlined, though methods that throw exceptions are still candidates for inlining.
  • If any of the method's formal arguments are structs, the method will not be inlined.

Sacha Goldshtein's blog article in 2012 on aggressive inlining in the CLR has much of the same advice.

Originally answered 30/3, 2019 at 13:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.