-1

According to the following unit test methods, StringBuilder is far slower than String.Replace, how come every one saying StringBuilder is faster? Am I missing something?

[TestMethod]
public void StringReplace()
{
    DateTime date = DateTime.Now;
    string template = File.ReadAllText("file.txt");
    for (int i = 0; i < 100000; i++)
    {
        template = template.Replace("cat", "book" );
        template = template.Replace("book", "cat"); 
    }
    Assert.Fail((DateTime.Now - date).Milliseconds.ToString()); 
}

[TestMethod]
public void StringBuilder()
{
    DateTime date = DateTime.Now;
    StringBuilder template = new StringBuilder(File.ReadAllText("file.txt"));
    for (int i = 0; i < 100000; i++)
    {
        template.Replace("cat", "book");
        template.Replace("book", "cat"); 
    }
    Assert.Fail((DateTime.Now - date).Milliseconds.ToString());
}

Here is the result:

StringReplace - 335ms

StringBuilder - 799ms

14
  • 1
    might help to show your output Commented Aug 20, 2013 at 18:41
  • 1
    If that's your actual code, you are not testing on the same file. Commented Aug 20, 2013 at 18:42
  • 3
    Also, don't time things using DateTime. There's a Stopwatch class that's good for this sort of thing. And (for completeness sake) you might want to rephrase your question to sound less argumentative. Commented Aug 20, 2013 at 18:43
  • 3
    That benchmark isn't even correct. You're testing loading the file plus replacing the string when you really just want to test the latter. Commented Aug 20, 2013 at 18:43
  • 7
    This is not the use case where StringBuilder is faster - you are just creating new strings of exactly the same length, instead of slowly growing one by appending more pieces. Commented Aug 20, 2013 at 18:44

4 Answers 4

8

StringBuilder is faster at building strings. Replacing is a different concern.

Eg, the following example code:

    [TestMethod]
    public void StringConcat()
    {
        var start = DateTime.Now;

        var s = string.Empty;
        for (int i = 0; i < 100000; i++)
        {
            s += "cat";
        }
        Assert.Fail((DateTime.Now - start).TotalMilliseconds.ToString()); 
    }

    [TestMethod]
    public void StringBuilder()
    {
        var start = DateTime.Now;

        var sb = new StringBuilder();
        for (int i = 0; i < 100000; i++)
        { 
            sb.Append("cat");
        }
        Assert.Fail((DateTime.Now - start).TotalMilliseconds.ToString()); 
    }

For me, I get 14,645ms (14.6 seconds) and 2ms respectively.

Sign up to request clarification or add additional context in comments.

3 Comments

So stringBuilder is not string heavent as some show it , String.Replace will do better when it is pure replacing process
But how often do you really concatenate 100 000 small strings together? Jeff Atwood has something to say about string concatenation
100 000 the number is experimental
3

According to several tests (links to more tests at the bottom) as well as a quick and sloppy test of my own, String.Replace performs better than StringBuilder.Replace. You do not seem to be missing anything.

For completeness sake, here's my testing code:

int big = 500;
String s;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; ++i)
{
    sb.Append("cat mouse");
}
s = sb.ToString();

Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < big; ++i)
{ 
    s = s.Replace("cat", "moo"); 
    s = s.Replace("moo", "cat"); 
}
sw.Stop(); Trace.WriteLine(sw.ElapsedMilliseconds); sw.Reset(); sw.Start();
for (int i = 0; i < big; ++i)
{
    sb.Replace("cat", "moo");
    sb.Replace("moo", "cat");
}
sw.Stop(); Trace.WriteLine(sw.ElapsedMilliseconds); sw.Reset(); sw.Start();
for (int i = 0; i < big; ++i)
{
    s = s.Replace("cat", "mooo");
    s = s.Replace("mooo", "cat");
}
sw.Stop(); Trace.WriteLine(sw.ElapsedMilliseconds); sw.Reset(); sw.Start();
for (int i = 0; i < big; ++i)
{
    sb.Replace("cat", "mooo");
    sb.Replace("mooo", "cat");
}
sw.Stop(); Trace.WriteLine(sw.ElapsedMilliseconds);

The output, on my machine, is:

9
11
7
1977

[EDIT]

I missed one very important case. That is the case where every time the string is replaced with something else. This could matter because of the way C# handles strings. What follows is the code that tests the missing case, and the results on my system.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
class Program
{
    static void Main()
    {
        var repl = GenerateRandomStrings(4, 500);
        String s;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 100; ++i)
        {
            sb.Append("cat mouse");
        }
        s = sb.ToString();
        Stopwatch sw = new Stopwatch();
        sw.Start();
        foreach (string str in repl)
        {
            s = s.Replace("cat", str);
            s = s.Replace(str, "cat");
        }
        sw.Stop(); Trace.WriteLine(sw.ElapsedMilliseconds); sw.Reset(); sw.Start();
        foreach (string str in repl)
        {
            sb.Replace("cat", str);
            sb.Replace(str, "cat");
        }
        sw.Stop(); Trace.WriteLine(sw.ElapsedMilliseconds);
    }

    static HashSet<string> GenerateRandomStrings(int length, int amount)
    {
        HashSet<string> strings = new HashSet<string>();
        while (strings.Count < amount)
            strings.Add(RandomString(length));           
        return strings;
    }

    static Random rnd = new Random();
    static string RandomString(int length)
    {
        StringBuilder b = new StringBuilder();
        for (int i = 0; i < length; ++i)
            b.Append(Convert.ToChar(rnd.Next(97, 122)));
        return b.ToString();
    }
}

Output:

8
1933

However, as we start to increase the length of the random strings, the StringBuilder solution comes closer and closer to the String solution. For random strings with a length of 1000 characters, my results are

138
328

Using this new knowledge on the old tests, I get similar results when increasing the length of the string to replace with. When replacing with a string that is a thousand 'a' characters instead of "mooo", my results for the original answer become:

8
11
160
326

Although the results do become closer, it still seems that for any real world use, String.Replace beats StringBuilder.Replace.

7 Comments

yeah that is what seem to be , hope other developers notice that too
I missed the case where the string we're replacing with is not always the same. I edited the answer accordingly. Also, it turns out that when replacements become longer, StringBuilder comes closer to String. I added that to the answer as well.
Is there any way to check which one consume more memory ?
A quick and free way to do that would be to use CLR Profiler. There are also questions right here on Stack Overflow.
@BartvanNierop, What .net version did you test? The internal implementation changed between 2 and 4. In .net 2 there is a single string buffer which means replace could be around the same speed. In .net 4 there are multiple string arrays which means replacing across arrays would be very slow compared to replacing in a single string.
|
0

StringBuilder is fastern to concatenate strings:

"abc" + "cde" implies creating a new string "abccde" copying all information "abc" and "cde" to the new string

With stringbuilder you concatenate "abc" with "cde" just adding the text, so there is no need to create a new class and copy all information

The replace function need a string to work, so each iteration of stringbuilder have to create the string, replace, and regenerate the stringbuilder class

7 Comments

SB is faster where the number of strings being concatted isn't known at compile time. "abc" + "cde" will result in concatenation at compile time and thus will be faster than using a SB.
But all the C# developers community agree that StringBuilder is faster for replacing string.you just need to google to see how many articles about that. I have now real project and doesnt seem to see it really doing better than String.Replace
@sino But all the C# developers community agree <citation needed>
"abc" + "cde" is an example, agree with it's linked in compiled time, I just want to expose a + b where a is a string and b is a string. And string is fastern than stringbuilder doing replaces: blogs.msdn.com/b/debuggingtoolbox/archive/2008/04/02/…
"But all the C# developers community agree that StringBuilder is faster for replacing string." I've never heard such a claim. This hardly constitutes "all the C# developers".
|
-3

StringBuilder is faster for 2+ strings. When you only need to concatenate 2 strings, actually doing + is faster.

9 Comments

+ is only defined for two strings. You can't concatenate three or more strings with +.
you are saying you can't do "asdf" + "foo" + "boo"?
That is two uses of +. Each + is a function with two parameters. It is the same as writing ("asdf" + "foo") + "boo"
ok, so i am still correct and ppl who downvoted me did not look at assembly for doing StringBuilder vs. String concatenation using +. If they did, they would know that i am right. please do your homework, ppl
You're being downvoted because your "answer" doesn't address the actual question, which involves string replacement. And as I said, + concatenation doesn't even exist for three or more strings, so it isn't faster. Do you understand that + concatenation allocates a new string every time? In your example, five strings will be allocated. + might be fast for short strings, but long ones will take longer to allocate, and every allocation means a future garbage collection.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.