Monday, December 28, 2015

Best practise for Performance Tuning of .NET application

Need to follow these points
  • StringBuilder instead of String concatenation
  • LINQ – ‘Where’ with ‘First’ instead of FirstOrDefault
  • Casting by means of ‘(T)’ instead of ‘as (T)’ when possibly not castable
  • Incorrect exceptions re-throwing
  • Not using ‘using’ for objects disposal
  • Using ‘foreach’ instead of ‘for’ for anything else than collections



1.    StringBuilder instead of String concatenation
          
String concatenation: Add something to a string, a new address in the memory is being allocated. The previous string is copied to a new location with the newly added part. This is inefficient.
·                 On the other hand we have StringBuilder which keeps the same position in the memory without performing the copy operation, StringBuilder process is much more efficient, especially in case of hundreds of append operations.
//INCORRECT
List values = new List(){"This ","is ","Sparta ","!"};
string outputValue = string.Empty;

foreach (var value in values)
{
                        outputValue += value;
}
            //CORRECT
StringBuilder outputValueBuilder = new StringBuilder();

foreach (var value in values)
{
                        outputValueBuilder.Append(value);
}
2.  LINQ – FirstOrDefault instead of ‘Where’ with ‘First’
·                 If “First” is used when no value is found, an exception will be thrown.
·                 Thus, it’s better to use FirstOrDefault instead.
·                 When using FirstOrDefault, if no value has been found, the default value for this type will be
returned and no exception will be thrown.
//INCORRECT
List numbers = new List(){1,4,5,9,11,15,20,21,25,34,55};
return numbers.Where(x => Fibonacci.IsInFibonacciSequence(x)).First(); 

//PARTLY CORRECT
return numbers.First(x => Fibonacci.IsInFibonacciSequence(x));

//CORRECT
return numbers.FirstOrDefault(x => Fibonacci.IsInFibonacciSequence(x));
3. Casting by means of ‘as (T)’ instead of  ‘(T)’ when possibly not castable
·                 It’s common that software developers use simple ‘(T)’ casting, instead of ‘as (T)’.
·                 And usually it doesn’t have any negative influence because casted objects are 
                  always castable. Yet,
if there is even a very slight probability that an object can be under some circumstances not castable, „as (T)” casting should be used.


 
   //INCORRECT
   var woman = (Woman)person;
   //CORRECT
   var woman = person as Woman;
     
    4. Incorrect exceptions re-throwing
·                                   C# programmers usually forget that when they throw an exception using„ 
                     ”throw  ex” they loose the stack trace. It is then considerably harder to debug an 
                      application  and to achieve appropriate log messages.
·                              When simply using„ ”throw” no data is lost and the whole exception together with the
                      stack trace   can be easily retrieved.

//INCORRECT
try
{
   //some code that can throw exception [...]
}
catch (Exception ex)
{
   //some exception logic [...]
   throw ex;
}
 //CORRECT
try
{
   //some code that can throw exception [...]
}
catch (Exception ex)
{
   //some exception logic [...]
   throw;
}
5. Not using ‘using’ for objects disposal
·                 Many C# software developers don’t even know that ‘using’ keyword is not only used as a directive for adding namespaces, but also for disposing objects.
·                 If you know that a certain object should be disposed after performing some operations, always use the ‘using’ statement to make sure that the object will actually be disposed.

//the below code:
using(SomeDisposableClass someDisposableObject = new SomeDisposableClass())
{
   someDisposableObject.DoTheJob();
}

//does the same as:
SomeDisposableClass someDisposableObject = new SomeDisposableClass();
try
{
   someDisposableObject.DoTheJob();
}
finally
{
   someDisposableObject.Dispose();
}
6. Using ‘for’ instead of ‘foreach’ for anything else than collections
Remember that if you want to iterate through anything that is not a collection (so through e.g. an array), using the ‘for’ loop is much more    efficient than using the ‘foreach’ loop.


