.NET Framework 1.1 Performance Guidelines - Iterating and Looping

From Guidance Share

Jump to: navigation, search

- J.D. Meier, Srinath Vasireddy, Ashish Babbar, Rico Mariani, and Alex Mackman


Contents

Avoid Repetitive Field or Property Access

If you use data that is static for the duration of the loop, obtain it before the loop instead of repeatedly accessing a field or property. The following code shows a collection of orders being processed for a single customer.

for ( int item = 0; item < Customer.Orders.Count ; item++ ){
 CalculateTax ( Customer.State, Customer.Zip, Customer.Orders[item] );
}

Note that State and Zip are constant for the loop and could be stored in local variables rather than accessed for each pass through the loop as shown in the following code.

string state = Customer.State;
string zip = Customer.Zip;
int count = Customers.Orders.Count;
for ( int item = 0; item < count ; item++ )
{
  CalculateTax (state, zip, Customer.Orders[item] );
}

Note that if these are fields, it may be possible for the compiler to do this optimization automatically. If they are properties, it is much less likely. If the properties are virtual, it cannot be done automatically.


Optimize or Avoid Expensive Operations Within Loops

Identify operations in your loop code that can be optimized. Look for code that causes boxing or allocations as a side effect. The following code causes side effect strings to be created for each pass through the loop.

String str;
Array arrOfStrings = GetStrings();
for(int i=0; i<10; i++)
{
  str+= arrOfStrings[i];
}

The following code avoids extra string allocations on the heap by using StringBuilder.

StringBuilder sb = new StringBuilder();
Array arrOfStrings = GetStrings();
for(int i=0; i<10; i++)
{
  sb.Append(arrOfStrings.GetValue(i));
}

The following guidelines can help you avoid expensive operations in loops:

  • Be aware of the method calls you make inside loops. Watch out for inadvertent method calls and consider using inline code where appropriate.
  • Consider StringBuilder for string concatenation inside a loop. For more information, see "String Operations" later in this chapter.
  • When testing for multiple conditions to exit out or continue looping, order your tests so that the one most likely to let you escape the loop, is run first.


Copy Frequently Called Code into the Loop

If you repeatedly call methods from inside a loop, consider changing the loop to reduce the number of calls made. The JIT compiler usually inlines any called code if it is simple, but in most complex scenarios it is your responsibility to optimize the code. The costs of the call increase as you cross process or computer boundaries with remoting or Web services. The following code shows a method being called repeatedly inside a loop.

for ( int item = 0 ; item < Circles.Items.Length; item++ ){

 CalculateAndDisplayArea(Circles[item]);

}

Consider the following strategies to reduce the calls incurred:

  • Move the called code into the loop. This reduces the number of calls being made.
  • Move the whole unit of work to the called object. The following code modifies the object being called and passes all the required data so that the whole loop can happen remotely. This is helpful to avoid round trips and offloads the work to local calls for an object which may be hosted remotely.
     // call function to store all items
     OrderProcessing op = new OrderProcessing();
     StoreAllOrderItems (Order.Items);
     ...
     class OrderProcessing{
     ...
       public bool StoreAllOrderItems ( Items itemsToInsert )
       {
         SqlConnection conn = new SqlConnection(...
         SqlCommnd cmd = new SqlCommand(...
         for ( int item = 0 ; item < orders.Items.Length; item++ ){
           // insert order into database
           // set parameters on command object
           cmd.ExecuteNonQuery();
           // insert order item
         }
       }
       . . .
     }


Consider Replacing Recursion with Looping

Each recursive call adds data to the stack. Examine your code and see if your recursive calls can be converted to a looping equivalent. The following code makes recursive calls to accomplish a small task of string concatenation.

Array arr = GetArrayOfStrings();
int index = arr.Length-1;
String finalStr= RecurStr(index);
string RecurStr(int ind){
  if (ind<=0)
    return "";
  else
    return (arr.GetValue(ind)+RecurStr(ind-1));
}

Rewritten, the following code now avoids creating new data on the stack for each successive call and avoids an additional method call to itself.

string ConcString (Array array)
{
  StringBuilder sb = new StringBuilder();
  for (int i= array.Length; i>0; i--)
  {
    sb.Append(array.GetValue(i));
  }
  return sb;
}


Consider for Instead of foreach

Use for instead of foreach (C#) to iterate the contents of arrays or collections in performance critical code, particularly if you do not need the protections offered by foreach.

Both foreach in C# and For Each in Visual Basic .NET use an enumerator to provide enhanced navigation through arrays and collections. As discussed earlier, typical implementations of enumerators, such as those provided by the .NET Framework, will have managed heap and virtual function overhead associated with their use.

If you can use the for statement to iterate over your collection, consider doing so in performance sensitive code to avoid that overhead.

Personal tools