Is String.Format as efficient as StringBuilder
Asked Answered
P

12

165

Suppose I have a stringbuilder in C# that does this:

StringBuilder sb = new StringBuilder();
string cat = "cat";
sb.Append("the ").Append(cat).(" in the hat");
string s = sb.ToString();

would that be as efficient or any more efficient as having:

string cat = "cat";
string s = String.Format("The {0} in the hat", cat);

If so, why?

EDIT

After some interesting answers, I realised I probably should have been a little clearer in what I was asking. I wasn't so much asking for which was quicker at concatenating a string, but which is quicker at injecting one string into another.

In both cases above I want to inject one or more strings into the middle of a predefined template string.

Sorry for the confusion

Pfeffer answered 9/8, 2008 at 14:24 Comment(3)
Please leave these open to allow to future improvements.Eddington
In a special-case scenario, the quickest is neither of these: if the part to be replaced is equal in size to the new part, you can change the string in-place. Unfortunately, this requires reflection or unsafe code and deliberately violates the immutability of the string. Not a good practice, but if speed is an issue... :)Inexhaustible
in the example given above string s = "The "+cat+" in the hat"; might be the fastest unless it is used in a loop, in which case fastest will be with a StringBuilder initialised outside the loop.Nodical
P
148

NOTE: This answer was written when .NET 2.0 was the current version. This may no longer apply to later versions.

String.Format uses a StringBuilder internally:

public static string Format(IFormatProvider provider, string format, params object[] args)
{
    if ((format == null) || (args == null))
    {
        throw new ArgumentNullException((format == null) ? "format" : "args");
    }

    StringBuilder builder = new StringBuilder(format.Length + (args.Length * 8));
    builder.AppendFormat(provider, format, args);
    return builder.ToString();
}

The above code is a snippet from mscorlib, so the question becomes "is StringBuilder.Append() faster than StringBuilder.AppendFormat()"?

Without benchmarking I'd probably say that the code sample above would run more quickly using .Append(). But it's a guess, try benchmarking and/or profiling the two to get a proper comparison.

This chap, Jerry Dixon, did some benchmarking:

http://jdixon.dotnetdevelopersjournal.com/string_concatenation_stringbuilder_and_stringformat.htm

Updated:

Sadly the link above has since died. However there's still a copy on the Way Back Machine:

http://web.archive.org/web/20090417100252/http://jdixon.dotnetdevelopersjournal.com/string_concatenation_stringbuilder_and_stringformat.htm

At the end of the day it depends whether your string formatting is going to be called repetitively, i.e. you're doing some serious text processing over 100's of megabytes of text, or whether it's being called when a user clicks a button now and again. Unless you're doing some huge batch processing job I'd stick with String.Format, it aids code readability. If you suspect a perf bottleneck then stick a profiler on your code and see where it really is.

Physiognomy answered 9/8, 2008 at 15:57 Comment(2)
One problem with the benchmarks on Jerry Dixon's page is that he never calls .ToString() on the StringBuilder object. Over a great many iterations, that time makes a big difference, and means that he's not quite comparing apples to apples. That's the reason he shows such great performance for StringBuilder and probably accounts for his surprise. I just repeated the benchmark correcting that mistake and got the expected results: the String + operator was fastest, followed by StringBuilder, with String.Format bringing up the rear.Diesis
6 years later, this ain't quite so anymore. In Net4, string.Format() creates and caches a StringBuilder instance which it reuses, so it might in some test cases be faster than StringBuilder. I've put a revised benchmark in answer below (which still says that concat is fastest and for my test case, format is 10% slower than StringBuilder).Aracelis
C
45

From the MSDN documentation:

The performance of a concatenation operation for a String or StringBuilder object depends on how often a memory allocation occurs. A String concatenation operation always allocates memory, whereas a StringBuilder concatenation operation only allocates memory if the StringBuilder object buffer is too small to accommodate the new data. Consequently, the String class is preferable for a concatenation operation if a fixed number of String objects are concatenated. In that case, the individual concatenation operations might even be combined into a single operation by the compiler. A StringBuilder object is preferable for a concatenation operation if an arbitrary number of strings are concatenated; for example, if a loop concatenates a random number of strings of user input.

Catawba answered 9/8, 2008 at 14:36 Comment(0)
F
12

I ran some quick performance benchmarks, and for 100,000 operations averaged over 10 runs, the first method (String Builder) takes almost half the time of the second (String Format).

So, if this is infrequent, it doesn't matter. But if it is a common operation, then you may want to use the first method.

Fontanel answered 9/8, 2008 at 19:34 Comment(0)
H
10

