views:

499

answers:

5

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?

A: 

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.

Mike
I disagree that stack or making a side effect is the reason. I think that's mostly because of parameter size variance issues that make it hard to inline those stuff. It's completely possible to set a reference type inside a method to another value: `void x(string s) { s = "hello"; }` That's not much different from a struct version.
Mehrdad Afshari
I disagree as well, see the links in my post for a less speculative view.
Ryan Emerle
A: 

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.

JaredPar
@JaredPar: I'm not sure about what I'm going to say... but I think `struct` here has a narrow meaning. Primitive types such as `int` are treated differently by the runtime. Ain't it?
Mehrdad Afshari
@Mehrdad for the JITer, I'm unsure as to how it will treat a normal struct vs. a primitive. My guess is it will inline certain operations on primitives more aggresively but I can't find any documentation to back that up at the moment.
JaredPar
A: 

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.

Charles Bretana
+2  A: 

Here's a better article describing why some methods will not be inlined. And here is a MS connect feedback entry with comments that include benchmark results (FWIW)

Ryan Emerle
+2  A: 

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.

Mehrdad Afshari