Managing Unmanaged Service Data

There are several places in .NET where you must leave managed code and enter the aka unmanaged code area. To name a few:

  • extern dll functions
  • COM call

There are always comments on overheads that jump from one side to the other, and my question here is if someone MEASURED the exact overheads that could occur and can explain how this can be calculated. For example, perhaps byte[] could be converted to IntPtr or even byte* to .NET and help marshaller save some processor cycles.

+8
performance unmanaged
source share
2 answers

[I see, I really did not answer the question of how you will measure; the best way to measure is simply with some tools, or with tool classes (see http://msdn.microsoft.com/en-us/library/aa645516(v=vs.71).aspx ), or even with something- then simply placing in some timers around any calls that interest you. Thus, in the roughest form, when we tried to find a successful result, for example, in our call between C # and ATL COM, we just placed timers around an empty function call, we must start the timer, start in a narrow cycle between C # and empty with the ATL COM function, do enough loops so that we can get reasonably consistent responses between starts, and then do the same in C ++. Then the difference between the two numbers is the overhead of making a call across this border.]

I actually don't have hard numbers, but I can answer from previous experience that while you use things efficiently, C # does with very little, if at all possible, overhead beyond what you would expect in C + +, depending on the exact nature of what you are trying to do.

I worked on several applications that collected very large volumes of ultrasound data using high-frequency (100 MHz-3GHz A / D-boards) and performing certain actions (for example, byte [] arrays allocated in managed code and then blocked as pointers and transferred in the form of buffers for data, transferring large amounts of data and processing them to process various parts).

Let's go back when we contacted C ++ code with VB6 and we will wrap C ++ in ATL Simple COM objects and pass pointers back and forth when needed for data and images. We practice similar methods much later with C # in VS.NET 2003. In addition, I wrote a library for the question here, which allows a massive unmanaged data store that can provide support for very large arrays and array operations, as well as even a lot of LINQ functionality -type! Using array fields instead of a massive number of objects . (note: there are some issues with link counting that were in the latest version, and I haven't tracked it yet.)

In addition, I did some interaction using ATL COM with the FFTW library to efficiently execute high-performance DSPs, although this library is not completely ready for prime time, it became the basis of the solution I created for the link above, and the spike gave me most the information I was looking to complete my much more full-blown unmanaged memory allocator and processing fast arrays that support both externally distributed and unmanaged allocations from the unmanaged heap, which ultimately Nita treatment that currently exists in the library FFTW C #.

So the thing is, in my opinion, the performance rating is very bloated, especially with the processing technology that we have these days. In fact, you can get very good performance if you try to avoid some pitfalls (for example, calling many small cross-border functions instead of passing buffers or multiple line distributions, etc.) using C #. But when it comes to high-speed processing, C # can still fit the bill for all the scenarios I mentioned. Do you sometimes need to think a little, yes. But the advantages achieved in the speed of development, maintainability and understandability, the time I spent figuring out how to get the performance I needed, was always much less than the time spent developing mostly or completely in C ++.

My two bits. (Oh, one caveat, I mention ATL COM specifically because the performance you chose when using MFC was not worth it. As far as I remember, it is two orders of magnitude slower when called through an MFC COM object compared to an ATL interface, and didn’t meet our needs. ATL, on the other hand, was only slower than calling the equivalent function directly in C ++. Sorry, I don’t remember any specific digits except this, even with large amounts of ultrasound data that we collected and moved, we did not find it narrow estom.)


Oh, I found this: http://msdn.microsoft.com/en-us/library/ms973839.aspx "Tips for Improving Performance and Tricks in .NET Applications". I found this quote very interesting:

To speed up your transition time, try using P / Invoke whenever you can. Overhead is only 31 instructions plus the cost of marshalling if data sorting is required, and only 8 otherwise. COM interop is much more expensive with 65 instructions.

Examples of section headers: "Make Chunky Calls", "Use for loops for string iteration", "Be in search of asynchronous I / O capabilities."


Some fragments from the specified fast memory library:

in MemoryArray.cs

  public MemoryArray(int parElementCount, int parElementSize_bytes) { Descriptor = new MemoryArrayDescriptor ( Marshal.AllocHGlobal(parElementCount * parElementSize_bytes), parElementSize_bytes, parElementCount ); } protected override void OnDispose() { if (Descriptor.StartPointer != IntPtr.Zero) Marshal.FreeHGlobal(Descriptor.StartPointer); base.OnDispose(); } // this really should only be used for random access to the items, if you want sequential access // use the enumerator which uses pointer math via the array descriptor TryMoveNext call. // // i haven't figured out exactly where it would go, but you could also do something like // having a member MemoryArrayItem that gets updated here rather than creating a new one each // time; that would break anything that was trying to hold on to a reference to the item because // it will no longer be immutable. // // that could be remedied by something like a call that would return a new copy of the item if it // was to be held onto. i would definitely need to see that i needed the performance boost and // that it was significant enough before i would contradict the users expectations on that one. public MemoryArrayItem this[int i] { get { return new MemoryArrayItem(this, Descriptor.GetElementPointer(i), Descriptor.ElementSize_bytes); } } // you could also do multiple dimension indexing; to do so you would have to pass in dimensions somehow in // the constructor and store them. // // there all sorts of stuff you could do with this; take various slices, etc, do switching between // last-to-first/first-to-last/custom dimension ordering, etc, but i didn't tackle that for the example. // // if you don't need to error check here then just you could always do something like: public MemoryArrayItem this[int x, int y] { get { if (myDimensions == null) throw new ArrayTypeMismatchException("attempted to index two dimensional array without calling SetDimensions()"); if (myDimensions.Length != 2) throw new ArrayTypeMismatchException("currently set dimensions do not provide a two dimensional array. [dimension: " + myDimensions.Length + "]"); int RowSize_bytes = myDimensions[0] * Descriptor.ElementSize_bytes; return new MemoryArrayItem(this, Descriptor.StartPointer + (y * RowSize_bytes) + x * Descriptor.ElementSize_bytes, Descriptor.ElementSize_bytes); } } public void SetDimensions(int[] parDimensions) { if (parDimensions.Length <= 0) throw new Exception("unable to set array to dimension of zero."); for (int i = 0; i < parDimensions.Length; ++i) if (parDimensions[i] <= 0) throw new ArgumentOutOfRangeException("unable to set dimension at index " + i.ToString() + " to " + parDimensions[i] + "."); myDimensions = new int[parDimensions.Length]; parDimensions.CopyTo(myDimensions, 0); } private int[] myDimensions = null; 

from MemoryArrayEnumerator.cs

 public class MemoryArrayEnumerator : IEnumerator<MemoryArrayItem> { // handles reference counting for the main array private AutoReference<MemoryArray> myArray; private MemoryArray Array { get { return myArray; } } private IntPtr myCurrentPosition = IntPtr.Zero; public MemoryArrayEnumerator(MemoryArray parArray) { myArray = AutoReference<MemoryArray>.CreateFromExisting(parArray); } //--------------------------------------------------------------------------------------------------------------- #region IEnumerator<MemoryArrayItem> implementation //--------------------------------------------------------------------------------------------------------------- public MemoryArrayItem Current { get { if (Array.Descriptor.CheckPointer(myCurrentPosition)) return new MemoryArrayItem(myArray, myCurrentPosition, Array.Descriptor.ElementSize_bytes); else throw new IndexOutOfRangeException("Enumerator Error: Current() was out of range"); } } public void Dispose() { myArray.Dispose(); } object System.Collections.IEnumerator.Current { get { throw new NotImplementedException(); } } public bool MoveNext() { bool RetVal = true; if (myCurrentPosition == IntPtr.Zero) myCurrentPosition = Array.Descriptor.StartPointer; else RetVal = Array.Descriptor.TryMoveNext(ref myCurrentPosition); return RetVal; } public void Reset() { myCurrentPosition = IntPtr.Zero; } //--------------------------------------------------------------------------------------------------------------- #endregion IEnumerator<MemoryArrayItem> implementation //--------------------------------------------------------------------------------------------------------------- 
+5
source share

Getting the address of a managed array is really possible.

First you need to bind the array using System.Runtime.InteropServices.GCHandle so that the garbage collector does not move the array. You must keep this descriptor if unmanaged code has access to the managed array.

 byte[] the_array = ... ; GCHandle pin = GCHandle.Alloc(the_array, GCHandleType.Pinned); 

Then you can use System.Runtime.InteropServices.Marshal.UnsafeAddrOfPinnedArrayElement to get IntPtr for any element in the array.

 IntPtr p = Marshal.UnsafeAddrOfPinnedArrayElement(the_array,0); 

Important: Linking objects seriously disrupts the GC. The ability to move objects around the heap is one of the reasons that modern GCs can (to some extent) keep up with manual memory management. By securing objects in a managed heap, the GC loses one performance advantage over manual memory management: a relatively non-irritating heap.

So, if you plan to store these arrays "on the unmanaged side" for some time, consider making a copy of the array. Copying memory is surprisingly fast. Use the Marshal.Copy(*) methods to copy from managed to unmanaged memory and vice versa.

+6
source share

All Articles