Tech Off Thread

31 posts

Forum Read Only

This forum has been made read only by the site admins. No new threads or comments can be added.

C# string vs. StringBuilder

Back to Forum: Tech Off
  • User profile image
    travis

    How many concatenations would it take for StringBuilder to be more useful than a plain old string?

    I saw this article in purgatory on codeproject, this msdn blogs post, and this one too.

    Any experts want to give it a stab for those of us who don't have time to benchmark every function that concats a string?

    Thanks!

  • User profile image
    rmessier

    Here is my rule of  thumb, anything over like 256 bytes and I go with a stringbuilder, or a loop over more than 4 or 5...

    so,

    string cFoo = String.Empty;

        cFoo = "Hi";
        cFoo += "there";

    but I would use a stringuilder and NOT the += in the following example

    for (i=0; i<10; i++)
    {
        cFoo += String.Format("Hi {0}",i.ToString());  
    }

  • User profile image
    Dr. Shim

    That's interesting! Are StringBuilders faster for larger data?

  • User profile image
    ZippyV

    Yes. When you use a normal string and change it, the .net framework will destroy the current string in memory and create a new one somewhere else in the memory. With a stringbuilder the object isn't destroyed each time it's adapted.

  • User profile image
    gabe19

    As far as reasoning, MS docs suggest it is for use in loops.

    "The String object is immutable. Every time you use one of the methods in the System.String class, you create a new string object in memory, which requires a new allocation of space for that new object. In situations where you need to perform repeated modifications to a string, the overhead associated with creating a new String object can be costly. The System.Text.StringBuilder class can be used when you want to modify a string without creating a new object. For example, using the StringBuilder class can boost performance when concatenating many strings together in a loop."

    So, to answer the question, at what point is it more efficient, I wrote a little tester. The StringBuilder returned Sub-10ms times (DateTime not capable of registering < 10ms) at 1000 or fewer iterations of a loop simply appending the string "string". The += method of concatenation takes 15+ms for the same iterations. At 5000 iterations, the += method was becoming much slower at 218ms. versus the StringBuilder which was still sub 10ms.Even at 10000 iterations, the SB was sub 10, while the concat method had gone over 1.2 seconds. My test code is as follows:

    private void UpdateTime()

    {

    int iterations = Int32.Parse(txtIterations.Text);

    string theString = txtTheString.Text;

    DateTime strCall = DateTime.Now;

    string targetString = null;

    for(int x = 0 ; x < iterations ; x++)

    {

    targetString += theString;

    }

    TimeSpan time = (DateTime.Now - strCall);

    txtConcatTime.Text = time.TotalMilliseconds.ToString();

    //StringBuilder

    DateTime inCall = DateTime.Now;

    StringBuilder sb = new StringBuilder(theString);

    for(int x = 0 ; x < iterations ; x++)

    {

    sb.Append(theString);

    }

    time = (DateTime.Now - inCall);

    txtStringBTime.Text = time.TotalMilliseconds.ToString();

    MessageBox.Show("done");

    }

  • User profile image
    jonathanh

    There was a semi-spirited discussion of this very topic on some Microsoft blogs a while back Smiley

    I found the blog posts again using "site:blogs.msdn.com string stringbuilder" as a Google query.  

  • User profile image
    travis

    jonathanh wrote:
    There was a semi-spirited discussion of this very topic on some Microsoft blogs a while back Smiley

    I found the blog posts again using "site:blogs.msdn.com string stringbuilder" as a Google query.  

    Whoa, 83 results!  I'll start skimming and come back here if I'm still confused.  Thanks!

  • User profile image
    travis

    Well, maybe I'll just post my code.  I'm wondering if I should guess the approximate length of a stringbuilder when I create it, or just let it grow dynamically:

    System.Text.StringBuilder letterNavText = new System.Text.StringBuilder(1106);
    letterNavText.Append("<span>");
    for (int i = (int)'A'; i <= (int)'Z'; i++)
    {
     string letter = ((char)i).ToString();
     if (letter == SelectedLetter)
     {
      letterNavText.Append("<strong>");
      letterNavText.Append(letter);
      letterNavText.Append("</strong>");
     }
     else
     {
      letterNavText.Append("<a href=\"");
      letterNavText.Append(letter);
      letterNavText.Append(".htm\" title=\"");
      letterNavText.Append(letter);
      letterNavText.Append("\">");
      letterNavText.Append(letter);
      letterNavText.Append("</a>");
     }
     letterNavText.Append("&nbsp;&nbsp;");
    }
    letterNavText.Append("</span>");
    LetterNav.InnerHtml = letterNavText.ToString();

  • User profile image
    Skibum

    For reference...

    The StringBuilder's initial capacity is set to 16 by default.  The capacity then increases by an order of 2 whenever the current capacity is passed.  Therefore, if the String you append to the StringBuilder causes the capacity to expand, it will increase as seen below:

       StringBuilder sb = 
          new System.Text.StringBuilder();
       int capacity = sb.Capacity;

       for (int i=0; i<5000; i++)
       {
        sb.Append("a");
        if (capacity != sb.Capacity)
        {
         PrintLine("Capacity:  " + sb.Capacity);
        }
        capacity = sb.Capacity;
       }

    results in the following capacity adjustments:
    Capacity: 16
    Capacity: 32
    Capacity: 64
    Capacity: 128
    Capacity: 256
    Capacity: 512
    Capacity: 1024
    Capacity: 2048
    Capacity: 4096
    Capacity: 8192

  • User profile image
    MisterDonut

    One thing I do to create strings, for smaller ones, but still don't want to concatenate is this:

    String.Format("{0}{1}{2}{3}", string1, string2, string3);

    Substuting string1-3 with the characters of your choice.

    Seems to work pretty well.

  • User profile image
    bushidocoder

    MisterDonut wrote:
    One thing I do to create strings, for smaller ones, but still don't want to concatenate is this:

    String.Format("{0}{1}{2}{3}", string1, string2, string3);

    Substuting string1-3 with the characters of your choice.

    Seems to work pretty well.



    If you crack open String.Format, it creates a StringBuilder internally and uses StringBuilder.AppendFormat to accomplish its goal.

    Each call to String.Format results in the the allocation of an instance of StringBuilder - I've only looked at it for two minutes or so, but it appears that StringBuilder.AppendFormat doesn't attempt to estimate the size of the eventual output, so if you're just using it to append strings together, you could find out how large the output is supposed to be and use a StringBuffer with a predetermined capacity to do the appending.

    That said, in my own code, I just append the things together for code cleanliness and if that ever shows up in our profiling roadmaps, I'll come back and clean it up.  To date, for at least the work I do, inefficient string appending has been a nonfactor.

  • User profile image
    Tom Servo

    A StringBuilder does all its operations in a given block of allocated memory. E.g. if you add something to a string, it appends it at the marked end of string, in the same memory block. A regular string operation would allocate a new chunk and copy both strings into the new chunk. It's more work.

    However since the StringBuilder needs to be instanced and all that stuff, it's not efficient enough if you're always just quickly adding two strings together, as opposed to for example composing a whole HTML page.

  • User profile image
    kriskdf

    MisterDonut wrote:
    String.Format("{0}{1}{2}{3}", string1, string2, string3);




    For perf reasons, you should consider String.Concat(string1, string2, string3) instead of the formating.  If it is only called once in your program, it probably doesn't matter, but if you are doing something over and over again, it might be worth looking into the change. 

    String.Concat (if I remember correctly) sums up the lengths of all the strings, build a buffer, then places the contents into that buffer.  It only allocates what it needs (different than stringbuilder).

    If you are using string builder and you know how about how much space you will need, definitly instantiate it with a default capacity.

    I also encourage everyone to look at the Rotor source code to figure out how these things work.  It will help you make informed decisions.  But keep in mind that the Rotor source may be different than the actual MS implementation and the MS implementation could change at any time.

    Also, measure perf to make good decisions.

  • User profile image
    Maurits

    travis wrote:

     {
      letterNavText.Append("<a href=\"");
      letterNavText.Append(letter);
      letterNavText.Append(".htm\" title=\"");
      letterNavText.Append(letter);
      letterNavText.Append("\">");
      letterNavText.Append(letter);
      letterNavText.Append("</a>");
     }



    I think you can get away with
    letterNavText.Append(
        "<a href=\"" + letter + ".htm\" title=\"" + letter + "\">" + letter + "</a>"
    );

    because multiple string concatenations in a single statement are auto-optimized with an inline StringBuilder replacement.

  • User profile image
    tim2s

    Hello !

    I have got a Problem that is related to this topic.
    I have to copy Data from one Database to an Access db and make
    some computing during this transfer.

    There are quite a lot of records, estimated 40K per table with several tables.

    Until now i'm doing the following thing for the inser Operation:

    for(int i = 0; i < myDataList.Count; i++) {

    cmdText = "INSERT INTO TBLNAME (x,y,z) VALUES "( ";
    cmdText += xValue + ", " + yValue + ", " + zValue + " )";

    DbWriter.ExecuteNonReader(cmdText);

    }

    Where DbWriter is a class with static Functions for Capsulating the database connection.

    Do you think it would be more useful to use a StringBuilder in this case? I mean the text has to be resetted for every item in the List, so there will be a new StringBuilder Object for every row. Is this still faster?

    Best Regards,
      Tim


  • User profile image
    figuerres

    tim2s wrote:
    Hello !

    I have got a Problem that is related to this topic.
    I have to copy Data from one Database to an Access db and make
    some computing during this transfer.

    There are quite a lot of records, estimated 40K per table with several tables.

    Until now i'm doing the following thing for the inser Operation:

    for(int i = 0; i < myDataList.Count; i++) {

    cmdText = "INSERT INTO TBLNAME (x,y,z) VALUES "( ";
    cmdText += xValue + ", " + yValue + ", " + zValue + " )";

    DbWriter.ExecuteNonReader(cmdText);

    }

    Where DbWriter is a class with static Functions for Capsulating the database connection.

    Do you think it would be more useful to use a StringBuilder in this case? I mean the text has to be resetted for every item in the List, so there will be a new StringBuilder Object for every row. Is this still faster?

    Best Regards,
      Tim




    before the loop create a dbCommand and add a collection of Parameters.  then set the parameter values and call execute.
    you will remove a whole bunch of strings ....
     
    dbCommand Cmd = new dbCommand( "Insert (x,y,z) values(@Name,@name2..." );
    Parameter P1 = new Parameter( "@Name", dbtype);

    Cmd.Parameters.Add(P1);

    and so on then .. in the loop:

    for()
    {
    P1 =  value;
    P2 = other;
    P3 = "what";
    Cmd.ExecuteNonQuery();
    }

    no making strings in the loop, and no creating cmd's in a loop.
    this makes for nice clean code that runs fast.

  • User profile image
    Sven Groot

    If the thread is that old and your topic is only marginally related, start a new thread.

    Anyway, in your case I'd recommend using parameterized queries rather than doing string concatenation at all.

    Example:

    using( OleDbCommand cmd = connection.CreateCommand() )
    {
       cmd.CommandText = "INSERT INTO TBLNAME (x, y, z) VALUES (?, ?, ?)";
       cmd.Parameters.AddWithValue("x", xValue);
       cmd.Parameters.AddWithValue("y", yValue);
       cmd.Parameters.AddWithValue("z", zValue);
       cmd.ExecuteNonQuery();
    }


    Not only does it remove the need for string concatenation, it eliminates the risk of SQL injection as well.
  • User profile image
    odujosh

    Use a repeater control. Why are using making your own? Smiley

Conversation locked

This conversation has been locked by the site admins. No new comments can be made.