Welcome Guest!
Create Account | Login
Locator+ Code:

Search:
FTPOnline Channels Conferences Resources Hot Topics Partner Sites Magazines About FTP RSS 2.0 Feed

Back to VSLive! San Francisco Show Daily Home

email article
printer friendly
more resources

Improve Performance With IL
Learn how to use StringBuilder and the for and foreach statements to speed up your C# apps.
by Godfrey Nolan

VSLive! San Francisco, February 8, 2005

One of the key benefits of managed code on the .NET platform is your source code gets turned into an intermediate language (IL). This gives you great insights into how your code will be compiled and executed by the CLR, and it removes many of the urban myths around what is and isn't high-performance C# code.

ADVERTISEMENT

You can squeeze performance gains from .NET programs in many ways. In this article, I'll show you a few things to be aware of when trying to improve the performance of your .NET application, and hopefully along the way you'll get some idea of how the IL gives you the last word when discussing C# performance.

The first example looks at probably the most often quoted C# performance tip: using StringBuilder for concatenating long strings (especially in loops). When building large strings, it pays not to use the + operator or the String.Concat() method to concatenate strings. For example, take this code:

    void stringDemo(ArrayList
   outputStrings)
   {
      String concatenator = "";
      for(int i = 0; i < outputStrings.Count; i++)
         concatenator += outputStrings[i];
   }

This code produces the corresponding IL:

  .locals init ([0] string concatenator,
           [1] int32 i)
  IL_000a:  ldloc.0 //load ref to string accumulator
  IL_000b:  ldarg.1 //load ref to ArrayList
  IL_000c:  ldloc.1 //load index int
  IL_000d:  callvirt   instance object
  //get string from ArrayList
[mscorlib]System.Collections.ArrayList::get_Item(int32) 
  //call String.Concat
  IL_0012:  call       
        string [mscorlib]System.String::Concat(object,
             object)
  //replace the old String ref with the new one.
  //the old ref now needs to be garbage collected.  
  IL_0017:  stloc.0  

With every += in the previous example, String.Concat creates another string variable and another reference is released. As a result your code will run unnecessarily slowly, use more memory than it should, and create more work for the garbage collector.

Here is the code to use if you want to use StringBuilder instead of the + operator:

   void stringbuilderDemo(ArrayList outputStrings)
   {
      StringBuilder concatenator = new StringBuilder();
      for(int i = 0; i < outputStrings.Count; i++)
         concatenator.Append(outputStrings[i]);
      concatenator.ToString();
   }

Here is the resulting IL code for each loop:

  .locals init ([0] class
       [mscorlib]System.Text.StringBuilder concatenator, [1] int32 i)

  IL_000a:  ldloc.0 //load reference to StringBuilder
  IL_000b:  ldarg.1 //load ref to the ArrayList
  IL_000c:  ldloc.1 //load index int
  //get string from ArrayList
  IL_000d:  callvirt   instance object
[mscorlib]System.Collections.ArrayList::get_Item(int32

  //append string 
  IL_0012:  callvirt   instance class [mscorlib]System.Text.StringBuilder
[mscorlib]System.Text.StringBuilder::Append(object) 
  //pop reference to StringBuilder
  IL_0017:  pop   

More memory is initially allocated to the StringBuilder, but no new object is created as more and more strings are added. This answers the question of why StringBuilder is slower than String.Concat() for a small number of Append operations but is much faster for strings created in loops (see Table 1). So, if you're using ASP.NET to generate large documents, you might want to avoid the + operator or String.Concat().

In the next example, I'll take a look at how and when to use the for and foreach statements. Take a look at the for loop (see Listing 1). The foreach loop, on the other hand, produces surprisingly different IL code (see Listing 2). This is because the foreach statement—when not applied to basic arrays—creates an enumerator and adds its own try-finally codes and other overhead that can affect performance. In certain instances such as the infamous Windows.Forms.RichTextBox.Lines, however, foreach can be much faster. In this case, the foreach statement has a built-in assumption about how you intend to use it and can cut back on referencing the Lines property, which for large TextBoxes is a performance bottleneck.

You can try many other examples such as boxing and unboxing, using variables for conditionals in loops, or using StringBuilder outside a loop or even with string literals. Hopefully you now have a taste for why it's important to understand what goes on under the hood of the compiler in order to get the most out of your code.

About the Author
Godfrey Nolan is president of RIIS LLC, a consultancy based in Royal Oak, Mich., that specializes in optimizing and performance-tuning Web sites. He has given speeches at JavaOne, ASP Connections, Web Devcon, and VSLive!. He also has written dozens of articles for magazines and newspapers across the United States and Europe, and he is the author of Decompiling Java (Apress). He is currently working on a new book titled Decompiling .NET.



Back to top










Java Pro | Visual Studio Magazine | Windows Server System Magazine
.NET Magazine | Enterprise Architect | XML & Web Services Magazine
VSLive! | Thunder Lizard Events | Discussions | Newsletters | FTPOnline Home