Why won't C# inline functions with struct parameters?
-
20-08-2019 - |
Question
At http://blogs.msdn.com/ericgu/archive/2004/01/29/64717.aspx, we learn that C# will not inline methods with structs as formal parameters. Is this due to potential dependence on the stack, e.g. for recursion? If so, could I potentially benefit by turning struct parameters into ref parameters like this?
public int Sum(int i)
{
return array1[i] + array2[i];
}
turns into:
public int Sum(ref int i)
{
return array1[i] + array2[i];
}
Edit: I went to attempt a test, but I can't get anything in inline. Here is what I tried:
class Program
{
private static string result;
static void Main(string[] args)
{
Console.WriteLine(MethodBase.GetCurrentMethod().Name);
Console.WriteLine();
m1();
Console.WriteLine(result);
}
private static void m1()
{
result = MethodBase.GetCurrentMethod().Name;
}
}
It prints "m1" as the second line, which indicates that it did not get inlined. I built a Release build and ran it with Ctrl-F5 (to not attach the debugger). Any ideas?
Solution
As Jon said, it's a very old post. I can confirm that in the following code:
using System;
using System.Runtime.CompilerServices;
struct MyStruct
{
public MyStruct(int p)
{
X = p;
}
public int X;
// prevents optimization of the whole thing to a constant.
[MethodImpl(MethodImplOptions.NoInlining)]
static int GetSomeNumber()
{
return new Random().Next();
}
static void Main(string[] args)
{
MyStruct x = new MyStruct(GetSomeNumber());
// the following line is to prevent further optimization:
for (int i = inlinetest(x); i != 100 ; i /= 2) ;
}
static int inlinetest(MyStruct x)
{
return x.X + 1;
}
}
inlinetest
method is inlined.
Main method disassembly:
; set up the stack frame:
00000000 push ebp
00000001 mov ebp,esp
; calls GetSomeNumber:
00000003 call dword ptr ds:[005132D8h]
; inlined function:
00000009 inc eax
; the dummy for loop:
0000000a cmp eax,64h
0000000d je 0000001B
0000000f sar eax,1
00000011 jns 00000016
00000013 adc eax,0
00000016 cmp eax,64h
00000019 jne 0000000F
0000001b pop ebp
0000001c ret
I've tested this on x86 .NET Framework 3.5 SP1 on Windows 7 x64 RC.
As I believed there's nothing inherently wrong with inlining methods with struct
parameters. Probably, JIT has not been smart enough at that time.
OTHER TIPS
A bit of clarification. That blog is not discussing what C# will or won't inline. It is discussing what the JITer will or won't inline.
As to why the JITer does not inline methods with structs as formal parameters, I do not know.
However I am relatively certain that if they won't inline a method with a struct parameter, then making the struct by ref won't change that decision.
My first guess is because this would involve changing the size of the stack frame created for that method. With a reference type, (a class) the stack frame has to allow for the storage of a pointer (to the object on the Heap) whereas with a struct ALL the structs primitive fields must be added to the stackframes footprint.
This is a value-type versus reference-type issue, as you noted. Imagine that you have a function that has a value-type (say, int) argument, and you change that variable in the function. Obviously, this has no side effects in the calling function, as you passed by value and not by reference.
Now imagine that you inline the same code. All of a sudden, your variable is changing in the calling function! Not what you intended.
You can probably convince yourself by thinking through the same scenario with a reference type that inlining has no problems for by-ref params - a change in the original function has the same repurcussions as a change in an inlined version of that function.
That's WHY it doesn't allow it, but I agree that there should be some way for you to tell the compiler that you "guarantee" that your function has no side effects on value-type arguments. Might be nice for performance reasons in a lot of cases.
Incidentally, you can always declare a module level int and bypass the argument all together :) Much uglier, but you would end up inlining the function in question.
Whatever you decide to do, good luck!
EDIT: I just remembered that there was an ancient C compiler on Irix (the SGI operating system) that actually had a compiler option for this, allowing you to "force" inlining. So it can be done, but I agree with the choice made here, opting for the less error-prone default.