MethodImpl(AggressiveInlining) - how aggressive is it?
Asked Answered
U

1

58

So I was having a bit of a look at the MethodImplAttribute and I came across MethodImplOptions.AggressiveInlining which is described as:

The method should be inlined if possible.

Oooooh, I thought to myself, that sounds interesting - I can pretend I am cleverer than a compiler and force code to be inlined using just the power of my evil will and a couple of square brackets, ha, ha, ha...

I therefore started up Visual Studio 2013, created a console application, set the .NET version to 4.5.1 and wrote the program to end all programs (compiling it in Release mode, of course):

using System;
using System.Runtime.CompilerServices;

namespace ConsoleApplication1
{   
    public static class SoHelpful
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int GetSomeValueProbablyTwelve()
        {
            return 12;
        }
        [MethodImpl(MethodImplOptions.NoInlining)]
        public static int GetSomeValueLikelyThirteen()
        {
            return 13;
        }
        public static int GetSomeValueMaybeItIsTwentyEight()
        {
            return 29;
        }
    }

    class Program
    {       
        static void Main()
        {
            int twelve = SoHelpful.GetSomeValueProbablyTwelve();
            int thirteen = SoHelpful.GetSomeValueLikelyThirteen();
            int twentyNine = SoHelpful.GetSomeValueMaybeItIsTwentyEight();
            Console.WriteLine((twelve + thirteen + twentyNine));
        }
    }
}

Now, if I do a swift ildasm I see this:

.class public abstract auto ansi sealed beforefieldinit ConsoleApplication1.SoHelpful
       extends [mscorlib]System.Object
{
  .method public hidebysig static int32  GetSomeValueProbablyTwelve() cil managed
  {
    // Code size       3 (0x3)
    .maxstack  8
    IL_0000:  ldc.i4.s   12
    IL_0002:  ret
  } // end of method SoHelpful::GetSomeValueProbablyTwelve

  .method public hidebysig static int32  GetSomeValueLikelyThirteen() cil managed noinlining
  {
    // Code size       3 (0x3)
    .maxstack  8
    IL_0000:  ldc.i4.s   13
    IL_0002:  ret
  } // end of method SoHelpful::GetSomeValueLikelyThirteen

  .method public hidebysig static int32  GetSomeValueMaybeItIsTwentyEight() cil managed
  {
    // Code size       3 (0x3)
    .maxstack  8
    IL_0000:  ldc.i4.s   29
    IL_0002:  ret
  } // end of method SoHelpful::GetSomeValueMaybeItIsTwentyEight

} // end of class ConsoleApplication1.SoHelpful

.class private auto ansi beforefieldinit ConsoleApplication1.Program
       extends [mscorlib]System.Object
{
  .method private hidebysig static void  Main() cil managed
  {
    .entrypoint
    // Code size       29 (0x1d)
    .maxstack  2
    .locals init ([0] int32 twelve,
             [1] int32 thirteen,
             [2] int32 twentyNine)
    IL_0000:  call       int32 ConsoleApplication1.SoHelpful::GetSomeValueProbablyTwelve()
    IL_0005:  stloc.0
    IL_0006:  call       int32 ConsoleApplication1.SoHelpful::GetSomeValueLikelyThirteen()
    IL_000b:  stloc.1
    IL_000c:  call       int32 ConsoleApplication1.SoHelpful::GetSomeValueMaybeItIsTwentyEight()
    IL_0011:  stloc.2
    IL_0012:  ldloc.0
    IL_0013:  ldloc.1
    IL_0014:  add
    IL_0015:  ldloc.2
    IL_0016:  add
    IL_0017:  call       void [mscorlib]System.Console::WriteLine(int32)
    IL_001c:  ret
  } // end of method Program::Main

  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  } // end of method Program::.ctor

} // end of class ConsoleApplication1.Program

Interesting, so the three methods are defined:

.method public hidebysig static int32  GetSomeValueLikelyThirteen() cil managed noinlining
.method public hidebysig static int32  GetSomeValueProbablyTwelve() cil managed
.method public hidebysig static int32  GetSomeValueMaybeItIsTwentyEight() cil managed

It looks, therefore, like my aggressive inlining attribute is lost.

I can see noinlining on GetSomeValueLikelyThirteen but GetSomeValueProbablyTwelve and GetSomeValueMaybeItIsTwentyEight are the same.

So what has happened? I suppose there are a few possibilities:

  1. The C# compiler has realised that GetSomeValueProbablyTwelve just cannot be inlined so is not going to trouble the JIT with my foolish attribute idiocy.
  2. The C# compiler has realised that GetSomeValueMaybeItIsTwentyEight can be inlined and that is why it is the same in the IL as GetSomeValueProbablyTwelve (so my attributing was largely pointless and idiotic in this case also).
  3. There's some compile setting or something I have to tweak to get the attribute to be recognised.
  4. It is not implemented in C# at all.
  5. I have found a bug
  6. Something else

So, does anyone know which it is?

Unconcerned answered 11/12, 2013 at 10:49 Comment(5)
Hmm... compiling it from the command line, I get aggressiveinlining on GetSomeValueProbablyTwelve. Trying under Visual Studio now...Convention
Nope, just tried in VS2013 as well, and I'm seeing aggressiveinlining there too. Which version of ildasm are you running?Convention
Using .NET Reflector, I can still see the AggressiveInlining flag in the decompiled C# code (but Reflector doesn't show it in the IL view)Maurey
Arrrgggghh, that was it! I was using version 4.0.30319.1 of ILDASM but in 4.0.30319.18020 it works. I feel so stupid. Thanks for your time.Unconcerned
LOL GetSomeValueMaybeItIsTwentyEight(){ return 29; }Sestet
U
38

MethodImplAttributes.AggressiveInlining compiles to a flag in the ImplFlags column of the MethodDef metadata table (ECMA-335 Partition II §22.26). The values for this attribute are listed in Partition II §23.1.11, but AggressiveInlining is undocumented (no value exists in the table for 0x0100).

During the compilation process, the compiler removes the attribute itself from the metadata. The disassembler must implement special logic to add the attribute back if the 0x0100 bit is set in the ImplFlags for a method.

Unionism answered 11/12, 2013 at 13:22 Comment(2)
ah, thanks, that is very interesting - and would explain why I needed to be using a newer disassembler since it would need logic to know about this attribute and would not just be dumping out all the attributesUnconcerned
I discovered the 'Inlining Analyzer' Extension for Visual Studio or GitHub which makes this inlining analysis a bit easier to see what the JIT will do. It can also tell you why something is not inlined.Remorseless

© 2022 - 2024 — McMap. All rights reserved.