views:

517

answers:

7

I was reading this book - Professional Javascript for Web Developers where the author mentions string concatenation is an expensive operation compared to using an array to store strings and then using the join method to create the final string. Curious, I did a couple test here to see how much time it would save and this is what I got -

http://jsbin.com/ivako

Somehow, the Firefox usually produces somewhat similar times to both ways, but in IE, string concatenation is much much faster. So, can this idea now be considered outdated (browsers probably have improved since?

+1  A: 

Even if it were true and the join() was faster than concatenation it wouldn't matter. We are talking about tiny amounts of miliseconds here which are completely negligible.

I would always prefer well structured and easy to read code over microscopic performance boost and I think that using concatenation looks better and is easier to read.

Just my two cents.

Richard Knop
+11  A: 

This is a really good question actually.

Strings in JavaScript (like a flavor of other languages) are immutable. So, anytime you append a string to another string you are effectively recreating the object and destroying the old string.

So, now object instantiation in JavaScript is quirky. You get different results depending on the number of private variables, methods, the way you reference objects, and so forth. The modern engines like V8 and TraceMonkey are obviously superior in their handling of object creations. This is why you get the 'well it doesn't seem slow' effect when you are in Firefox/Chrome, but it is then apparent in IE.

So, why append an array and join the strings? An array is mutable and can be quickly grown. So, when you are creating long lists of strings you want to append them into an array and then .join('') because you are now preventing the numerous object instantiations that would have been needed had you just looped over and concatenated a string.

It's best to just try it for yourself and time the loops.

var s = "Hello World";
for (var i = 0; i < 100000; i++) {
    s += "Hello World";
};
print s;

vs

var s = ["Hello World"];
for (var i = 0; i < 100000; i++) {
    s.push("Hello World");
};
print s.join("");

Hopefully that gives some insight!

Justin Van Horne
Very good answer, but if the strings being concatenated were longer (at least 500 chars) the difference will be evident no matter what kind of computer runs it
Rodrigo
**TraceMonkey**, not Trackmonkey : )
kangax
Misspellings :)
Justin Van Horne
+1  A: 

I actually have some experience in this area, since my primary product is a big, IE-only webapp that does a LOT of string concatenation in order to build up XML docs to send to the server. For example, in the worst case a page might have 5-10 iframes, each with a few hundred text boxes that each have 5-10 expando properties.

For something like our save function, we iterate through every tab (iframe) and every entity on that tab, pull out all the expando properties on each entity and stuff them all into a giant XML document.

When profiling and improving our save method, we found that using string concatention in IE7 was a lot slower than using the array of strings method. Some other points of interest were that accessing DOM object expando properties is really slow, so we put them all into javascript arrays instead. Finally, generating the javascript arrays themselves is actually best done on the server, then you write then onto the page as a literal control to be exectued when the page loads.

Coxy
I had a similar problem in one of my apps and I decided to try this approach today- whaddya say - improvements in IE7 was definitely visible.
Dhana
+1  A: 

On my system (IE 8 in Windows 7) the times of StringBuilder in that test very from about 70-100% in range -- that is, it is not stable -- although the mean is about 95% of that of the normal appending.

While it's easy now just to say "premature optimization" (and I suspect that in almost every case it is) there are things worth considering:

The problem with repeated string concatenation comes repeated memory allocations and repeated data copies (advanced string data-types can reduce/eliminate much of this, but let's keep assuming a simplistic model for now). From this lets raise some questions:

  • What memory allocation is used? In the naive case each str+=x requires str.length+x.length new memory to be allocated. The standard C malloc, for instance, is a rather poor memory allocator. JS implementations have undergone changes over the years including, among other things, better memory subsystems. Of course these changes don't stop there and touch really all aspects of modern JS code. Because now ancient implementations may have been incredibly slow in certain tasks does not necessarily imply that the same issues still exist, or to the same extents.

  • As with above the implementation of Array.join is very important. If it does NOT pre-allocate memory for the final string before building it then it only saves on data-copy costs -- how many GB/s is main memory these days? 10,000 x 50 is hardly pushing a limit. A smart Array.join operation with a POOR MEMORY ALLOCATOR would be expected to perform a good bit better simple because the amount of re-allocations is reduced. This difference would be expected to be minimized as allocation cost decreases.

  • The micro-benchmark code may be flawed depending on if the JS engine creates a new object per each UNIQUE string literal or not. (This would bias it towards the Array.join method but needs to be considered in general).

  • The benchmark is indeed a micro benchmark :) Increase the growing size should have an impact of performance based on any or all (and then some) above conditions. It is generally easy to show extreme cases favoring some method or another -- the expected use case is generally of more importance.

Although, quite honestly, for any form of sane string building, I would just use normal string concatenation until such a time it was determined to be a bottleneck, if ever.

I would re-read the above statement from the book and see if there perhaps other implicit considerations the author was indeed meaning to invoke such as "for very large strings" or "insane amounts of string operations" or "in JScript/IE6", etc... If not, then such a statement is about as useful as "Insert sort is O(n*n)" [the realized costs depend upon the state of the data and the size of n of course].

And the disclaimer: the speed of the code depends upon the browser, operating system, the underlying hardware, moon gravitational forces and, of course, how your computer feels about you.

pst
A: 

Okay, regarding this here is a related module:

http://www.openjsan.org/doc/s/sh/shogo4405/String/Buffer/0.0.1/lib/String/Buffer.html

This is an effective means of creating String buffers, by using

var buffer = new String.Buffer();
buffer.append("foo", "bar");

This is the fastest sort of implementation of String buffers I know of. First of all if you are implementing String Buffers, don't use push because that is a built-in method and it is slow, for one push iterates over the entire arguments array, rather then just adding one element.

It all really depends upon the implementation of the join method, some implementations of the join method are really slow and some are relatively large.

jhuni
Do you have a link to anything that would suggest that the push method is slow?
Prestaul
http://openjsan.org/doc/j/jh/jhuni/StandardLibrary/1.81/lib/Array.htmlYou have to deal with __proto__ as well as the length of the arguments Array, two things which should not be necessary.http://groups.google.com/group/jquery-dev/browse_thread/thread/5019fa537c8d3595
jhuni
+1  A: 

In principle the book is right. Joining an array should be much faster than repeatedly concatenating to the same string. As a simple algorithm on immutable strings it is demonstrably faster.

The trick is: JavaScript authors, being largely non-expert dabblers, have written a load of code out there in the wild that uses concatenating, and relatively little ‘good’ code that using methods like array-join. The upshot is that browser authors can get a better improvement in speed on the average web page by catering for and optimising the ‘bad’, more common option of concatenation.

So that's what happened. The newer browser versions have some fairly hairy optimisation stuff that detects when you're doing a load of concatenations, and hacks it about so that internally it is working more like an array-join, at more or less the same speed.

bobince
A: 

As we know, not all browsers are created equal. Because of this, performance in different areas is guaranteed to differ from browser to browser.

That aside, I noticed the same results as you did; however, after removing the unnecessary buffer class, and just using an array directly and a 10000 character string, the results were even tighter/consistent (in FF 3.0.12): http://jsbin.com/ehalu/

Unless you're doing a great deal of string concatenation, I would say that this type of optimization is a micro-optimization. Your time might be better spent limiting DOM reflows and queries (generally the use of document.getElementbyById/getElementByTagName), implementing caching of AJAX results (where applicable), and exploiting event bubbling (there's a link somewhere, I just can't find it now).

Justin Johnson
I tried your script, unfortunately it keeps timing out on me in Firefox 3.5.
Dhana
Even more of a highlight for differences in performance between browsers and their versions.
Justin Johnson