I would expect String.Format to be slower - it has to parse the string and then concatenate it.

Couple of notes:

  • Format is the way to go for user-visible strings in professional applications; this avoids localization bugs
  • If you know the length of the resultant string beforehand, use the StringBuilder(Int32) constructor to predefine the capacity
Henden answered 9/8, 2008 at 17:9 Comment(0)
V
8

I think in most cases like this clarity, and not efficiency, should be your biggest concern. Unless you're crushing together tons of strings, or building something for a lower powered mobile device, this probably won't make much of a dent in your run speed.

I've found that, in cases where I'm building strings in a fairly linear fashion, either doing straight concatenations or using StringBuilder is your best option. I suggest this in cases where the majority of the string that you're building is dynamic. Since very little of the text is static, the most important thing is that it's clear where each piece of dynamic text is being put in case it needs updated in the future.

On the other hand, if you're talking about a big chunk of static text with two or three variables in it, even if it's a little less efficient, I think the clarity you gain from string.Format makes it worth it. I used this earlier this week when having to place one bit of dynamic text in the center of a 4 page document. It'll be easier to update that big chunk of text if its in one piece than having to update three pieces that you concatenate together.

V1 answered 9/8, 2008 at 14:40 Comment(1)
Yes! Use String.Format when it makes sense to do so, i.e. when you're formatting strings. Use string concatenation or a StringBuilder when you're performing mechanical concatenation. Always strive to pick the method that communicates your intention to the next maintainer.Teferi
A
8

If only because string.Format doesn't exactly do what you might think, here is a rerun of the tests 6 years later on Net45.

Concat is still fastest but really it's less than 30% difference. StringBuilder and Format differ by barely 5-10%. I got variations of 20% running the tests a few times.

Milliseconds, a million iterations:

  • Concatenation: 367
  • New stringBuilder for each key: 452
  • Cached StringBuilder: 419
  • string.Format: 475

The lesson I take away is that the performance difference is trivial and so it shouldn't stop you writing the simplest readable code you can. Which for my money is often but not always a + b + c.

const int iterations=1000000;
var keyprefix= this.GetType().FullName;
var maxkeylength=keyprefix + 1 + 1+ Math.Log10(iterations);
Console.WriteLine("KeyPrefix \"{0}\", Max Key Length {1}",keyprefix, maxkeylength);

var concatkeys= new string[iterations];
var stringbuilderkeys= new string[iterations];
var cachedsbkeys= new string[iterations];
var formatkeys= new string[iterations];

var stopwatch= new System.Diagnostics.Stopwatch();
Console.WriteLine("Concatenation:");
stopwatch.Start();

for(int i=0; i<iterations; i++){
    var key1= keyprefix+":" + i.ToString();
    concatkeys[i]=key1;
}

Console.WriteLine(stopwatch.ElapsedMilliseconds);

Console.WriteLine("New stringBuilder for each key:");
stopwatch.Restart();

for(int i=0; i<iterations; i++){
    var key2= new StringBuilder(keyprefix).Append(":").Append(i.ToString()).ToString();
    stringbuilderkeys[i]= key2;
}

Console.WriteLine(stopwatch.ElapsedMilliseconds);

Console.WriteLine("Cached StringBuilder:");
var cachedSB= new StringBuilder(maxkeylength);
stopwatch.Restart();

for(int i=0; i<iterations; i++){
    var key2b= cachedSB.Clear().Append(keyprefix).Append(":").Append(i.ToString()).ToString();
    cachedsbkeys[i]= key2b;
}

Console.WriteLine(stopwatch.ElapsedMilliseconds);

Console.WriteLine("string.Format");
stopwatch.Restart();

for(int i=0; i<iterations; i++){
    var key3= string.Format("{0}:{1}", keyprefix,i.ToString());
    formatkeys[i]= key3;
}

Console.WriteLine(stopwatch.ElapsedMilliseconds);

var referToTheComputedValuesSoCompilerCantOptimiseTheLoopsAway= concatkeys.Union(stringbuilderkeys).Union(cachedsbkeys).Union(formatkeys).LastOrDefault(x=>x[1]=='-');
Console.WriteLine(referToTheComputedValuesSoCompilerCantOptimiseTheLoopsAway);
Aracelis answered 12/1, 2015 at 12:52 Comment(1)
By "string.Format doesn't exactly do what you might think" I mean that in the 4.5 source code it tries to create and re-use a cached StringBuilder instance. So I included that approach in the testAracelis
P
7