7. String.Compare()
When performing case-insensitive string comparisons, Check for the lines which calls ToLower() as these are not required for performing case-insensitive comparisons
String.Compare(String str1, String str2, bool ignoreCase);
Calling ToLower() method & then comparing will require temporary string allocation which can be expensive when called in Loop.
          8 .Foreach(C#)
            Use for loop instead of foreach to iterate through simple type array OR collection (built in value  types such as int, char etc..) in performance critical code.
Optimize Loops
Examine the code in loop to find the opportunities to optimize it. 
Some of them are as  mentioned below:

                                          i.    Move out any code that does not change inside the loop. Use StringBuilder for concatenating the strings inside the loop.
                                        ii.    Considering inlining the code instead of calling the function which contain small amount of code. Avoid calling properties inside a loop.


9. Exception Handling
       Exception handling by try/catch block is recommended way to handle exceptional error condition in  managed code. Improper managed exceptions can significantly affect performance.
Finally block
Make sure you use finally block to free up your resources. Finally block is always executed, even if an exception occurs e.g.:
try
{
  conn.Open(); // assume some connection object, which implements IDisposable
}
finally
{
 if(null!=conn)
   conn.Close();  // Always executed even if an exception occurs
}
Else you can use using construct in C#, which call the dispose at end of the construct, assuming the required resource implements the Idisposable.

Using(conn)
{
 conn.open();
}
Rethrow exception:
The cost of using throw to rethrow an existing exception is same a throwing the new  exception.
Try{
//do something which can throw an exception
}
Catch(Exception e){
Throw;
}

In above e.g. there is no saving from re-throwing the existing exception. You should consider wrapping an re-throwing the exception on when it provides additional diagnostic information.
Loops
Check if your code throws an exception inside for loop. This should be avoided, place your try/catch block outside the for loop.
Reduce unnecessary exception:
Do not catch exception which you cannot handle. You should catch exception to provide some debugging information (exception details) or retry any failed information.
Avoid catching generic exception, this leads to catching all exception & most of these exceptions are re-thrown eventually.

//INCORRECT                                                                                                                                        
Catch(Exception e)
{
}

Explicitly name the exception to avoid catching & re-throwing. Below code catches all System.IO exceptions:

//CORRECT                                                                                                                                                  
Catch(System.IO)
{
}
Avoid exception to control application flow:
Do not use exception to control your application flow. If you except events in normal course of code execution, you should not throw an exception. In below e.g. exception is thrown inappropriately when customer name is not found.

//INCORRECT                                                                                                                               
Static void nameExists(String name)
{
If(cr.read(name) == 0)
{
Throw(new Exception(“name not found”));
}
}
Name not found is expected condition, so re-factor the code to return value instead of throwing exception.

//CORRECT                                                                                                                                              
Static bool nameExists(String name)
{
      If(cr.read(name) == 0)
      {
                     Return false;
}
}
10. Threading
Thread Start:
Threads can easily reduce your application performance rather than improving. Frequently creating new threads can lead to extensive context switching, memory allocation & additional cleanup when a thread dies.
Below code shows new thread been created & maintained for each page load.
Private void page_load(Object o, System.EventArgs e){

If (page.isPastBack)
{
   ThreadStart ts = new ThreadStart(callFunc);
   Thread tr = new Thread(ts);
   Tr.start();
   ….
}
                 Thread Pool
Use CLR thread pool to execute thread based work, to avoid expensive thread creation OR  initialization. Below code shows method been executed using a thread from thread pool
           WaitCallBack methodTarget = new WaitCallBack(MyClass.updateCache)
                       ThreadPool.QueueUserWorkItem(methodTarget);
                                          Thread Pool class uses a thread from application pool to execute the method passed in callback as  soon a thread is available
                   System.Threading.Timer
Use Timer class to perform periodic tasks. Timer class provides execution of method by specifying the time interval. Each time the timer elapsed a thread from a thread pool is used to execute the method indicated in TimerCallBack.
            Below code shows calling the myFunc() method every 30 secs
                        TimerCallBack myCallBack = new TimerCallBack(myFunc);
                        Timer tr = new System.Threading.Timer(myCallBack, null, 0, 30000,);
                        Static void myFunc(object state)
                        {
                              …..
                        }
                        This results in optimal performance because it avoids thread initialization incurred in
                      spawning (producing) new thread

           Thread.Abort
                         Aviod using Thread.Abort for terminating other threads. Abort caused CLR to throw a ThreadAbortException on a thread to be terminated. You can use Thread.Join to wait on the thread to make sure that the thread has terminated.
           
Thread.Resume / Thread.suspend
Never call Thread.Resume OR Thread.suspend to synchronize the activities between the threads. Never call suspend to suspend low priority thread, instead consider setting  Thread.Priority property
Calling suspend on one thread from other can cause application deadlock. For example you might suspend a thread holding May resources needed by other threads.

11. Memory Management
                      Call Dispose OR Close
Your code should call Dispose or Close on all objects which supports this methods. For e.g. all the objects which implements IDisposable.
   Common disposable objects are as follows:
·                                Database – Connection, DataReader & Transaction
·                                File – FileStream, BinaryWriter
  ·           Stream Based – StreamReader/Writer, TextReader/Writer, BinaryReader/Writer
                          Also, check Finally & Using blocks to ensure resources are released

  Weak Reference Objects
Consider using Weak Reference when working with cached data, So that  cached objects can be resurrected (bring back to life) when needed or released by garbage collector when there is memory pressure. Weak Reference is suitable for medium to large sized objects stored in collection. e.g. of Weak Reference implementation is as below:

  Void someMethod()
   {
               ArrayList arList = new ArrayList[5];
               MyObject obj = new MyObject();
               WeakReference wr = new WeakReference(obj);
               arList.add(wr);
               //retrive Weak reference
               WeakReference wr = (WeakReference)arList[0];
               //Create new myObj to be assigned
               MyObject myObj = Null;
               If(Weakreference.IsAlive)
                               myObj = (MyObject)wr.Target;              //if this object is not collected
                                                                                            //by garbage collector
               if(MyObj == NULL)
{
               //As object is collected by GC, surrect again
}
}
GC.Collect:
Garbage collector is self-tuning, by programmatically forcing a collection you might hinder the performance rather than improve. Your code should not call GC.Collect explicitly
                        Finalizers:
                        Use finalization for the objects that need to perform cleanup task during collection process & just before the object memory is reclaimed. Finalizers are mainly use to release unmanaged resources (database connection, file handles, COM object reference) maintained by object
                        Use below consideration for writing Finalizers
·                                 Implement finalizer only for the object which holds unmanaged code. Unnecessary finalizers adds extra load to finalizer thread as well as garbage collector
·                       Class that implements finalizers should also implements Idisposable and the dispose function should use supressFinalization if the cleanup is already been performed in dispose function
·                             Dispose methods should call dispose of base class as well as dispose of class members
·                             Cleanup code in Finalizers should be thread safe for thread safe types
                        Boxing & Unboxing
Boxing & unboxing enables value types to be treated as objects (reference). Boxing a value type packages it inside an instance of type Object reference type. This results in storing value type in managed heap. Unboxing is reversed process which extracts value from the object type

Consider below example
           Int I = 111;
Object o = (Object) I;   //boxing – allocated value type on managed heap by
                      
//creating new object
O = 222;
I = (int)o; //unboxing
Boxing & unboxing are computationally expensive process i.e. when a value type is boxed entirely new object has to be created & allocated

Some considerations:

§  Avoid passing value types to the method which expect a reference type
§  Pay attention to code in loops where boxing overhead can quickly add up
§  Consider using arrays or collection of custom-type class rather than using collection of System. Objects For example consider arrays to store integer value type instead of using ArrayLists.
§  You can measure your assembly for box & unbox instruction by using below command line


No comments:

Post a Comment

Write a program to reverse a string? using System; namespace ConsoleApp1 {     class Program     {         static void Main(string[] args)  ...