Return to HomePage



Code Review: Interop 1.1 Performance

Source: http://msdn.microsoft.com/library/en-us/dnpag/html/ScaleNetChapt13.asp
J.D. Meier, Srinath Vasireddy, Ashish Babbar, Rico Mariani, and Alex Mackman


Overview

Code reviews should be a regular part of your development process. Performance and scalability code reviews focus on identifying coding techniques and design choices that could lead to performance and scalability issues. The review goal is to identify potential performance and scalability issues before the code is deployed. The cost and effort of fixing performance and scalability flaws at development time is far less than fixing them later in the product deployment cycle.
Avoid performance code reviews too early in the coding phase because this can restrict your design options. Also, bear in mind that that performance decisions often involve tradeoffs. For example, it is easy to reduce maintainability and flexibility while striving to optimize code.
This chapter begins by highlighting the most significant issues that time and again result in inefficient code and suboptimal performance. The chapter then presents the review questions you need to ask while reviewing managed code. These questions apply regardless of the type of managed application you are building. Subsequent sections focus on questions specific to ASP.NET, interoperability with unmanaged code, Enterprise Services, Web services, .NET remoting, and data access. The chapter concludes by identifying a set of tools that you can use to help perform your code reviews.
How to Use This Chapter
This chapter presents the questions that you need to ask to expose potential performance and scalability issues in your managed code. To get the most out of this chapter, do the following:
* Jump to topics or read from beginning to end. The main headings in this chapter help you locate the topics that interest you. Alternatively, you can read the chapter from beginning to end to gain a thorough appreciation of the areas to focus on while performing performance-related code reviews.
* Read Chapter 3, "Design Guidelines for Application Performance." Read Chapter 3 to help ensure that you do not introduce bottlenecks at design time.
* Know your application architecture. Before you start to review code, make sure you fully understand your application's architecture and design goals. If your application does not adhere to best practices architecture and design principles for performance, it is unlikely to perform or scale satisfactorily, even with detailed code optimization. For more information, see Chapter 3, "Design Guidelines for Application Performance," and Chapter 4, "Architecture and Design Review of a .NET Application for Performance and Scalability."
* Scope your review. Identify the priority areas in your application where the review should focus. For example, if you have an online transaction processing (OLTP) database, data access is typically the key area where the most number of optimizations are probable. Similarly, if your application contains complex business logic, focus initially on the business layer. While you should focus on high impact areas, keep in mind the end-to-end flow at the application level.
* Read "Application Performance" chapters. Read the "Application Performance and Scalability" chapters found in Part III of this guide to discover technical solutions to problems raised during your code review.
* Update your coding standards. During successive code reviews, identify key characteristics that appear repeatedly and add those to your development department's coding standards. Over time, this helps raise developer awareness of the important issues and helps reduce common performance-related coding mistakes and encourage best practices during development.
* Use and evolve the accompanying checklist in the "Checklists" section of this guide. Use the "Checklist: Code Review" checklist to quickly view and evaluate the guidelines presented in this chapter. Extend and evolve the checklists to reflect your specific application.

Interop
There is a cost associated with calling unmanaged code from managed code. There is a fixed cost associated with the transition across the boundary, and a variable cost associated with parameter and return value marshaling. The fixed contribution to the cost for both COM interop and P/Invoke is small; typically less than 50 instructions. The cost of marshaling to and from managed types depends on how different the in-memory type representations are on either side of the boundary. Additionally, when you call across thread apartments, a thread switch is incurred which adds to the total cost of the call.
To locate calls to unmanaged code, scan your source files for "System.Runtime.InteropServices," which is the namespace name used when you call unmanaged code.
If your code uses interop, use the following questions when you review your code:
* Do you explicitly name the method you call when using P/Invoke?
Be explicit with the name of the function you want to call. When you use the DllImport attribute, you can set the ExactSpelling attribute to true to prevent the CLR from searching for a different function name.
* Do you use Blittable types?
When possible, use blittable types when calling unmanaged code. Blittable data types have the same representation in managed and unmanaged code and require no marshaling. The following types from the System namespace are blittable types: Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, IntPtr, and UIntPtr.
* Do you use In/Out attribute explicitly for parameters?
By default, parameters are marshaled into and out of each call. If you know that a parameter is used only in a single direction, you can use the In attribute or Out attribute to control when marshaling occurs. Combining the two is particularly useful when applied to arrays and formatted, non-blittable types. Callers see the changes a callee makes to these types only when you apply both attributes. Because these types require copying during marshaling, you can use the In attribute and Out attribute to reduce unnecessary copies.

