String Operations:-
Review
your code which performs intensive stringmanipulations.
‘+’ Operator:-
When
number of operands is known, perform stringconcatenate
using +operator.
e.g.
String
str = “abc” + “xyz” + “pqr”
Why -
StringBuilder Class:-
When
number of operand is unknown, perform string concatenate using
StringBuilder.Append(). Such as string concatenation in loop , you should use
StringBuilder
e.g.
for(int i=0; i<Results.count; i++)
{
StringBuilder.Append(Results[i])
}
Some
example to demonstrate StringBuilder:
//prefer this
StringBuilder sb;
Sb.Append(str1);
Sb.Append(str2);
//Over this
Sb.Append(str1+str2);
//prefer this for concatenate string for various functions
Void F1(sb, …);
Void F2(sb, …);
//Over this
StringBuilder sb;
Sb.Append(F1(…));
Sb.Append(F2(…));
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
Looping & Recursions:-
Slight
inefficiency in looping & recursions is magnified due to it being
repeatedly called.
Recursions
·
In
some scenarios looping is preferable than using recursion because recursive
call builds new stack frame for calls which results in memory consumption
·
Looping
does not require building stack frame unless a method is called inside a loop
·
Make
sure the recursive call have a way out & there is no danger of running out
of stack space
Foreach(C#):-
Foreach
provides an enumerator by overrriding IEnumerable.GetEnumerator. This causes to
add both managed heap & virtual function overhead on simple types
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.
Properties:-
Though
properties looks like a field it has hidden costs. You should design the
classes accordingly to define properties & fields as required
You
need to be-aware that when the property is accessed, additional code such as
validation check can be executed. Hence this might be slower than accessing a
field. Simple properties like setting or getting the private member variable
there is no performance difference as compared to accessing a field. However if
you use virtual properties it become easily complicated as virtual properties
cannot be in-lined
Repeatedly
accessing a property in for loop becomes more expensive. So if possibly it
should be read outside the for loop
Optimize Loops:-
Examine
the code in loop to find the opportunities to optimize it. Some of them are as
mentioned below
·
Move
out any code that does not change inside the loop
·
Use
StringBuilder for concatenating the strings inside the loop
·
Considering
inlining the code instead of calling the function which contain small amount of
code
·
If
you test multiple condition inside the loop, begin the expression which will
most likely allow you to exit
·
Avoid
calling properties inside a loop.
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 a 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/catchblock outside the forloop.
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.
Catch(Exception e)
{
}
Explicitly name the exception to avoid catching &
re-throwing. Below code catches all System.IOexceptions:
Catch(System.IO)
{
}
Preserve
as much as diagnostic information as possible in your exception handlers
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
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
Static bool nameExists(String name)
{
If(cr.read(name)
== 0)
{
Return
false;
}
}
Locking & Synchronization:-
Mutex object
Tradeoff
for mutex object is when you require interprocess synchronization. Do not use
mutex to cross –synchronization of threads within a single process. Mutex
object is more expensive as this is kernel object, hence provides cross-process
synchronization
Lock”this”
For
correctness reasons avoid locking on this object. Instead provide a private
object to lock on. Below is example
//prefer this
Class A{
Private Object mylock = new Object();
Lock(myLock)
{ … }
}
//over this
Class A{
Lock(this)
{ … }
}
“This”
is external visible object & you never know what other code might be
acquiring this same lock
If
you require a atomic updates to your member variable use
System.Threading.Interlocked class
Lock typeof(object)
Avoid
locking type of the object, because there might be other threads in same
process that lock on same type of object. Hence it might cause your code to
hang until the thread releases the lock on same type of object
This
also creates a potential to create deadlocks. Prefer using a private static
object in your class to provide synchronization
//prefer this
Class A{
Private static Object _lock = new object();
Lock(_lock)
{ …. }
}
//over this
Lock(typeof(myObject))
ReaderWriterLock
Tradeoff
for using ReaderWriterLock is when you require multiple threads to read the
resource concurrently & synchronized access to write the resource. Hence
during such scenarios ReaderWriter lock should be preferred over Lock &
Monitor locking mechanisms
Threading:-
ThreadStart
Indiscriminately
spawning the 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
Recycling
threads with thread pools results in superior quality, then spawning new thread
for new requests
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();
….
}
ThreadPool
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);
ThreadPool
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 elasped 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 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
If
you want to synchronize activities between multiple threads try using
synchronization objects like lock, mutex, monitor, Events etc..
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
Complex
Object Graphs
Candidates
for the complex object graph can be those with many references to other objects.
Complex Object graphs can result in additional work for garbage collectors to
create & allocate these objects. Identify opportunities to simplify the
structures & classes to have good heap locality & easier to maintain
Also,
identify the problem where short-lived objects are referenced from long-lived
objects. As a result of this short-lived object are been promoted from
generation 0 & increases burden on garbage collector
WeakReference Objects:-
Consider
using WeakReference when working with cached data, So that cached objects can
be resurrected when needed or released by garbage collector when there is
memory pressure. WeakReference is suitable for medium to large sized objects
stored in collection
e.g.
of WeakReferenceimplementation 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 ArrayList
·
You
can measure your assembly for box & unbox instruction by using below
command line
Ildasm.exe
yourcomponent.dll /text | findstr box (OR unbox)
String Operations:-
Review
your code which performs intensive stringmanipulations.
‘+’ Operator:-
When
number of operands is known, perform stringconcatenate
using +operator.
e.g.
String
str = “abc” + “xyz” + “pqr”
Why -
StringBuilder Class:-
When
number of operand is unknown, perform string concatenate using
StringBuilder.Append(). Such as string concatenation in loop , you should use
StringBuilder
e.g.
for(int i=0; i<Results.count; i++)
{
StringBuilder.Append(Results[i])
}
Some
example to demonstrate StringBuilder:
//prefer this
StringBuilder sb;
Sb.Append(str1);
Sb.Append(str2);
//Over this
Sb.Append(str1+str2);
//prefer this for concatenate string for various functions
Void F1(sb, …);
Void F2(sb, …);
//Over this
StringBuilder sb;
Sb.Append(F1(…));
Sb.Append(F2(…));
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
Looping & Recursions:-
Slight
inefficiency in looping & recursions is magnified due to it being
repeatedly called.
Recursions
·
In
some scenarios looping is preferable than using recursion because recursive
call builds new stack frame for calls which results in memory consumption
·
Looping
does not require building stack frame unless a method is called inside a loop
·
Make
sure the recursive call have a way out & there is no danger of running out
of stack space
Foreach(C#):-
Foreach
provides an enumerator by overrriding IEnumerable.GetEnumerator. This causes to
add both managed heap & virtual function overhead on simple types
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.
Properties:-
Though
properties looks like a field it has hidden costs. You should design the
classes accordingly to define properties & fields as required
You
need to be-aware that when the property is accessed, additional code such as
validation check can be executed. Hence this might be slower than accessing a
field. Simple properties like setting or getting the private member variable
there is no performance difference as compared to accessing a field. However if
you use virtual properties it become easily complicated as virtual properties
cannot be in-lined
Repeatedly
accessing a property in for loop becomes more expensive. So if possibly it
should be read outside the for loop
Optimize Loops:-
Examine
the code in loop to find the opportunities to optimize it. Some of them are as
mentioned below
·
Move
out any code that does not change inside the loop
·
Use
StringBuilder for concatenating the strings inside the loop
·
Considering
inlining the code instead of calling the function which contain small amount of
code
·
If
you test multiple condition inside the loop, begin the expression which will
most likely allow you to exit
·
Avoid
calling properties inside a loop.
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 a 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/catchblock outside the forloop.
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.
Catch(Exception e)
{
}
Explicitly name the exception to avoid catching &
re-throwing. Below code catches all System.IOexceptions:
Catch(System.IO)
{
}
Preserve
as much as diagnostic information as possible in your exception handlers
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
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
Static bool nameExists(String name)
{
If(cr.read(name)
== 0)
{
Return
false;
}
}
Locking & Synchronization:-
Mutex object
Tradeoff
for mutex object is when you require interprocess synchronization. Do not use
mutex to cross –synchronization of threads within a single process. Mutex
object is more expensive as this is kernel object, hence provides cross-process
synchronization
Lock”this”
For
correctness reasons avoid locking on this object. Instead provide a private
object to lock on. Below is example
//prefer this
Class A{
Private Object mylock = new Object();
Lock(myLock)
{ … }
}
//over this
Class A{
Lock(this)
{ … }
}
“This”
is external visible object & you never know what other code might be
acquiring this same lock
If
you require a atomic updates to your member variable use
System.Threading.Interlocked class
Lock typeof(object)
Avoid
locking type of the object, because there might be other threads in same
process that lock on same type of object. Hence it might cause your code to
hang until the thread releases the lock on same type of object
This
also creates a potential to create deadlocks. Prefer using a private static
object in your class to provide synchronization
//prefer this
Class A{
Private static Object _lock = new object();
Lock(_lock)
{ …. }
}
//over this
Lock(typeof(myObject))
ReaderWriterLock
Tradeoff
for using ReaderWriterLock is when you require multiple threads to read the
resource concurrently & synchronized access to write the resource. Hence
during such scenarios ReaderWriter lock should be preferred over Lock &
Monitor locking mechanisms
Threading:-
ThreadStart
Indiscriminately
spawning the 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
Recycling
threads with thread pools results in superior quality, then spawning new thread
for new requests
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();
….
}
ThreadPool
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);
ThreadPool
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 elasped 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 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
If
you want to synchronize activities between multiple threads try using
synchronization objects like lock, mutex, monitor, Events etc..
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
Complex
Object Graphs
Candidates
for the complex object graph can be those with many references to other objects.
Complex Object graphs can result in additional work for garbage collectors to
create & allocate these objects. Identify opportunities to simplify the
structures & classes to have good heap locality & easier to maintain
Also,
identify the problem where short-lived objects are referenced from long-lived
objects. As a result of this short-lived object are been promoted from
generation 0 & increases burden on garbage collector
WeakReference Objects:-
Consider
using WeakReference when working with cached data, So that cached objects can
be resurrected when needed or released by garbage collector when there is
memory pressure. WeakReference is suitable for medium to large sized objects
stored in collection
e.g.
of WeakReferenceimplementation 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 ArrayList
·
You
can measure your assembly for box & unbox instruction by using below
command line
Ildasm.exe
yourcomponent.dll /text | findstr box (OR unbox)
No comments:
Post a Comment