Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

StringBuilder vs. String performance

3 views
Skip to first unread message

Richard Lewis Haggard

unread,
Apr 20, 2006, 6:22:34 PM4/20/06
to
I thought that the whole point of StringBuilder was that it was supposed to
be a faster way of building strings than string. However, I just put
together a simple little application to do a comparative analysis between
the two and, surprisingly, string seems to out perform StringBuilder by a
significant amount. A string concatenation takes not quite twice as long
using StringBuilder than it does with a string. This doesn't sound right to
me. Does anyone else have any hard performance data experience on the
subject?

Here's what I did- I made a simple little app that starts two worker
threads, one uses string to concatenate string and the other uses
StringBuilder. Once a second, each of the worker threads reports the number
of concatenations it has performed to the UI which displays them. The string
thread generally runs at 3 million a second. The StringBuilder turns in
about 1.8 million. Does this run counter or agree with your experience?
--
Richard Lewis Haggard
www.Haggard-And-Associates.com


Bob Grommes

unread,
Apr 20, 2006, 6:35:49 PM4/20/06
to
You'd have to share the code in order for anyone to make an accurate
assessment.

However, my experience is that there's a certain amount of overhead in
newing up a StringBuilder and getting the string back out of it when
you're done. So you have to be doing enough concatenation to overcome
that. For example, if you use a StringBuilder to build an HTML page
from a thousand bits, that will be much faster than repeated string
concatenations. At the other extreme if you simply want to append
something once then don't bother with StringBuilder. The crossover
point will vary somewhat, but a good rule of thumb is that string
concatenation is fine for 3 or fewer concatenations; consider a
StringBuilder if you're doing more.

As a final thought, I would not test the raw performance of a specific
feature while mixing in the complication of threading issues. It's apt
to muddy the waters too much.

--Bob

