Here’s some code that shows how you can directly manipulate the contents of an array using native pointers. The first sample is for a single dimensional array and the second is for a jagged array.
Natively accessing a single-dimensional array
void Test1()
{
array<int>^ arr = gcnew array<int>(3);
arr[0] = 100;
arr[1] = 200;
arr[2] = 300;
Console::WriteLine(arr[0]);
Console::WriteLine(arr[1]);
Console::WriteLine(arr[2]);
/*
Output :-
100
200
300
*/
// Modifying the array using a native int*
// that points to a pinned pointer in GC'd heap
pin_ptr<int> p1 = &arr[0];
int* p2 = p1;
while(*p2)
{
(*p2)++;
p2++;
}
Console::WriteLine(arr[0]);
Console::WriteLine(arr[1]);
Console::WriteLine(arr[2]);
/*
Output :-
101
201
301
*/
}
Natively accessing a jagged array
void Test2()
{
array<array<int>^>^ arr = gcnew array<array<int>^>(2);
arr[0] = gcnew array<int>(2);
arr[1] = gcnew array<int>(2);
arr[0][0] = 10;
arr[0][1] = 100;
arr[1][0] = 20;
arr[1][1] = 200;
Console::WriteLine(arr[0][0]);
Console::WriteLine(arr[0][1]);
Console::WriteLine(arr[1][0]);
Console::WriteLine(arr[1][1]);
/*
Output:
10
100
20
200
*/
// Copying the managed jagged array to
// a native array of pointers and accessing
// the members using the native array
pin_ptr<int> p1 = &arr[0][0];
pin_ptr<int> p2 = &arr[1][0];
int* p3[2];
p3[0] = p1;
p3[1] = p2;
Console::WriteLine(p3[0][0]);
Console::WriteLine(p3[0][1]);
Console::WriteLine(p3[1][0]);
Console::WriteLine(p3[1][1]);
/*
Output:
10
100
20
200
*/
// Assigning the native array of pointers
// to a native int** and modifying the array
int** p4 = p3;
for(int i=0; i<2; i++)
for(int j=0; j<2; j++)
p4[i][j] += 3;
Console::WriteLine(arr[0][0]);
Console::WriteLine(arr[0][1]);
Console::WriteLine(arr[1][0]);
Console::WriteLine(arr[1][1]);
/*
Output:
13
103
23
203
*/
}
Essentially we use a pinning pointer to the GC’d heap and then treat the casted native pointer as if it were pointing to a native array. Gives us a really fast method to manipulate array content!
In Test1() you do:
while(*p2) { ... }which dereferences off the end of the array, n’est-ce pas?
Ooooh! Thanks for catching that Mike. C’était une erreur
Damn! The post parser messed up my French
Gives us a really fast method to manipulate array content!
In my tests using a pin_ptr in such trivial cases decreases performance instead of increasing it.
Hello Ben
The code snippets were for demo purposes. But I am fairly sure that for a real-life scenario where you need to perform intensive array-manipulations, pointer arithmetic would be considerably faster than using the array in a managed manner.
Hello Nish.
You’re right, there will be some scenarios where this is faster but you should really measure before doing stuf like that. For example I would have thought using memset instead of looping through the array to init it to a certain value would be much faster. Turns out it’s quite irrelevant. Seems like managed arrays are pretty well optimized as they are already.
Anyways, performance wasn’t really the point of you’re blog post, I just mentioned it because I was a bit surprised when I did the measuring. A main reason for using pin_ptr is to pass managed objects to unmanaged code. When pinning large amounts of data, you should release the pin_ptr by setting it to nullptr asap to let the GC do it’s business.
Thanks for the tips, Ben. Maybe the CLR and the GC have become good enough so that trying to optimize speed by using pointers/unmanaged code etc might not have as wonderful effects as we expect.
I think the Whidbey compiler is still too much alpha-ish/beta-ish to be doing benchmarks, but I think I’ll do a few tests to compare array manipulation speeds of managed and pinning pointer access.