Tuesday, June 30, 2009
Check List for Improving the Performance of .NET Applications
1. Resource Cleanup
Failing to clean up resources is a common cause of performance and scalability
bottlenecks. Review your code to make sure all resources are closed and released as
soon as possible. This is particularly important for shared and limited resources such as
connections. Make sure your code calls Dispose (or Close) on disposable resources.
Make sure your code uses finally blocks or using statements to ensure resources are
closed even in the event of an exception.
While structured exception handling is encouraged because it leads to more robust
code and code that is less complex to maintain than code that uses method return
codes to handle error conditions, exceptions can be expensive. Make sure you do not
use exception handling to control regular application flow. Use it only for exceptional
conditions. Avoid exception handling inside loops — surround the loop with a
try/catch block instead if that is required. Also identify code that swallows exceptions
or inefficient code that catches, wraps, and rethrows exceptions for no valid reason.
3. String Management
Excessive string concatenation results in many unnecessary allocations, creating extra
work for the garbage collector. Use StringBuilder for complex string manipulations and
when you need to concatenate strings multiple times. If you know the number of
appends and concatenate strings in a single statement or operation, prefer the +
operator. In ASP.NET applications, consider emitting HTML output by using multiple
Response.Write calls instead of using a StringBuilder.
Server-side code should generally use the common language runtime (CLR) thread
pool and should not create threads on a per-request basis. Review your code to ensure
that appropriate use is made of the thread pool and that the appropriate
synchronization primitives are used. Make sure your code does not lock whole classes
or whole methods when locking a few lines of code might be appropriate. Also make
sure your code does not terminate or pause threads by using Thread.Abort or
Boxing causes a heap allocation and a memory copy operation. Review your code to
identify areas where implicit boxing occurs. Pay particular attention to code inside
loops where the boxing overhead quickly adds up. Avoid passing value types in
method parameters that expect a reference type. Sometimes this is unavoidable. In
Check List for Improving the Performance of .NET Applications
this case, to reduce the boxing overhead, box your variable once and keep an object
reference to the boxed copy as long as needed, and then unbox it when you need a
value type again. Excessive boxing often occurs where you use collections that store
System.Object types. Consider using an array or a custom-typed collection class
instead. To identify locations that might have boxing overhead, you can search your
assembly’s Microsoft intermediate language (MSIL) code for the box and unbox
instructions, using the following command line.
Ildasm.exe yourcomponent.dll /text findstr box
Ildasm.exe yourcomponent.dll /text findstr unbox
To measure the overhead, use a profiler.
Checklist for Managed Code Performance
1. Design Considerations
1.1. Design for efficient resource management.
1.2. Reduce boundary crossings.
1.3. Prefer single large assemblies rather than multiple smaller assemblies.
1.4. Factor code by logical layers.
1.5. Treat threads as a shared resource.
1.6. Design for efficient exception management.
2. Class Design Considerations
2.1. Do not make classes thread safe by default.
2.2. Consider using the sealed keyword.
2.3. Consider the tradeoffs of using virtual members.
2.4. Consider using overloaded methods.
2.5. Consider overriding the Equals method for value types.
2.6. Know the cost of accessing a property.
2.7. Consider private versus public member variables.
2.8. Limit the use of volatile fields.
3. Garbage Collection Guidelines
3.1. Identify and analyze your application’s allocation profile.
3.2. Avoid calling GC.Collect.
3.3. Consider weak references with cached data.
3.4. Prevent the promotion of short-lived objects.
3.5. Set unneeded member variables to Null before making long-running calls.
3.6. Minimize hidden allocations.
3.7. Avoid or minimize complex object graphs.
3.8. Avoid preallocating and chunking memory.
4. Finalize and Dispose
4.1. Call Close or Dispose on objects that support it.
4.2. Use the using statement in Microsoft® C# and Try/Finally blocks in Microsoft
4.3. Basic®.NET to ensure Dispose is called.
4.4. Do not implement Finalize unless required.
4.5. Implement Finalize only if you hold unmanaged resources across client calls.
4.6. Move the finalization burden to the leaves of object graphs.
4.7. If you implement Finalize, implement IDisposable.
4.8. If you implement Finalize and Dispose, use the Dispose pattern.
4.9. Suppress finalization in your Dispose method.
4.10. Allow Dispose to be called multiple times.
4.11. Call Dispose on base classes and on IDisposable members.
4.12. Keep finalizer code simple to prevent blocking.
4.13. Provide thread-safe cleanup code only if your type is thread-safe.
5.1. If you need to pin buffers, allocate them at startup.
6.1. Minimize thread creation.
6.2. Use the thread pool when you need threads.
6.3. Use a Timer to schedule periodic tasks.
6.4. Consider parallel versus synchronous tasks.
6.5. Do not use Thread.Abort to terminate other threads.
6.6. Do not use Thread.Suspend and Thread.Resume to pause threads.
7. Asynchronous Calls
7.1. Consider client-side asynchronous calls for UI responsiveness.
7.2. Use asynchronous methods on the server for I/O bound operations.
7.3. Avoid asynchronous calls that do not add parallelism.
8. Locking and Synchronization
8.1. Determine if you need synchronization.
8.2. Determine the approach.
8.3. Determine the scope of your approach.
8.4. Acquire locks late and release them early.
8.5. Avoid locking and synchronization unless required.
8.6. Use granular locks to reduce contention.
8.7. Avoid excessive fine-grained locks.
8.8. Avoid making thread safety the default for your type.
8.9. Use the fine grained lock (C#) statement instead of Synchronized.
8.10. Avoid locking “this”.
8.11. Coordinate multiple readers and single writers by using ReaderWriterLock
instead of lock.
8.12. Do not lock the type of the objects to provide synchronized access.
9. Boxing and Unboxing
9.1. Avoid frequent boxing and unboxing overhead.
9.2. Measure boxing overhead.
9.3. Use DirectCast in your Visual Basic .NET code.
10. Exception Management
10.1. Do not use exceptions to control application flow.
10.2. Use validation code to avoid unnecessary exceptions.
10.3. Use the finally block to ensure resources are released.
10.4. Replace Visual Basic .NET On Error Goto code with exception handling.
10.5. Do not catch exceptions that you cannot handle.
10.6. Be aware that rethrowing is expensive.
10.7. Preserve as much diagnostic information as possible in your exception
10.8. Use performance monitor to monitor common language runtime (CLR)
11. Iterating and Looping
11.1. Avoid repetitive field or property access.
11.2. Optimize or avoid expensive operations within loops.
11.3. Copy frequently called code into the loop.
11.4. Consider replacing recursion with looping.
11.5. Use for instead of foreach in performance-critical code paths.
12. String Operations
12.1. Avoid inefficient string concatenation.
12.2. Use + when the number of appends is known.
12.3. Use StringBuilder when the number of appends is unknown.
12.4. Treat StringBuilder as an accumulator.
12.5. Use the overloaded Compare method for case-insensitive string comparisons.
13.1. Prefer arrays to collections unless you need functionality.
13.2. Use strongly typed arrays.
13.3. Use jagged arrays instead of multidimensional arrays.
14.1. Analyze your requirements before choosing the collection type.
14.2. Initialize collections to the right size when you can.
14.3. Consider enumerating overhead.
14.4. Prefer to implement IEnumerable with optimistic concurrency.
14.5. Consider boxing overhead.
14.6. Consider for instead of foreach.
14.7. Implement strongly typed collections to prevent casting overhead.
14.8. Be efficient with data in collections.
15. Reflection and Late Binding
15.1. Prefer early binding and explicit types rather than reflection.
15.2. Avoid late binding.
15.3. Avoid using System.Object in performance critical code paths.
15.4. Enable Option Explicit and Option Strict in Visual Basic.NET.
16. Code Access Security
16.1. Consider SuppressUnmanagedCodeSecurity for performance-critical, trusted
16.2. Prefer declarative demands rather than imperative demands.
16.3. Consider using link demands rather than full demands for performance -
critical, trusted scenarios.
17. Working Set Considerations
17.1. Load only the assemblies you need.
17.2. Consider assemblies that are being loaded as side effects.
17.3. Reduce the number of application domains, and/or make assemblies shared
17.4. Reduce the number of threads.
Checklist for ADO.NET Performance
18. Design Considerations
18.1. Design your data access layer based on how the data is used.
18.2. Cache data to avoid unnecessary work.
18.3. Connect by using service accounts.
18.4. Acquire late, release early.
18.5. Close disposable resources.
18.6. Reduce round trips.
18.7. Return only the data you need.
18.8. Use Windows authentication.
18.9. Choose the appropriate transaction type.
18.10. Use stored procedures.
18.11. Prioritize performance, maintainability, and productivity when you choose
how to pass data across layers.
18.12. Consider how to handle exceptions.
18.13. Use appropriate normalization.
19. Microsoft® .NET Framework Data Providers
19.1. Use System.Data.SqlClient for Microsoft SQL Server™ 7.0 and later.
19.2. Use System.Data.OleDb for SQL Server 6.5 or OLE DB providers.
19.3. Use System.Data.ODBC for ODBC data sources.
19.4. Use System.Data.OracleClient for Oracle.
19.5. Use SQLXML managed classes for XML data and SQL Server 2000.
20.1. Open and close the connection in the method.
20.2. Explicitly close connections.
20.3. When using DataReaders, specify CommandBehavior.CloseConnection.
20.4. Do not explicitly open a connection if you use Fill or Update for a single
20.5. Avoid checking the State property of OleDbConnection.
20.6. Pool connections.
21.1. Validate SQL input and use Parameter objects.
21.2. Retrieve only the columns and rows you need.
21.3. Support paging over large result sets.
21.4. Batch SQL statements to reduce round trips.
21.5. Use ExecuteNonQuery for commands that do not return data.
21.6. Use ExecuteScalar to return single values.
21.7. Use CommandBehavior.SequentialAccess for very wide rows or for rows with
21.8. objects (BLOBs).
21.9. Do not use CommandBuilder at run time.
22. Stored Procedures
22.1. Use stored procedures.
22.2. Use CommandType.Text with OleDbCommand.
22.3. Use CommandType.StoredProcedure with SqlCommand.
22.4. Consider using Command.Prepare.
22.5. Use output parameters where possible.
22.6. Consider SET NOCOUNT ON for SQL Server.
23.1. Use the Parameters collection when you call a stored procedure.
23.2. Use the Parameters collection when you build SQL statements.
23.3. Explicitly create stored procedure parameters.
23.4. Specify parameter types.
23.5. Cache stored procedure SqlParameter objects.
24.1. Close DataReader objects.
24.2. Consider using CommandBehavior.CloseConnection to close connections.
24.3. Cancel pending data.
24.4. Consider using CommandBehavior.SequentialAccess with ExecuteReader.
24.5. Use GetOrdinal when using an index-based lookup.
25.1. Reduce serialization.
25.2. Use primary keys and Rows.Find for indexed searching.
25.3. Use a DataView for repetitive non-primary key searches.
25.4. Use the optimistic concurrency model for datasets.
25.5. XML and DataSet Objects
25.6. Do not infer schemas at run time.
25.7. Perform bulk updates and inserts by using OpenXML.
26.1. Avoid unnecessary type conversions.
27. Exception Management
27.1. Use the ConnectionState property.
27.2. Use try/finally to clean up resources.
27.3. Use specific handlers to catch specific exceptions.
28.1. Use SQL transactions for server controlled-transactions on a single data store.
28.2. Use ADO.NET transactions for client-controlled transactions on a single data
28.3. Use Distributed Transaction Coordinators (DTC) for transactions that span
multiple data stores.
28.4. Keep transactions as short as possible.
28.5. Use the appropriate isolation level.
28.6. Avoid code that can lead to deadlock.
28.7. Set the connection string Enlist property to false.
29. Binary Large Objects
29.1. Use CommandBehavior.SequentialAccess and GetBytes to read data.
29.2. Use READTEXT to read from SQL Server 2000.
29.3. Use OracleLob.Read to read from Oracle databases.
29.4. Use UpdateText to write to SQL Server databases.
29.5. Use OracleLob.Write to write to Oracle databases.
29.6. Avoid moving binary large objects repeatedly.