Jon Skeet [C# MVP]

unread,
Apr 20, 2006, 6:49:00 PM4/20/06
to

It agrees with my experience of joining two strings together, and runs
counter to my experience of joining hundreds of strings together.

Could you share your code with us?

I would say, by the way, that multi-threading a benchmark sounds like a
really bad idea (unless it's actually testing threading). Better is to
give as much of the machine as possible to a single task, time that,
and then do the same for a different task. It sounds like you're
trusting the scheduler to be fair, for starters...

--
Jon Skeet - <sk...@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Michael Nemtsev

unread,
Apr 21, 2006, 3:37:01 AM4/21/06
to
The advantage of StringBuilder take place in appending large number of strings.
Difference became visible in appending about 35.000 strings and more. See
code below.

The results are that (release version, P4 2.8GB):
# of appends: 35.000
String + time: 1,42 sec
StringBuilder time: 0 sec

# of appends: 100.000
String + time: 13,961 sec
StringBuilder time: 0.01 sec

# of appends: 200.000
String + time: 72,264 sec
StringBuilder time: 0.01 sec


===[ CODE ]===================================
Console.Write("Enter number of strings to append:");
int count = Convert.ToInt32(Console.ReadLine());

string str = null;
int startTick = Environment.TickCount;
for (int i = 0; i < count; i++)
{
str = str + ".";
}
int endTick = Environment.TickCount;

double timeTakenByPlus =
(endTick - startTick) / 1000.0;

System.Text.StringBuilder bldr =
new System.Text.StringBuilder();

startTick = Environment.TickCount;
for (int i = 0; i < count; i++)
{
bldr.Append(".");
}
endTick = Environment.TickCount;

double timeTakenByStringBuilder =
(endTick - startTick) / 1000.0;

Console.Write("+ and StringBuilder took ");
Console.WriteLine("{0} and {1} seconds",
timeTakenByPlus,
timeTakenByStringBuilder);

============================


--
WBR,
Michael Nemtsev :: blog: http://spaces.msn.com/laflour

"At times one remains faithful to a cause only because its opponents do not
cease to be insipid." (c) Friedrich Nietzsche

Kevin Spencer

unread,
Apr 21, 2006, 6:17:53 AM4/21/06
to
The whole point of StringBuilder is to save memory, not increase speed.

--
HTH,

Kevin Spencer
Microsoft MVP
Professional Numbskull

Hard work is a medication for which
there is no placebo.

"Richard Lewis Haggard" <HaggardAtWorldDotStdDotCom> wrote in message
news:OAVAdkMZ...@TK2MSFTNGP04.phx.gbl...

Jon Skeet [C# MVP]

unread,
Apr 21, 2006, 6:27:33 AM4/21/06
to
Kevin Spencer wrote:
> The whole point of StringBuilder is to save memory, not increase speed.

I don't see that being true. The speed improvements can be staggering,
and I see no reason to dismiss them as not being part of the point of
StringBuilder.

Jon

Kevin Spencer

unread,
Apr 21, 2006, 12:02:13 PM4/21/06
to
Hi Jon,

I should have said "the most important point." I was quoting the previous
message. The predominant problems that occur with strings are centered
around memory accumulation. Speed is certainly a consideration, but not
likely to bring the app to a crashing halt, and not as much of an issue
unless there is a heck of a lot of string-building going on, in which case
memory is more likely to cause real problems.

--
HTH,

Kevin Spencer
Microsoft MVP
Professional Numbskull

Hard work is a medication for which
there is no placebo.

"Jon Skeet [C# MVP]" <sk...@pobox.com> wrote in message
news:1145615253.2...@u72g2000cwu.googlegroups.com...

Richard Lewis Haggard

unread,
Apr 21, 2006, 12:36:46 PM4/21/06
to
I'm going to gloss over the details in how the worker threads got started or
how they communicate their results back to the UI. If anyone cares, I'll put
this all up on my web site plus the executable itself. Here's the
simplified worker thread itself. The same code is used for both string and
StringBuilder. The only difference is in the m_iID member. It can be a 0 or
a 1. If 0 then string concatenation takes place. Otherwise, StringBuilder is
used.

I tried a number of different ways to start with an empty StringBuilder.
Both removing the complete contents and creating a new StringBuilder each
time seemed to take about the same amount of time but, surprisingly, simply
setting the StringBuilder's Length to 0 to initialize the buffer contents to
empty ate up significantly more time.

private void WorkerThread()
{
// Let the world know we're running.
m_bRunning = true;
UpdateStatus( 0 );
try
{
string s;
StringBuilder sb = null;
// Generate an initial '1 second' limit.
int iEndTick = Environment.TickCount + 1000;
// This is the primary loop.
for (int iLoopCount=1; m_bRunning; iLoopCount++)
{
// If ID is zero then do the concatenation
// using string. Otherwise, use StringBuilder.
if ( (m_iID == 0))
{
s = "First";
s += "Second";
s += "Third";
s += "Fourth";
s += "Fifth";
s += "Sixth";
s += "Seventh";
s += "Eigthth";
s += "Nineth";
s += "Tenth";
}
else
{
sb = new StringBuilder();
sb.Append("First");
sb.Append("Second");
sb.Append("Third");
sb.Append("Fourth");
sb.Append("Fifth");
sb.Append("Sixth");
sb.Append("Seventh");
sb.Append("Eigthth");
sb.Append("Nineth");
sb.Append("Tenth");
s = sb.ToString();
}
// If the loop has been running for a second then
// update the UI status for this worker.
if ( iEndTick < Environment.TickCount )
{
UpdateStatus( iLoopCount * 10 );
iLoopCount = 0;
iEndTick = Environment.TickCount + 1000;
}
}
}
catch
{
}
finally
{
m_bRunning = false;
}
}

--
Richard Lewis Haggard
www.Haggard-And-Associates.com

"Bob Grommes" <b...@bobgrommes.com> wrote in message
news:%23qxnQrM...@TK2MSFTNGP04.phx.gbl...

Jon Skeet [C# MVP]

unread,
Apr 21, 2006, 2:20:57 PM4/21/06
to
Kevin Spencer <ke...@DIESPAMMERSDIEtakempis.com> wrote:
> I should have said "the most important point." I was quoting the previous
> message. The predominant problems that occur with strings are centered
> around memory accumulation. Speed is certainly a consideration, but not
> likely to bring the app to a crashing halt, and not as much of an issue
> unless there is a heck of a lot of string-building going on, in which case
> memory is more likely to cause real problems.

Using string concatenation badly will cause a lot of GC work, but it
shouldn't actually bring the app to a crashing halt. With the typical
"append in a loop" which is what StringBuilder is usually the cure for,
each string is eligible for GC after a single extra iteration of the
loop, so although lots of gen0 strings are created, it shouldn't be an
issue. If the strings go into the large object heap, that's a different
matter - but then StringBuilder will have a similar problem, just less
pronounced.

Pretty much every question I've answered with a reply of "use
StringBuilder instead" has been due to someone complaining about speed,
not memory.

Jon Skeet [C# MVP]

unread,
Apr 21, 2006, 2:33:01 PM4/21/06
to
Michael Nemtsev <Michael...@discussions.microsoft.com> wrote:
> The advantage of StringBuilder take place in appending large number of strings.
> Difference became visible in appending about 35.000 strings and more. See
> code below.

Differences become visible much earlier than that if you do it often
enough (or with large enough strings). See the example I've just posted
with the ten strings Richard used.

This morning I had an example where just 4 concatenations still made
StringBuilder "win".

I agree that it becomes more pronounced the more strings are appended,
however.

Jon Skeet [C# MVP]

unread,
Apr 21, 2006, 2:30:50 PM4/21/06
to
<"Richard Lewis Haggard" <HaggardAtWorldDotStdDotCom>> wrote:
> I'm going to gloss over the details in how the worker threads got started or
> how they communicate their results back to the UI.

Why have a UI at all? For microbenchmarks like this, the only things a
UI and extra worker threads are likely to do is skew the results in
unpredictable ways.

Below is a short but complete console app which performs the same
string concatenations as your code - but with an extra usage of the
string to prevent possible compiler optimisations from removing all the
concatenation in the first place.

The results on my box are:
Concat: 00:00:09.8593750
StringBuilder: 00:00:06.4218750

(That's with .NET 2.0 - .NET 1.1 makes each of them a bit slower, but
StringBuilder still wins comfortably.)


using System;
using System.Text;

class Test
{
const int Iterations=10000000;
const int CorrectLength = 57;

static void Main()
{
TestConcatenation();
TestStringBuilder();
}

static void TestConcatenation()
{
DateTime start = DateTime.Now;
for (int i=0; i < Iterations; i++)
{
string s = "First";


s += "Second";
s += "Third";
s += "Fourth";
s += "Fifth";
s += "Sixth";
s += "Seventh";
s += "Eigthth";
s += "Nineth";
s += "Tenth";

if (s.Length != CorrectLength)
{
throw new Exception ("Incorrect length: "+s.Length);
}
}
DateTime end = DateTime.Now;
Console.WriteLine ("Concat: {0}", end-start);
}


static void TestStringBuilder()
{
DateTime start = DateTime.Now;
for (int i=0; i < Iterations; i++)
{
StringBuilder sb = new StringBuilder();


sb.Append("First");
sb.Append("Second");
sb.Append("Third");
sb.Append("Fourth");
sb.Append("Fifth");
sb.Append("Sixth");
sb.Append("Seventh");
sb.Append("Eigthth");
sb.Append("Nineth");
sb.Append("Tenth");

string s = sb.ToString();
if (s.Length != CorrectLength)
{
throw new Exception ("Incorrect length: "+s.Length);
}
}
DateTime end = DateTime.Now;
Console.WriteLine ("StringBuilder: {0}", end-start);

Kevin Spencer

unread,
Apr 21, 2006, 5:47:18 PM4/21/06
to
> Pretty much every question I've answered with a reply of "use
> StringBuilder instead" has been due to someone complaining about speed,
> not memory.

Well that's a reflection of what people are asking about. Many people are
overly concerned about speed. Sometimes saving processing cycles can become
counter-productive, when it is over-done, particularly with the constant
acceleration of processor capabilities, and other hardware factors that
affect speed. That is why Windows Vista has a 3D interface graphics base,
rather than the "traditional" (if one can use that word about anything
regarding computers) 2D graphics base that has reigned until now.

Don't get me wrong. I believe in optimization, and am relatively certain
that I'm pretty good at it. But an application has to get pretty darned
processor-intensive these days to worry about every cycle saved. I've
written a few that test the limits, so I think I have some experience in
that area.

In my experience at least, I have seen more benefit in saving memory,
particularly when it comes to string usage. The .Net string type is
pretty-well optimized, as is the StringBuilder. I certainly think one should
use the StringBuilder when appropriate, and not sweat the small stuff when
doing concatenation.

Now, I'm not sure about the 2.0 framework, because it has much-improved GC
over 1.1, but I do recall reading about at least several incidents on the
1.1 framework of people peaking out their memory with poor use of string
concatenation. At any rate, I don't think it's worth haggling over.
Optimization is optimization, and both speed and memory should be watched at
all times. I'm sure we can agree on that. As to how much, I'm not fond of
quibbling. I'd rather leave that to the Lilliputians.

--
;-),

Kevin Spencer
Microsoft MVP
Professional Numbskull

Hard work is a medication for which
there is no placebo.

"Jon Skeet [C# MVP]" <sk...@pobox.com> wrote in message

news:MPG.1eb332e85...@msnews.microsoft.com...

Jon Skeet [C# MVP]

unread,
Apr 21, 2006, 5:59:44 PM4/21/06
to
Kevin Spencer <ke...@DIESPAMMERSDIEtakempis.com> wrote:
> > Pretty much every question I've answered with a reply of "use
> > StringBuilder instead" has been due to someone complaining about speed,
> > not memory.
>
> Well that's a reflection of what people are asking about. Many people are
> overly concerned about speed. Sometimes saving processing cycles can become
> counter-productive, when it is over-done, particularly with the constant
> acceleration of processor capabilities, and other hardware factors that
> affect speed. That is why Windows Vista has a 3D interface graphics base,
> rather than the "traditional" (if one can use that word about anything
> regarding computers) 2D graphics base that has reigned until now.

And I agree with that - until you start comparing O(n^2) with O(n),
which IIRC is roughly what we're talking about. I've seen people
reporting things which were taking several minutes go down to seconds
(if that) due to using StringBuilder instead of string concatenation.
It's not hard to get that kind of improvement if you're doing a lot of
concatenation.

> In my experience at least, I have seen more benefit in saving memory,
> particularly when it comes to string usage. The .Net string type is
> pretty-well optimized, as is the StringBuilder. I certainly think one should
> use the StringBuilder when appropriate, and not sweat the small stuff when
> doing concatenation.

Actually, if you use StringBuilder instead of string concatenation,
you'll get much less *temporary* wastage, but the buffer in the final
string is almost certain to be larger.

> Now, I'm not sure about the 2.0 framework, because it has much-improved GC
> over 1.1, but I do recall reading about at least several incidents on the
> 1.1 framework of people peaking out their memory with poor use of string
> concatenation. At any rate, I don't think it's worth haggling over.
> Optimization is optimization, and both speed and memory should be watched at
> all times. I'm sure we can agree on that. As to how much, I'm not fond of
> quibbling. I'd rather leave that to the Lilliputians.

Using string concatenation would keep both the pre-concatenation and
the post-concatenation string in memory during each iteration, but
that's all. I've seen some GC issues where OutOfMemoryExceptions were
thrown where they shouldn't have been (as there were plenty of objects
which could have been collected) - but that's a bug in the GC as far as
I'm concerned. I doubt that the design for StringBuilder was influenced
by that.

StringBuilder helps to prevent memory *churn*, but it doesn't make that
much difference to the overall required memory used at any one time.

<snip>

0 new messages