Aggressive Inlining in the CLR 4.5 JIT

January 20, 2012

7 comments

Inlining is an important optimization that allows compilers to eliminate the cost of method calls in situations where the method call overhead is more significant than the method body itself. The CLR JIT uses inlining conservatively, but features some nice tricks such as interface method call inlining – this was one of the first things I covered on this blog, almost five years ago.

The limitations on JIT inlining are not known precisely, but some criteria have been announced previously (in 2004!). Namely, 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

Today, however, I’d like to direct your attention towards a new flag in CLR 4.5, MethodImplOptions.AggressiveInlining. The documentation here is very brief:

The method should be inlined if possible.

Well, thanks. Interestingly, Mono introduced support for this option as well, committed on January 5th (two weeks ago!), and here’s the effect it has on deciding whether to inline a method, inside the function mono_method_check_inlining:

-  if (header.code_size >= inline_limit)
+  if (header.code_size >= inline_limit && !(method->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING))

(The inline_limit parameter is configurable by an environment variable, and defaults to 20.)

So, what is the effect of AggressiveInlining on the Microsoft JIT?

From what I checked, it seems to be similar to what it does in Mono. Namely, methods that are not inlined only because of code size are inlined when this attribute is applied to them.

Here are two methods that you can try yourself:

public static int SmallMethod(int i, int j)
{
    if (i > j)
        return i + j;
    else
        return i + 2 * j – 1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int LargeMethod(int i, int j)
{
    if (i + 14 > j)
    {
        return i + j;
    }
    else if (j * 12 < i)
    {
        return 42 + i – j * 7;
    }
    else
    {
        return i % 14 – j;
    }
}

The code size for these methods is 16 and 34, respectively. Without the AggressiveInlining attribute, the first method is inlined and the second is not inlined. With the AggressiveInlining attribute, the second method is inlined as well.

However, methods that couldn’t be inlined previously because of other criteria are still not inlined. I checked the following, and neither of these methods was inlined:

  • Recursive method
  • Virtual method (even if the static type of the receiver variable is sealed)
  • Method with exception handling (representing a “complicated flowgraph”)

I am posting short updates and links on Twitter as well as on this blog. You can follow me: @goldshtn

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

7 comments

  1. Francois NelDecember 6, 2012 ב 3:51 AM

    Is a simple “throw” of an exception considered a “complicated flowgraph”?

    Reply
  2. Sasha GoldshteinDecember 24, 2012 ב 7:02 AM

    AFAIK a throw is not considered a complicated flowgraph, and will not prevent inlining.

    Reply
    1. DmitryJanuary 20, 2016 ב 11:33 AM

      Sasha, it seems to me that throwing an exception affects JIT possibility to inlining a method. I wrote a very simple method that only throws an exception of type Exception. And it was’t inlined. Even [MethodImpl(MethodImplOptions.AggressiveInlining)] attribute didn’t help. It is JIT-x86 that behaves this way. You may encounter another behaviour considering x64-JIT or RyuJIT. Can you please shed some light?

      Reply
      1. Sasha Goldshtein
        Sasha GoldshteinJanuary 21, 2016 ב 10:34 AM

        What’s the exact method body you’re trying to inline? How are you testing whether it was or was not inlined?

        Reply
        1. DmitryJanuary 22, 2016 ב 11:14 AM

          Sasha,

          The method body is:
          public class A
          { public static bool Method(char c)
          {
          throw new Exception();
          }
          }

          I invoke this method like this:

          bool x = A.Method(‘c’);
          System.Console.WriteLine(x);

          I use Visual Studio Disassembly window to investigate jitted assembly code. And it looks like the following:

          push ebp
          mov ebp,esp
          push esi
          mov ecx,63h
          call dword ptr ds:[002A3860h]
          mov esi,eax

          Thus, it seems to me that there is no inlining.

          Reply
          1. Sasha Goldshtein
            Sasha GoldshteinJanuary 22, 2016 ב 3:05 PM

            Indeed, I was able to reproduce your results. It is the same with the x64 JIT as well. Perhaps the criteria for inlining changed.

  3. Pingback: The Art of Benchmarking (Updated 2014-09-23) | Matt on Software