Why doesn’t C# have an ‘inline’ keyword?


I got this question in email today, and I thought I’d share my response.


For those of you who don’t know, the ‘inline’ keyword in the C language was designed to control whether the compiler inlined a function into the calling function. For example, if I had something like


int square(int x)
{
    return x * x;
}


and used it here:


int y = square(x);


I would be making a function call for a single expression, which would be pretty inefficient. Adding “inline’ to the definition would tell the compiler to inline it, effectively changing my use to:


int y = x * x;


and giving me the abstract of a function call without the overhead.


Inline had some good uses in early C compilers, but it was also prone to misuse – some programmers would decorate all their functions with ‘inline’, which would give them no function calls, very big code, and therefore slow execution.


That led to the next phase, where programmers would only put ‘inline’ on functions where they needed the performance. That kept the code smaller, but missed cases like the one I have above where the inlined code is smaller than the function call code, where you want to inline every time. These cases only got caught if the programmer noticed them.


Finally, as systems got better, compilers got into the act, and started doing enough analysis to make good decisions on their own without ‘inline’ keyword. In fact, the compiler writers discovered that the heuristics that they put in the compiler made better choices than programmers did, and some compilers started ignoring ‘inline’ and just doing what they thought best. This was legal because inline was always defined as a hint, not a requirement.


This worked really well for most cases, but there were some cases where the programmer really wanted something to be inline (ie override the heuristics). This was sometimes around as a request for the “inlinedammit!“ keyword, which showed up in VC++ in V6.0 (IIRC) as the nicer named “__forceinline“.


So how does that relate to C#? Well, it doesn’t, but it’s an interesting story so I wanted to share it with you.


For C#, inlining happens at the JIT level, and the JIT generally makes a decent decision. Rather than provide the ability to override that, I think the real long-term solution is to use profile-guided feedback so that the JIT can be smarter about what it inlines and what it doesn’t.


 

Comments (21)

  1. Joeseph says:

    Here’s a related question.

    When you create a property in .NET, we create it something like this.

    private int _num;

    public int TheNumber

    {

    get { return this._num; }

    set { this._num = value; }

    }

    The compiler will create the appropriate get_ and set_ accessor functions.

    My question is this.

    If you are creating, say… a control, and using your properties internally, is it

    more effecient to use the private member variable, or to call the property explicitly? Basically, should

    you do ….

    int temp = this.TheNumber

    or….

    int temp = this._num;

    This is assuming that the getter function is just returning the value of the private member. If additional logic was needed, then of course you would call the accessor. But if you’re just returning a simple value, I would think you could skip the overhead of calling the function. I doubt it would save much on 1 or 2 calls, but I’ve seen code similar to this many times.

    for ( int i = 0; i != 1000; i++ )

    {

    if ( this.TheNumber > 8 )

    // do something

    }

    In the above example, there would be a 1000 calls to the accessor function, which seems less effecient than 1000 calls to just check the value of a variable.

    Encapsulation wise, it is far better to call the property directly, but is it better performance wise to call the private member directly?

    I know there is no simple answer, but I’m curious as to whether or not the compiler helps out here.

  2. Jason Salas says:

    My desires are a bit more simplistic – I’ve just always wished C# would have something similar to VB.NET’s "With" keyword. 🙂

  3. W Poust says:

    Several years ago our software group had the same discussion about inline functions. The senior developers made the same point that the compiler does a better job at optimizing. Actually between the compiler and the CPU instruction pipeline optimizations, its a non-trivial issue to understand. We told the gungho performance geeks that we wouldn’t accept inline but if they were serious they could write asm blocks. Funny how none of the performance people took us up on the offer.

  4. What would happen in VC++ with:

    __forceinline void Factorial(int x)

    {

    if (x == 1)

    return 1;

    else

    return x * Factorial(x-1); }

    }

    ??? 😉

  5. Eric says:

    Scott,

    Human sacrifices, dogs and cats living together! Mass hysteria!

  6. James Curran says:

    Scott,

    VC++ has an internal inline recursion limit (what? you think you were the first to think of trying that?). It default’s to 8 (I think), but can be set higher with a #pragma.

    Basically, the following code:

    int a = Factorial(3);

    int b = Factorial(7);

    int c = Factorial(10);

    can conpiled as if written:

    int a = 6;

    int b = 5040;

    int c = 1814400 * Factorial(2);

    And even in the out-of-line Factorial it has to create, it tries to inline some of the recursion. It’s really fascinating to see the assembly code generated by that.

  7. Adnan Masood says:

    I appreciate Eric’s memory with GhostBusters.

    In reference to Microsoft Specific VC++ compiler, Scott’s code would not be inlined since "its recursive and not accompanied by #pragma inline_recursion(on)"

    C++ Language Reference

    inline, __inline, __forceinline

    http://msdn.microsoft.com/library/en-us/vclang/html/_pluslang_inline_specifier.asp?frame=true

    Even with __forceinline, the compiler cannot inline code in all circumstances. The compiler cannot inline a function if:

    The function or its caller is compiled with /Ob0 (the default option for debug builds).

    The function and the caller use different types of exception handling (C++ exception handling in one, structured exception handling in the other).

    The function has a variable argument list.

    The function uses inline assembly, unless compiled with /Og, /Ox, /O1, or /O2.

    The function returns an unwindable object by value, when compiled with /GX, /EHs, or /EHa.

    The function receives an unwindable copy-constructed object passed by value, when compiled with /GX, /EHs,, or /EHa.

    The function is recursive and not accompanied by #pragma inline_recursion(on). With the pragma, recursive functions can be inlined to a default depth of eight calls. To change the inlining depth, use inline_depth pragma.

    The function is virtual and is called virtually. Direct calls to virtual functions can be inlined.

    The program takes the address of the function and the call is made via the pointer to the function. Direct calls to functions that have had their address taken can be inlined.

    The function is also marked with the naked __declspec modifier.

    If the compiler cannot inline a function declared with __forceinline, it generates a level 1 warning (4714).

  8. moo says:

    JIT Inlining, is this set in stone on the first JIT or can it rejit if it realises its better inlined based on usage during runtime?

    If not would this be implemented in future? Obviously you dont want it to spend alot of time reJITting a method to inline or not inline based on profiling data if its oscilating about.

  9. moo says:

    The world MIGHT end and planets MAY collide.

    # re: Why doesn’t C# have an ‘inline’ keyword? 1/29/2004 9:24 PM Scott Mitchell

    What would happen in VC++ with:

    __forceinline void Factorial(int x)

    {

    if (x == 1)

    return 1;

    else

    return x * Factorial(x-1); }

    }

    ??? 😉

  10. Adnan Masood says:

    Also, the code was faulty since factorial is defined for n >= 0 and for 0!=1 by definition. (could be calculated too.)

    n! = n*(n-1)! for n > 1

    Therefore it should have been…

    __forceinline void Factorial(int x)

    {

    if (x < 1)

    return 1;

    else

    return x * Factorial(x-1); }

    }

  11. John Schultz says:

    Adnan, the original Factorial() author’s real question was "What happens with __forceinline and recursive fcns?", not "Does this code implement factorial right?"

    Since you needlessly picked on someone else’s pseudo code snippet, I’ll pick on yours:

    Your fix is only partial — you still quietly give an incorrect answer for negatives, you don’t detect overflow and you return void!

    Of course the best implementation of Factorial, as you noted, is to just have an array of the first 20 pre-computed, 64 bit answers and use the argument as the offset.

  12. MBA says:

    Helpful For MBA Fans.

  13. Channel 9 says:

    .Net inlines everything it can