C# Futures: Managed Pointers
A big emphasis for many developers, especially those writing games or working on pure number crunching, is raw performance. And for them, nothing is more problematic that memory allocation. While allocation itself is cheap, too many allocations add to memory pressure and causes more frequent garbage collection cycles.
Heap allocated memory can also cause problems for the cache. If you have a list or array of reference types, the actual data is stored separately from the array. This means you may have to waste separate cache lines for the array and the objects referenced by the array. And if those objects we’re created at the same time, they may be scattered far enough that even more cache lines are needed. This scattering of related data is known as poor locality.
Using value types (structs in C# parlance) can dramatically reduce the number of allocations and improve locality. However, there are limitations to what you can reasonably do with structures. Because they are designed to be copy-on-assignment, you have to keep them small or risk a serious performance penalty that negates the reason for using them in the first place.
One way to reduce unnecessary copying is by passing value types to functions using a managed pointer. Currently, the only way to create a managed pointer in C# is by using a ‘ref’ keyword as part of a parameter. While this addresses some performance scenarios, the CLR is capable of doing a lot more with managed pointers.
Under the Ref Returns and Locals proposal, two more options are opened up to C# programmers.
Assuming that a is a local variable of type int, the proposal would allow you to create a Ref Local with this syntax:
ref int x = a;
Like a ref parameter, the ref local effectively becomes an alias for the indicated local variable, eliminating the need to make a copy. You can also use it to get a pointer to an array element or a field in another object.
ref int y = b;
ref int z = c.d;
In CLR terms, a ref local is called a “TypedReference”. A TypedReference contains both the pointer to a location and information on what type of data may be stored at the location.
As a rule, a TypedReference is always a parameter or local variable. This is necessary because the CLR does not allow items on the heap to point to the interior of other items on the heap. Nor may you return a TypedReference, as that would make it possible to return a reference to a local value that would of course no longer exist once the function exits.
The second part of the proposal would allow you to return references from a function. This would allow for scenarios such as:
public static ref TValue Choose<TValue>(
Func<bool> condition, ref TValue left, ref TValue right)
return condition() ? ref left : ref right;
Matrix3D left = […], right = […];
Choose(chooser, ref left, ref right).M20 = 1.0;
With this new syntax, there are no copies made to the struct anywhere in the sample. Instead, it is always creating and passing managed pointers around.
Unlike ref local, implementing this feature may require making an alteration to the CLR standard. As mentioned before, returning a TypedReference is normally not allowed. Technically speaking you can do it, but it is considered to be not type-safe and thus “unverifiable”. Using unverified code is not allowed in restricted security settings as it introduces the risk for serious bugs that are normally seen only in C/C++.
To mitigate this risk, part of the proposal says you can only return a reference to something on the heap or an already existing ref/out parameter. Or in other words, the compiler would verify that you cannot possibly return a reference to local variable.