Friday, September 22, 2006

Important tips for improving performance of .NET applications

When you are working on .NET based applications, it is very important to know key areas which would affect the performance of your application. MSDN has lots of information about this. I have tried to consolidate and list down some important tips for the performance tuning of .NET based applications. I hope that you will find this useful. Some of the tips are applicable to .NET 2.0 only. Happy Performance Tuning! :)

Use of Generics

  1. Language features collectively known as generics act as templates that allow classes, structures, interfaces, methods, and delegates to be declared and defined with unspecified, or generic type parameters instead of specific types
  2. It is recommended that all applications that target Version 2.0 use the new generic collection classes instead of the older non-generic counterparts such as ArrayList
  3. Version 2.0 of the .NET Framework class library provides a new namespace, System.Collections.Generic, which includes several ready-to-use generic collection classes and associated interfaces. Other namespaces such as System also provide new generic interfaces such as IComparable. These classes and interfaces are more efficient and type-safe than the non-generic collection classes provided in earlier releases of the .NET Framework. Before designing and implementing your own custom collection classes, consider whether you can use or derive a class from one of the classes provided in the base class library.
  4. Using generic collections is generally recommended, because you gain the immediate benefit of type safety without having to derive from a base collection type and implement type-specific members. In addition, generic collection types generally perform better than the corresponding nongeneric collection types when the collection elements are value types, because with generics there is no need to box the elements.
  5. The following generic types correspond to existing collection types:
  • List is the generic class corresponding to ArrayList.
  • Dictionary is the generic class corresponding to Hashtable.
  • Collection is the generic class corresponding to CollectionBase. Collection can be used as a base class, but unlike CollectionBase it is not abstract, making it much easier to use.
  • ReadOnlyCollection is the generic class corresponding to ReadOnlyCollectionBase. ReadOnlyCollection is not abstract, and has a constructor that makes it easy to expose an existing List as a read-only collection.
  • The Queue, Stack, and SortedList generic classes correspond to the respective nongeneric classes

Weak References

  1. Weak References are suitable for medium-to-large sized objects stored in a collection.
  2. Using Weak Reference is one way of implementing caching policy.
  3. By using weak references, cached objects can be resurrected easily if needed or they can be released by garbage collection when there is memory pressure.

Memory Management

  1. Check that your code calls Dispose or Close on all classes that support these methods.Common disposable resources include: database related classes, files related classes, stream related classes and network related classes.
  2. Check that your code does not call GC.Collect
  3. Finalization has impact on performance. Identify which classes need finalize. Typically, classes using unmanaged resources will need it. Check that any class that provides a finalizer also implements IDisposable. Avoid implementing a finalizer on classes that do not require it because it adds load to the finalizer thread as well as the garbage collector .
  4. Identify potentially long-running method calls. Check that you set any class-level member variables that you do not require after the call to null before making the call. This enables those objects to be garbage collected while the call is executing. There is no need to explicitly set local variables to null because the just-in-time (JIT) compiler can statically determine that the variable is no longer referenced.

Looping and Recursion

  1. Even the slightest coding inefficiency is magnified when that code is located inside a loop. Loops that access an object's properties are a common culprit of performance bottlenecks, particularly if the object is remote or the property getter performs significant work.
  2. Repeated accessing of object properties can be expensive. Properties can appear to be simple, but might in fact involve expensive processing operations. Avoid repetitive field or property access.
  3. If you do use recursion, check that your code establishes a maximum number of times it can recurse, and ensure there is always a way out of the recursion and that there is no danger of running out of stack space.
  4. Optimize or avoid expensive operations within loops.
  5. Copy frequently called code into the loop.
  6. Consider replacing recursion with looping.
  7. Use for instead of foreach in performance-critical code paths.

String Operations

  1. Avoid inefficient string concatenation.
  2. Use + when the number of appends is known
  3. Use StringBuilder when the number of appends is unknown.
  4. Treat StringBuilder as an accumulator.
  5. Use the overloaded Compare method for case insensitive string comparisons.

