tags:

views:

54

answers:

1

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.)

+2  A: 

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.

Hans Passant
Interesting... because I'm working in XNA where *everything* is a float! Slightly concerning...
Andrew Russell
@Hans: *"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: https://connect.microsoft.com/VisualStudio/feedback/details/536781/unexpected-jit-inlining-behavior 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: http://blogs.msdn.com/b/davidnotario/archive/2005/08/08/449092.aspx
Andrew Russell
@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?
Hans Passant
@Hans: 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.)
Andrew Russell