Will the .NET JIT inline a small function that calls another small function?
Asked Answered
S

1

6

I would like to know - will the .NET JITter recursively inline small functions called from other small functions?

Just for example:

public static float Square(float value)
{
    return value * value;
}

public static float Cube(float value)
{
    return Square(value) * value;
}

If I call Cube from somewhere, will it inline all the way, or will I end up with a function call to Square?

And, if so, how deep will it recurse to do the inlining? (Say I were crazy enough to implement a Quartic or Quintic function in the same way.)

Scrutiny answered 31/7, 2010 at 8:56 Comment(0)
B
10

Unfortunately you picked a bad example. The x86 JIT compiler doesn't inline methods that return float. Not 100% sure why, I think it does to avoid consistently problems when the float is converted to an 80-bit floating point value in the FPU. Internal precision is 80-bits but those extra bits are sliced off when the 80-bit value is truncated back to a 32-bit value when it is flushed back to memory. Keeping the value in the FPU too long prevents this truncation from happening and changes the calculation result.

If you replace float by double and compile this code:

static void Main(string[] args) {
    Console.WriteLine(Cube(2.0));
}

Then this machine code is generated when the JIT optimizer is enabled:

00000000  push        ebp                             ; setup stack frame
00000001  mov         ebp,esp 
00000003  call        6DA2BEF0                        ; Console.get_Out() 
00000008  fld         qword ptr ds:[010914B0h]        ; ST0 = 8.0
0000000e  sub         esp,8                           ; setup argument for WriteLine
00000011  fstp        qword ptr [esp] 
00000014  mov         ecx,eax                         ; call Console.Out.WriteLine
00000016  mov         eax,dword ptr [ecx] 
00000018  call        dword ptr [eax+000000D0h] 
0000001e  pop         ebp                             ; done
0000001f  ret

Not only did it inline the functions, it was able to evaluate the expressions at compile time. And directly passes the result by calling Console.WriteLine(8.0). Pretty good huh?

Use double, not float.

Burp answered 31/7, 2010 at 15:12 Comment(5)
Interesting... because I'm working in XNA where everything is a float! Slightly concerning...Scrutiny
"The x86 JIT compiler doesn't inline methods that return float." - Are you absolutely sure about this? I have searched high and low and cannot find any references to back this up. The best I found was this page on Connect: connect.microsoft.com/VisualStudio/feedback/details/536781/… which seems to imply that functions returning float do, in fact, inline. And this (old) article implies that the coercion rules (so maybe different in practice) are the same for floats and doubles: blogs.msdn.com/b/davidnotario/archive/2005/08/08/449092.aspxScrutiny
@Andrew: inlining rules are not documented, only hinted at in some blog posts. Important, because they need to be able to change this to improve the jitter without breaking assumptions made from documented behavior. I can only document what I see my x86 jitter do. And it definitely does not inline the float version of these methods. Do you see different behavior?Burp
I'm afraid I don't have Visual Studio, so I can't easily check the JIT output :( otherwise I'd try it myself. But the Connect entry I linked does contradict your conclusion about floats - although as you rightly say, this is quite a "black magic" area. My immediate thought is that the JIT's heuristic estimate of the length of the float-returning function is longer than its estimate for the version that returns double (perhaps due to truncating), and that increasing the length cutoff by calling it within a loop might give a different result. (But maybe you've already checked that.)Scrutiny
Oh wow I questioned Hans Passant. Nice try, past-Andrew, but he's right it simply doesn't inline methods returning float ;) To answer my original question: it is capable of inlining pretty deeply. I still don't have a handle on exactly how deep it will go (although constant-folding seems to give up sooner than inlining). But in cases where you care about this - you really need to check the actual JIT output anyway (which means having something better than VS Express).Scrutiny

© 2022 - 2024 — McMap. All rights reserved.