As every .NET developer knows memory usage is managed from Garbage Collector. This layer determines when memory is released and how to reorganize it. It allocates spaces for each thread separately and avoid conflicts.
For this, we programmers often don’t know exactly what really happen at this level, the details.
In general, this is enough, because GC has been built in order to permit developer to concentrate at higher levels.
But sometimes, especially when you pass pointers to memory block, like array to API functions or specifically to MKL API functions, it is important to know what happen under the scene.
IntPtr x = new IntPtr(0); double x_init = null; x = mkl_malloc(sizeof(double) * n, 64); Marshal.Copy(x_init, 0, x, n); //use x pointer … mkl_free(ref x);
This set of instructions define a memory space for an array of double and assign a memory pointer to x. This pointer x is then passed to a function like this:
[DllImport("mkl_rt.dll", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, SetLastError = false)] internal static extern int dtrnlspbc_init( ref IntPtr handle, ref int n, ref int m, IntPtr x, IntPtr LW, IntPtr UP, double eps, ref int iter1, ref int iter2, ref double rs );
Everything seems to work, but this is a very subtle felling!
Yes, because memory is in the heap area managed by GC and can be moved, reorganized. This means that your pointer x is not reliable.
We spend a lot of time to fight against strange results, sometimes good, sometimes not. Where was the trick? Was Intel fault of our fault? I don’t like this word “fault”, but the question was where the issue?
At the end, we got the solution!
This was: Pin the pointer with the correct syntax and methods.
GCHandle x_handle = GCHandle.Alloc(x_init, GCHandleType.Pinned); x = x_handle.AddrOfPinnedObject(); //use x pointer … x_handle.Free();
Now GC can’t move the pointer and the array used by API function is always located.
I hope these notes useful for poor developer always alone in the ocean of troubles.