String.Format uses StringBuilder internally, so logically that leads to the idea that it would be a little less performant due to more overhead. However, a simple string concatenation is the fastest method of injecting one string between two others, by a significant degree. This evidence was demonstrated by Rico Mariani in his very first Performance Quiz, years ago. Simple fact is that concatenations, when the number of string parts is known (without limitation — you could concatenate a thousand parts, as long as you know it's always 1000 parts), are always faster than StringBuilder or String.Format. They can be performed with a single memory allocation and a series of memory copies. Here is the proof.

And here is the actual code for some String.Concat methods, which ultimately call FillStringChecked, which uses pointers to copy memory (extracted via Reflector):

public static string Concat(params string[] values)
{
    int totalLength = 0;

    if (values == null)
    {
        throw new ArgumentNullException("values");
    }

    string[] strArray = new string[values.Length];

    for (int i = 0; i < values.Length; i++)
    {
        string str = values[i];
        strArray[i] = (str == null) ? Empty : str;
        totalLength += strArray[i].Length;

        if (totalLength < 0)
        {
            throw new OutOfMemoryException();
        }
    }

    return ConcatArray(strArray, totalLength);
}

public static string Concat(string str0, string str1, string str2, string str3)
{
    if (((str0 == null) && (str1 == null)) && ((str2 == null) && (str3 == null)))
    {
        return Empty;
    }

    if (str0 == null)
    {
        str0 = Empty;
    }

    if (str1 == null)
    {
        str1 = Empty;
    }

    if (str2 == null)
    {
        str2 = Empty;
    }

    if (str3 == null)
    {
        str3 = Empty;
    }

    int length = ((str0.Length + str1.Length) + str2.Length) + str3.Length;
    string dest = FastAllocateString(length);
    FillStringChecked(dest, 0, str0);
    FillStringChecked(dest, str0.Length, str1);
    FillStringChecked(dest, str0.Length + str1.Length, str2);
    FillStringChecked(dest, (str0.Length + str1.Length) + str2.Length, str3);
    return dest;
}

private static string ConcatArray(string[] values, int totalLength)
{
    string dest = FastAllocateString(totalLength);
    int destPos = 0;

    for (int i = 0; i < values.Length; i++)
    {
        FillStringChecked(dest, destPos, values[i]);
        destPos += values[i].Length;
    }

    return dest;
}

private static unsafe void FillStringChecked(string dest, int destPos, string src)
{
    int length = src.Length;

    if (length > (dest.Length - destPos))
    {
        throw new IndexOutOfRangeException();
    }

    fixed (char* chRef = &dest.m_firstChar)
    {
        fixed (char* chRef2 = &src.m_firstChar)
        {
            wstrcpy(chRef + destPos, chRef2, length);
        }
    }
}

So, then:

string what = "cat";
string inthehat = "The " + what + " in the hat!";

Enjoy!

Portmanteau answered 23/5, 2009 at 21:33 Comment(1)
in Net4, string.Format caches and reuses a StringBuilder instance so may in some usages be faster.Aracelis
F
3

Oh also, the fastest would be:

string cat = "cat";
string s = "The " + cat + " in the hat";
Fontanel answered 9/8, 2008 at 19:51 Comment(3)
no, string concatenation is extremely slow, because .NET creates extra copies of your string variables between the concat operations, in this case: two extra copies plus the final copy for the assignment. Result: extremely poor performance compared to StringBuilder which is made to optimize this type of coding in the first place.Inexhaustible
@Abel: The answer might be lacking details, but this approach IS the fastest option, in this particular example. The compiler will transform this into a single String.Concat() call, so replacing with a StringBuilder will actually slow down the code.Chlor
@Fontanel is correct: in this case, concatenation is the fastest. Of course, the difference would be insignificant unless repeated a great many times, or perhaps operated over a much, much larger string.Diesis
O
0

It really depends. For small strings with few concatenations, it's actually faster just to append the strings.

String s = "String A" + "String B";

But for larger string (very very large strings), it's then more efficient to use StringBuilder.

Oratory answered 9/8, 2008 at 14:31 Comment(0)
L
0

In both cases above I want to inject one or more strings into the middle of a predefined template string.

In which case, I would suggest String.Format is the quickest because it is design for that exact purpose.

Legalism answered 9/8, 2008 at 16:29 Comment(0)
G
0

It really depends on your usage pattern.
A detailed benchmark between string.Join, string,Concat and string.Format can be found here: String.Format Isn't Suitable for Intensive Logging

Geist answered 1/8, 2010 at 18:40 Comment(0)
L
-1

I would suggest not, since String.Format was not designed for concatenation, it was design for formatting the output of various inputs such as a date.

String s = String.Format("Today is {0:dd-MMM-yyyy}.", DateTime.Today);
Legalism answered 9/8, 2008 at 14:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.