P/Invoke - Performance and best practice (IntPtr, ref myStruct or class)
-
24 februarie 2012 10:20
I am using P/Invoke and registering callback delegates to allow a C++ DLL to call back into my C# application. The C++ EXTENSION_BLOCK is a struct conatining various data types and pointers to data etc which is allocated in the C++ side. Note: the C++ struct is passed as a pointer to it.
My query is regarding performance and best practice, which is the appropriate method to use as show below:- (1, 2 or 3) and what are the marshalling implications for each. (Note the difference between 2 - struct and 3 - class).
1) struct EXTENSION_BLOCK {...}
static int MyExtensionProc(IntPtr pEB)
EX... eb = (EX...)Marshal.PtrToStructure(pEB, typeof(EX...);
inta = eb.Something;
2) struct EXTENSION_BLOCK {...}
static int MyExtensionProc(ref EXTENSION_BLOCK pEB)
inta = pEB.Something;
3) class EXTENSION_BLOCK {...}
static int MyExtensionProc(EXTENSION_BLOCK pEB)
inta = pEB.Something;
Any help or ideas greatfully appreciated.
John.
- Mutat de Leo Liu - MSFT 27 februarie 2012 06:44 Moved for better support. (From:Visual C# General)
Toate mesajele
-
24 februarie 2012 15:44Moderator
The first 2 are similar. In both cases the marshaller is going to have to lock the data in memory, get a pointer to the data and pass it on to the native code. In the first case you're doing it by hand but the second case the marshaller does it. The second case is preferable. Performance-wise the second might be slightly faster but the overhead of transitioning from managed to native and back is going to overwhelm everything.
The third block involves copying the data from managed to native so it'll be slower depending upon what is in the struct. Note that in the third block you switched it to a class but I assume you meant struct.
Perhaps more important is the behavior that the 3 blocks exhibit. In the first case you're passing a pointer so the native code gets a pointer to the structure and can make any changes it wants. However since you did it manually you'd have to have a corresponding call to convert the pointer back to a struct in order to see the changes.
The second block does this automatically so it is cleaner code. The behavior is the same though so the second block is preferable to the first.
The third block passes the data by value so any changes made on the native side will not be seen. Hence the overhead for starting the call is higher as it has to copy the data from managed to native code (theoretically) but after the call the data does not need to be passed back.
Personally I'd worry more about the behavior than the performance. Unless you're calling this stuff a bunch of times the performance isn't going to be that bad. The goal with managed/native transitions is to minimize how often it has to occur. That is where the performance gains will be seen.
Michael Taylor - 2/24/2012
http://msmvps.com/blogs/p3net
- Propus ca răspuns de Woohoooo 24 februarie 2012 16:04
-
24 februarie 2012 17:00
Hi CoolDadTX,
Thanks for your comments. I think you have slightly misunderstood what i meant. In each case the "MyExtensionProc" method is a callback delegate which is called from a C++ DLL. So the EXTENSION_BLOCK is created and filled in the C++ DLL, then a pointer to it is passed to my C# as the 3 different parameters. This is all C++ using delegates to call into managed code.
In the 3rd example it is correctly defined as a class on the C# side, obviously it is a struct in the C++ side. So because a C# struct is a value type it has to passed to me via the 'ref' keyword, yet if i define it as a class (reference type) you dont need to use 'ref'.
Note: the only mistakes in the example are missed spaces between each "inta = ", should be "int a =" obviously. And i didnt bother with the { } after the methods. so:-
3) class EXTENSION_BLOCK {...}
static int MyExtensionProc(EXTENSION_BLOCK pEB)
{
int a = pEB.Something;
}
So, does the use of class instead of struct have any impact or problems?
Regards, john.
-
24 februarie 2012 22:56Moderator
class is ref, struct is value is strictly a C# thing. It has no application to other languages including interop to C++. Whenever you are dealing with interop you'll always want to use a struct because it has the default layout attributes that are critical for interop to work correctly. You technically could apply the attributes to a class but no one recommends it.
Since the native code is calling the managed code the rules are the same but the behavior changes slightly. For the first case you have to marshal to the structure. If your managed code modifies the data then you'll have to marshal the structure back to the pointer. It's been a while since I've done it but I vaguely remember that Marshal.Copy is needed but I can't remember.
For case 2 the runtime handles the issues for you so it is the clear winner. But under the hood it's going to end up doing the same thing so performance should be similar.
For case 3 the marshal is going to copy the data from native to managed but it won't have to copy it back. This will be faster than cases 1 and 2 but the managed code won't be able to change any values.
So it boils down to whether the managed code needs to modify the data. If not then use case 3. If so then use case 2.
Michael Taylor - 2/24/2012
http://msmvps.com/blogs/p3net
- Marcat ca răspuns de J Marchant 27 februarie 2012 14:42
-
27 februarie 2012 08:49
Hi,
Thanks for your time, that pretty much answers my questions but can you point me to where it says using class instead of a struct is not recommended? Even Microsofts msdn site for StructLayoutAttribute states it can be used with classes or structs. Also i have seen various examples and quotes on using a class instead of a struct with P/invoke etc.
John.
-
27 februarie 2012 14:40Moderator
Here's the reason why it is discouraged (it is documented somewhere but I can't remember where).
With a class there is no guarantee of the ordering of the fields at runtime. The JIT is allowed to optimize the ordering for space reasons (to eliminate pad bytes, for example). By default a class does not have a StructLayout attribute so the JIT can do its magic. A structure has the StructLayout implicitly defined. The JIT cannot reorder the fields or do any sort of optimization. By applying StructLayout to a class you are preventing the JIT from optimizing the layout. Now if the only place you use the class is in an interop situation then this might be OK but since structs already have all the features you want (and you'd never use inheritance with interop anyway) there is really no good reason to use a class in an interop case.
Michael Taylor - 2/27/2012
http://msmvps.com/blogs/p3net