In this article in the optimizing .NET code series, we’ll discuss “Iteration and Looping”. Non-optimized code within loops can lead to performance issues, ranging from increased memory consumption to CPU exhaustion. This section summarizes guidelines that will improve iteration and loop efficiency:

Avoid Repetitive Field or Property Access

If your data 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 tips can help avoid expensive loop operations:

·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;

}

Use for Instead of foreach in Performance-Critical Code Paths

Use for instead of foreach (C#) to iterate the contents of collections in performance-critical code. foreach in C# and For Each in Visual Basic .NET use an enumerator to provide enhanced navigation through arrays and collections.

About Ard-Jan Barnas

Ard-Jan is a highly technical writer with deep knowledge into the industry. He has an international background and always brings forth articles that are not just technical but with a mix of business application. This
encompassing approach to technology married to business is a welcome approach to writing.