Arrays

  1. Prefer arrays to collections unless you need functionality. Arrays also avoid the boxing and unboxing overhead.
  2. Use strongly typed arrays.
  3. Use jagged arrays instead of multidimensional arrays.
  4. Arrays have a static size. The size of the array remains fixed after initial allocation. If you need to extend the size of the array, you must create a new array of the required size and then copy the elements from the old array.
  5. Arrays support indexed access. To access an item in an array, you can use its index.
  6. Arrays support enumerator access. You can access items in the array by enumerating through the contents using the foreach construct (C#) or For Each (Visual Basic .NET).
  7. Memory is contiguous. The CLR arranges arrays in contiguous memory space, which provides fast item access.

Collections

  1. Initialize the collection to an approximate final size. It is more efficient to initialize collections to a final approximate size even if the collection is capable of growing dynamically.
  2. Storing value types in a collection involves a boxing and unboxing overhead. The overhead can be significant when iterating through a large collection for inserting or retrieving the value types. Consider using arrays or developing a custom, strongly typed collection for this purpose.

Hash Table:

a. Do you store small amounts of data in a Hashtable? If you store small amounts of data (10 or fewer items), this is likely to be slower than using a ListDictionary. If you do not know the number of items to be stored, use a HybridDictionary.

b. Do you store strings? Prefer StringDictionary instead of Hashtable for storing strings, because this preserves the string type and avoids the cost of up-casting and down-casting during storing and retrieval.

c. Have you though of using generic class called as Dictionary? The Dictionary generic class provides a mapping from a set of keys to a set of values. Each addition to the dictionary consists of a value and its associated key. Retrieving a value by using its key is very fast, close to O(1), because the Dictionary class is implemented as a hash table.

d. Do You Use SortedList?You should use a SortedList to store key-and-value pairs that are sorted by the keys and accessed by key and by index. New items are inserted in sorted order, so the SortedList is well suited for retrieving stored ranges.You should use SortedList if you need frequent re-sorting of data after small inserts or updates. If you need to perform a number of additions or updates and then re-sort the whole collection, an ArrayList performs better than the SortedList.

ArrayList:

a. Do you store strongly typed data in ArrayLists? Use ArrayList to store custom object types, particularly when the data changes frequently and you perform frequent insert and delete operations.

b.Do you use Contains to search ArrayLists? Store presorted data and use ArrayList.BinarySearch for efficient searches. Sorting and linear searches using Contains are inefficient. This is of particular significance for large lists. If you only have a few items in the list, the overhead is insignificant. If you need several lookups, then consider Hashtable instead of ArrayList.

Reflection and Late Binding

  1. Prefer early binding and explicit types rather than reflection.
  2. Avoid late binding.
  3. Avoid using System.Object in performance critical code paths.
  4. Framework APIs such as Object.ToString use reflection. Although ToString is a virtual method, the base Object implementation of ToString uses reflection to return the type name of the class. Implement ToString on your custom types to avoid this.
  5. Avoid using System.Object to access custom objects because this incurs the performance overhead of reflection. Use this approach only in situations where you cannot determine the type of an object at design time.

Class Design Considerations

  1. Do not make classes thread safe by default.
  2. Consider using the sealed keyword.
  3. Consider the tradeoffs of virtual members.
  4. Consider using overloaded methods.
  5. Consider overriding the Equals method for value types.
  6. Know the cost of accessing a property.
  7. Consider private vs. public member variables.
  8. Limit the use of volatile fields.

Threading Considerations

  1. Minimize thread creation.
  2. Use the thread pool when you need threads.
  3. Use a Timer to schedule periodic tasks.
  4. Consider parallel vs. synchronous tasks.
  5. Do not use Thread.Abort to terminate other threads.
  6. Do not use Thread.Suspend and Thread.Resume to pause threads.

Asynchronous Call Considerations

  1. Consider client-side asynchronous calls for UI responsiveness.
  2. Use asynchronous methods on the server for I/O bound operations.
  3. Avoid asynchronous calls that do not add parallelism.
  4. For each call to BeginInvoke, make sure your code calls EndInvoke to avoid resource leaks.

Cheers,
Amol

No comments: