PInvoke perf guidelines


I just saw these go by on an internal alias and thought
i’d share them with you…


style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: Tahoma">Recommendation: If you
control the interface between managed and unmanaged code, make it “chunky”
rather than “chatty,” to reduce the total number of transitions
made.


style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: Tahoma">Chatty interfaces are
interfaces that make a lot of transitions without doing any significant work on
the other side.  For example, property setters and getters are
chatty.  Chunky interfaces are interfaces that make only a few transitions
and work done on the other side is significant.  For example, method that
opens a database connection and retrieves some data is chunky.  Chunky
interfaces involve fewer interop transitions, so you eliminate
overhead.


style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: Tahoma"> 


style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: Tahoma">Recommendation: Avoid
Unicode/ANSI conversions if possible.


style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: Tahoma">Converting strings
from Unicode to ANSI and vice versa is an expensive operation.  For
example, if some strings need to be passed but their content is not important
you can declare parameter as IntPtr and interop marhaler won’t do any
conversions.


style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: Tahoma"> 


style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: Tahoma">Recommendation: For
high-performance scenarios, declaring parameters and fields as IntPtr can boost
performance (at the expense of ease-of-use and
maintainability).


style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: Tahoma">Sometimes it is faster
to do manual marshaling using methods available on the Marshal class then rely
on default interop marshaling.  For example, if large arrays of strings
need to be passed across interop boundary but only few elements will be needed,
declaring array as IntPtr and accessing only those few elements manually will be
much faster.


style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: Tahoma"> 


style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: Tahoma">Recommendation: Using
InAttribute and OutAttribute wisely can reduce unnecessary
marshaling.


style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: Tahoma">Interop marhaler will
use default rules when deciding if some parameter needs to be marshaled in
before the call and out after the call.  These rules are based on level of
indirection and type of parameter. Some of these operations might not be
necessary depending on the method’s semantics.


style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: Tahoma"> 


style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: Tahoma">Recommendation: Use
SetLastError=false on PInvoke signatures if you’re not going to call
Marshal.GetLastWin32Error afterward.


style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: Tahoma">Setting
SetLastError=true on PInvoke signatures will require some additional work from
interop layer to preserve last error code.  Use this only when you rely on
this information and will use it after the call is made.


style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: Tahoma"> 


style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: Tahoma">Recommendation: If
(and only if) unmanaged calls are exposed in a non-exploitable fashion, use
SuppressUnmanagedCodeSecurityAttribute to reduce number of security
checks.


style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: Tahoma">Security checks are
very important.  They will make sure that nobody can misuse your API. 
Sometimes your API doesn’t expose any protected resources/sensitive information
or they are well protected. In those situations extensive security checks might
introduce unnecessary overhead.


 

Comments (4)