8

Possible Duplicate:
C# parameters by reference and .net garbage collection

I was thinking of using ref arguments to limit the bounds checking of an array. For example the code of swapping two elements is :

class Test {
  int[] array;

  private void qSort() {
    ...blah...
    int temp = array[a];
    array[a] = array[b];
    array[b] = temp;
  }
}

which has 4 access to the array the alternative will be :

class Test {
  int[] array;

  private void qSort() {
  ...blah...
    Swap( ref array[a], ref array[b] );
  }

  static void Swap(ref int a,ref int b) {
    int temp = a;
    a=b;
    GC.Collect();  // suppose this happens
    b=temp;
  }
}

which theoretically has only 2 access to the array

What confusing me is that I don't know what exactly happens when I pass an array element by ref. If Garbage Collector kicks in, while executing code in the Swap function, will be able to move the array ? or the array is pinned for the duration of the call ?

Mind that the above code is a simple test case. I want to use it in much more complex scenarios

Edit: As BrokenGlass pointed out, this is answered by Eric Lippert here C# parameters by reference and .net garbage collection

The array will not be pinned and GCollector can move it and will update accordinly any ref to an element of it, that resides on the stack

Community
  • 1
  • 1
Panos Theof
  • 1,450
  • 1
  • 21
  • 27
  • 2
    Don't try to optimize prematurely, *especially* if you don't have deep knowledge of how the machinery works. Case in point: your second version (with `ref`) will actually be *much* slower than the first due to boxing (try it in a loop and see). – Jon Oct 18 '11 at 12:35
  • 4
    @Jon, I agree there is probably no point in doing this for performance reasons, but it does improve readability... And BTW, there is no boxing in that case – Thomas Levesque Oct 18 '11 at 12:37
  • Panos, added a GC.Collect() to emphasise the point, roll back if you don't like it. – H H Oct 18 '11 at 12:44
  • 3
    Above SO thread has an answer by Eric Lippert that should clarify the compacting/moving issue – BrokenGlass Oct 18 '11 at 12:57
  • @BrokenGlass, I ask this, for performance reasons. Pinning an array will of course have a penalty on the performance in the long run. Eric Lippert says that it doesn't happen, but leaves a question as whether managed references hide some kind of indirection or they have the same performance as unmanaged references (pointers). Anyway I consider the original question as answered/duplicate – Panos Theof Oct 18 '11 at 13:34
  • I don't believe I said that **making a managed reference to an array allows it to be moved**. Rather, I said the converse: that **making an unmanaged pointer to an array requires it to be pinned**. – Eric Lippert Oct 18 '11 at 15:13
  • 2
    @Jon: Where is the boxing? Boxing happens when a value type is converted to a reference type, but there is no reference type here. – Eric Lippert Oct 18 '11 at 15:14
  • @Eric: You quote "does the garbage collector update even references created by passing reference parameters?" with this answer: "Yep. It would be rather fragile if it didn't." The only reason I could think that GCollector will update ref parameters on the stack is when the array is moved. – Panos Theof Oct 18 '11 at 15:37
  • 1
    @PanosTheof: Suppose I told you that *if it rains, then the streets are wet*. That does not logically imply that it is raining! *If* the garbage collector moves a block of memory, *then* some mechanism exists so that outstanding managed references continue to be valid. Whether the garbage collector actually *does* move blocks of memory in these circumstances, and if it does, what *mechanism* it uses to keep managed references safe, I have no idea. I'm not an expert on the garbage collector. (Or, properly, collectors -- there are many implementations.) – Eric Lippert Oct 18 '11 at 15:53
  • I think the relevant quote from the linked question is "You're thinking about this like an unmanaged C++ programmer -- C++ makes you do this work, but C# does not. Remember, the whole point of the managed memory model is to free you from having to think about this stuff." – dlev Oct 18 '11 at 15:57
  • *which theoretically has only 2 access to the array* -- no, it still has 4. How can you imagine otherwise? And consider the more straightforward `int tmpa = a, tmpb = b; a = tmpb; b = tmpa;` -- that still has 4. It should be obvious that you can't have less than 4 ... 2 reads and 2 writes. – Jim Balter Nov 14 '18 at 23:09
  • @JimBalter: to the array, not to the array memory contents, which means that it does 4 bound checks if indexes "a" and "b" are valid vs 2 bound checks on the "ref" case – Panos Theof Nov 16 '18 at 17:22
  • It would help if you said what you mean ... "access" is not at all the same as "bound check". As for bound checks, the compiler would optimize it down to 2 in your first code snippet. – Jim Balter Nov 16 '18 at 22:00
  • I think you are correct. The current release of JIT of .NET Core it would optimize it, but I doubt that the standard .NET framework JIT would. Anyway when I post this, 7 years ago, the JIT will not optimize it. Since now we have Span&friends consider the question, the examples and the answers here, *very* outdated – Panos Theof Nov 20 '18 at 11:04

2 Answers2

1

The Swap function still access the array 3 or 4 times, the Swap function does not offer any performance adavantage over the simpler code. It might be useful if it is reused.

static void Swap(ref int a, ref int b) 
{     
    int temp = a;  //<-- Here, a is in the array
    a=b;           //<-- a and b are in the array
    b=temp;        //<-- b is in the array
}

The garbage collector will not release memory you have a reference to, as happens when you pass by reference.

Jodrell
  • 34,946
  • 5
  • 87
  • 124
0

The stack could look like this:

  • qSort() has a reference to the array
  • Swap()

So if GC.Collect() executes in swap there is still a reference to the array in qSort() which means it won't be collected.

Sjoerd
  • 620
  • 5
  • 10