Pointers in C#-land
Posted by admin on 17th November 2006
Pointers, of course, are a way of life in C/C++. They have their aggravations, but it is virtually impossible to code anything performance-related without them. So, you get pretty used to them in those languages.
Along comes C#, with managed memory. Under most circumstances, perfectly adequate. But, performance issues are often raised when you have to do complex things on large buffers (a common situation in interactive 3D graphics) – you still have to get down to the metal to get the level of performance you need.
C# does provide for pointer manipulation, but you have to be, to paraphrase an Elmer Fudd quote: vewy, vewy caweful.
Just how careful you have to be was underscored during a code review of some work I’ve been doing. The system has been working quite well, but hangs after visiting a feature in which I’d done some significant work. More maddengly, it hangs in release, but the debug builds appear to work flawlessly.
During the code review, we discovered a utility I had been using (written by another faction in the client’s organization) returned an unprotected pointer to a pretty large buffer. We don’t know if that has been the cause of the hangs, but it is certainly suspect.
Pointer Usage in C# Review
You don’t specifically allocate a chunk of memory in C# and get a pointer to it, under any circumstances. You can create variables, you can create references to variables, but you never get a pointer directly.
Instead, to get to a pointer, you have to use one of two mechanisms:
- The ‘fix’ mechanism, whereby you assign a pointer to a variable and a block in which you can use that pointer. The construct is very much like a function call:
- Through the ‘GCHandle’ mechanism, whereby you create a ‘handle’ on the
object and get a pointer via that handle. The memory is ‘pinned’ in place -
the Garbage Collector (the ‘GC’ part of the name) will leave it alone.
fix (float *ptr = someFloatBuf)
{
/*
| You can use the pointer here, but nowhere outside of this scope.
|
| You can't modify the pointer, either - you have to use offsets
| or assign other pointers (that *are* open to modification, since
| they'll point into your fixed buffer).
|
| If you need access to another buffer, simply add another fix
| statement, like so:
*/
fix ( float *ptr2 = anotherFloatBuf)
{
/*
| We can access both 'ptr' and 'ptr2' in this block.
*/
}
}
Using System.Runtime.InteropServices ... GCHandle hFloatBuf = Alloc(floatBuf, GCHandleType.Pinned); float *ptr = (float *)hFloatBuf.AddrOfPinnedObject().ToPointer(); /** | We can use this pointer, anywhere - the only constraint is when | we're done, we need to do the following to release the block back | to the Garbage collector. */ hFloatBuf.Free();
All well and good, but there are things to consider in either approach, and
malignancies that can arise, all worth exploring in the next post.
Posted in CSharp, Programming | No Comments »