instance string marshal( bstr) FormatNameByRef(
inout string& marshal( bstr) first,
inout string& marshal( bstr) middle,
		    [in][out] string&  marshal( bstr) last)
	

* Do you rely on the default interop marshaling?
Sometimes it is faster to perform manual marshaling by using methods available on the Marshal class, rather than relying on default interop marshaling. For example, if large arrays of strings need to be passed across an interop boundary but the unmanaged code needs only a few of those elements, you can declare the array as IntPtr and manually access only those few elements that are required.
* Do you have Unicode to ANSI conversions?
When you call functions in the Microsoft Win32® API, you should call the Unicode version of the API; for example, GetModuleNameW instead of the ANSI version GetModuleNameA. All strings in the CLR are Unicode strings. If you call a Win32 API through P/Invoke that expects an ANSI character array, every character in the string has to be narrowed.
* Do you explicitly pin short-lived objects?
Pinning short-lived objects may cause fragmentation of the managed heap. You can find places where you are explicitly pinning objects by searching for "fixed" in your source code.
You should pin only long-lived objects and where you are sure of the buffer size; for example, those used to perform repeated I/O calls. You can reuse this type of buffer for I/O throughout the lifetime of your application. By allocating and initializing these buffers when your application starts up, you help ensure that they are promoted faster to generation 2. In generation 2, the overhead of heap fragmentation is largely eliminated.
* How do you release COM objects?
Consider calling Marshal.ReleaseComObject in a finally block to ensure that COM objects referenced through a runtime callable wrapper (RCW) release properly even if an exception occurs.
When you reference a COM object from ASP.NET, you actually maintain a reference to an RCW. It is not enough to simply assign a value of null to the reference that holds the RCW, and instead you should call Marshal.ReleaseComObject. This is of most relevance to server applications because under heavy load scenarios, garbage collection (and finalization) might not occur soon enough and performance might suffer due to a build up of objects awaiting collection.
You do not need to call ReleaseComObject from WinForm applications that use a modest number of COM objects that are passed freely in managed code. The garbage collector can efficiently manage the garbage collection for these infrequent allocations.
A common pitfall when releasing COM objects is to set the object to null and call GC.Collect followed by GC.WaitForPendingFinalizers. You should not do this because the finalization thread takes precedence over the application threads to run the garbage collection. This can significantly reduce application performance.
* Do you use the /unsafe switch when creating interop assemblies?
By default, RCWs perform run-time security checks that cause the stack to be walked to ensure that the calling code has the proper permissions to execute the code. You can create run-time callable wrappers that perform reduced run-time security checks by running the Tlbimp.exe file with the /unsafe option. This should be used only after careful code reviews of such APIs to ensure that it is not subjected to luring attack.
For more information see "Use SuppressUnmanagedCodeSecurity with Caution" in Chapter 8, "Code Access Security in Practice" of Improving Web Application Security: Threats and Countermeasures on MSDN at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/ThreatCounter.asp.

C:\>tlbimp mycomponent.dll /out:UnSafe_MyComponent.dll /unsafe

More Information
For more information about the issues raised in this section, see Chapter 7, "Improving Interop Performance."
Summary
Performance and scalability code reviews are similar to regular code reviews or inspections, except that the focus is on the identification of coding flaws that can lead to reduced performance and scalability.
This chapter has shown how to review managed code for top performance and scalability issues. It has also shown you how to identify other more subtle flaws that can lead to performance and scalability issues.
Performance and scalability code reviews are not a panacea. However, they can be very effective and should be a regular milestone in the development life cycle.
Additional Resource
For more information, see the following resources:
* Chapter 4 "Architecture and Design Review of a .NET Application for Performance and Scalability."
* Chapter 6, "Improving ASP.NET Performance."
* Chapter 7, "Improving Interop Performance."
* Chapter 8, "Improving Enterprise Services Performance."
* Chapter 9, "Improving XML Performance."
* Chapter 10, "Improving Web Services Performance."
* Chapter 11, "Improving Remoting Performance."
* Chapter 12, "Improving ADO.NET Performance."
For printable checklists, see the following checklists in the "Checklists” section of this guide:
* "Checklist: ASP.NET Performance."
* "Checklist: Managed Code Performance."
* "Checklist: Enterprise Services Performance."
* "Checklist: Interop Performance."
* "Checklist: Remoting Performance."
* "Checklist: Web Services Performance."
* "Checklist: XML Performance."
For further reading, see the following resource:
* For more information about designing for performance, see "Performance" on MSDN at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsent7/html/vxconPerformance.asp?frame=true.



Return to HomePage
Microsoft Communities