You want to reduce the time spent concatenating strings in an application that performs this operation repeatedly.
Concatenate strings with a StringBuilder
object
instead of the classic &
and
+
concatenation operators.
Example 16-4 through Example 16-6 show the .aspx
file and
the VB and C# code-behind files for our application that demonstrates
the performance difference between using the classic string operators
and a StringBuilder
object to perform
concatenation. Our example concatenates two strings repeatedly, and
then calculates the average time per concatenation for the two
approaches. The output of the application is shown in Figure 16-1.
In the common language runtime (CLR), strings are immutable, which
means that once they have been created they cannot be changed. If you
concatenate the two strings, str1
and
str2
, shown in the following code fragment, the
resulting value of str1
is
“1234567890”.
str1 = "12345" str2 = "67890" str1 = str1 & str2 str1 = "12345"; str2 = "67890"; str1 = str1 + str2;
The way in which this concatenation is accomplished may come as a bit
of a surprise to you. Since str1
cannot be changed
(it is immutable), it is actually disposed of and a new string
str1
is created that contains the concatenation of
str1
and str2
. As you might
expect, there is a lot of overhead associated with this operation.
The StringBuilder
object provides a much faster
method of concatenating strings. A StringBuilder
object treats strings as an array of characters that can be altered
without recreating the object. When a
StringBuilder
object is created, the CLR allocates
a block of memory in which to store the string. As characters are
added to a StringBuilder
object, they are stored
in the already available block of memory. If the additional
characters will not fit within the current block, additional memory
is allocated to store the new data.
The default capacity of a StringBuilder
is 16
characters, but the number can be set to any value up to
2,147,483,647 characters, the maximum size of an integer type. If you
know approximately how long the final string will be, you can improve
performance further by setting the maximum size when the
StringBuilder
is created, which reduces the number
of additional memory allocations that must be performed.
As Figure 16-1 shows, the performance difference
between classic concatenation and concatenation using a
StringBuilder
object is dramatic. Classic
concatenation averaged 1.0188 milliseconds per concatenation, while
using a StringBuilder
object averaged 0.0003
milliseconds per concatenation, which is nearly 3,000 times faster.
StringBuilder
objects are not limited to
concatenation: they also support inserting, removing, replacing, and
appending formatted strings. The StringBuilder
is
a significant improvement over the classic manipulation of strings.
The question always arises as to when classic string manipulation or
a StringBuilder
should be used. Every application
is different. However, if you are performing a simple concatenation
of less than 5-10 strings outside of a loop (done only once), you
should probably use classic string manipulation due to the overhead
of creating a StringBuilder
object. Anytime you
are performing string manipulations within a loop or are combining
many string fragments, a StringBuilder
should be
used. If you are not sure, set up a test similar to the one we use in
our examples for this recipe and measure the two approaches yourself.
In the MSDN Library, search for “Use StringBuilder for Complex String Manipulation”
Example 16-4. Measuring string concatenation performance (.aspx)
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="CH16StringManipulationPerformanceVB.aspx.vb" Inherits="ASPNetCookbook.VBExamples.CH16StringManipulationPerformanceVB" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>String Concatenation Performance</title> <link rel="stylesheet" href="css/ASPNetCookbook.css"> </head> <body leftmargin="0" marginheight="0" marginwidth="0" topmargin="0"> <form id="frmStringPerformance" method="post" runat="server"> <table width="100%" cellpadding="0" cellspacing="0" border="0"> <tr> <td align="center"> <img src="images/ASPNETCookbookHeading_blue.gif"> </td> </tr> <tr> <td class="dividerLine"> <img src="images/spacer.gif" height="6" border="0"></td> </tr> </table> <table width="90%" align="center" border="0"> <tr> <td align="center"> </td> </tr> <tr> <td align="center" class="PageHeading"> Improving String Concatenation Performance (VB) </td> </tr> <tr> <td><img src="images/spacer.gif" height="10" border="0"></td> </tr> <tr> <td align="center"> <table width="70%" align="center" border="0"> <tr> <td> </td> <td align="center">Time Per Concatenation (mSec)</td> </tr> <tr><td>Classic Concatentation</td>
<td id="cellClassic" runat="server" align="center"></td>
</tr> <tr><td>Using StringBuilder</td>
<td id="cellSB" runat="server" align="center"></td>
</tr> </table> </td> </tr> </table> </form> </body> </html>
Example 16-5. Measuring string concatenation performance code-behind (.vb)
Option Explicit On Option Strict On '----------------------------------------------------------------------------- ' ' Module Name: CH16StringManipulationPerformanceVB.aspx.vb ' ' Description: This module provides the code behind for the ' CH16StringManipulationPerformanceVB.aspx page '***************************************************************************** Imports System Imports System.Text Namespace ASPNetCookbook.VBExamples Public Class CH16StringManipulationPerformanceVB Inherits System.Web.UI.Page 'controls on the form Protected cellClassic As System.Web.UI.HtmlControls.HtmlTableCell Protected cellSB As System.Web.UI.HtmlControls.HtmlTableCell '************************************************************************* ' ' ROUTINE: Page_Load ' ' DESCRIPTION: This routine provides the event handler for the page load ' event. It is responsible for initializing the controls ' on the page. '------------------------------------------------------------------------- Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.LoadConst STRING_SECTION As String = "1234567890"
Dim testStr As String
Dim testStrBuilder As StringBuilder
Dim counter As Integer
Dim startTime As DateTime
Dim elapsedTime As TimeSpan
Dim loops As Integer
'measure the elapsed time for 10000 classic string concatenation
loops = 10000
startTime = DateTime.Now( )
testStr = ""
For counter = 1 To loops
testStr &= STRING_SECTION
Next
elapsedTime = DateTime.Now.Subtract(startTime)
'set the table cell value to the average time per concatenation
'in milliseconds
cellClassic.InnerText = _
(elapsedTime.TotalMilliseconds / loops).ToString("0.0000")
'measure the elapsed time for 1,000,000 classic string concatenation
'NOTE: Many more loops were used to provide a measureable time period
loops = 1000000
startTime = DateTime.Now( )
testStrBuilder = New StringBuilder
For counter = 1 To loops
testStrBuilder.Append(STRING_SECTION)
Next
elapsedTime = DateTime.Now.Subtract(startTime)
'set the table cell value to the average time per concatenation
'in milliseconds
cellSB.InnerText = _
(elapsedTime.TotalMilliseconds / loops).ToString("0.0000")
End Sub 'Page_Load End Class 'CH16StringManipulationPerformanceVB End Namespace
Example 16-6. Measuring string concatenation performance code-behind (.cs)
//---------------------------------------------------------------------------- // // Module Name: CH16StringManipulationPerformanceCS.aspx.cs // // Description: This class provides the code behind for // CH16StringManipulationPerformanceCS.aspx // //**************************************************************************** using System; using System.Text; namespace ASPNetCookbook.CSExamples { public class CH16StringManipulationPerformanceCS : System.Web.UI.Page { // controls on the form protected System.Web.UI.HtmlControls.HtmlTableCell cellClassic; protected System.Web.UI.HtmlControls.HtmlTableCell cellSB; //************************************************************************ // // ROUTINE: Page_Load // // DESCRIPTION: This routine provides the event handler for the page // load event. It is responsible for initializing the // controls on the page. //------------------------------------------------------------------------ private void Page_Load(object sender, System.EventArgs e) {const string STRING_SECTION = "1234567890";
string testStr = null;
StringBuilder testStrBuilder = null;
DateTime startTime;
TimeSpan elapsedTime;
int counter;
int loops;
// measure the elapsed time for 10000 classic string concatenation
loops = 10000;
startTime = DateTime.Now;
testStr = "";
for (counter = 1; counter <= loops; counter++)
{
testStr += STRING_SECTION;
}
elapsedTime = DateTime.Now.Subtract(startTime);
// set the table cell value to the average time per concatenation
// in milliseconds
cellClassic.InnerText =
(elapsedTime.TotalMilliseconds / loops).ToString("0.0000");
// measure the elapsed time for 1,000,000 classic string concatenation
// NOTE: Many more loops were used to provide a measureable time period
loops = 1000000;
startTime = DateTime.Now;
testStrBuilder = new StringBuilder( );
for (counter = 1; counter <= loops; counter++)
{
testStrBuilder.Append(STRING_SECTION);
}
elapsedTime = DateTime.Now.Subtract(startTime);
// set the table cell value to the average time per concatenation
// in milliseconds
cellSB.InnerText =
(elapsedTime.TotalMilliseconds / loops).ToString("0.0000");
} // Page_Load } // CH16StringManipulationPerformanceCS }