404-js-string-concatenation

High-performance String Concatenation in JavaScript

By | | JavaScript

Concatenation, or joining strings, is an important facility within any programming language. It’s especially vital within web applications, since strings are regularly used to generate HTML output. Several languages offer fast string-handling classes such as StringBuilder in .NET and StringBuffer/StringBuilder in Java.

There are a number of ways to concatenate strings in JavaScript:


str = "a" + "b";
str += "c";
str = str.concat("d", "e");

You can also join an array of strings:


str = ["a", "b", "c", "d", "e"].join("");

If you’re only joining a few strings, you should use whichever method is most practical. Performance gains or losses will be negligible in all browsers.

Concatenating Many Strings

Consider the following functionally identical examples. The first uses the string concatenation operator:


// standard string append
var str = "";
for (var i = 30000; i > 0; i--) {
	str += "String concatenation. ";
}

The second uses an array join:


// array join
var str = "", sArr = [];
for (var i = 30000; i > 0; i--) {
	sArr[i] = "String concatenation. ";
}
str = sArr.join("");

Which will execute the fastest?

Some developers will assume the concatenation operator is faster because it uses less code and doesn’t require an array that doubles memory requirements. For others, conventional wisdom dictates that array joins are faster—it’s more memory efficient within the JavaScript interpreter.

The truth is rather more complex. In all the most recent browsers, either method is fast and will complete within 80ms on a mid-range PC. Here are the results from my own completely unscientific benchmark tests:

  • Chrome 6.0: standard string appends are usually quicker than array joins, but both complete in less than 10ms.
  • Opera 10.6: again, standard appends are quicker, but the difference is marginal—often 15ms compared to 17ms for an array join.
  • Firefox 3.6: the browser normally takes around 30ms for either method. Array joins usually have the edge, but only by a few milliseconds.
  • IE 8.0: a standard append requires 30ms, whereas an array join is more than double—typically 70ms.
  • Safari 5.0.1: bizarrely, a standard append takes no more than 5ms but an array join is more than ten times slower at 55ms.

The latest JavaScript engines are optimized for string concatenation operators. Array joins remain fast, but are without a performance gain.

The flIEs in the ointment

IE7 is the world’s third-most-used browser with a 14% market share. IE6 accounts for another 8%. There’s no need to read further if you’ve dropped support for these aging applications.

Still here? This is the shocker: IE7 and below use a concatenation handler that repeatedly copies strings and causes an exponential increase in time and memory usage. The code using the concatenation operator takes around 2.5 minutes (150,000ms) to execute, and the browser remains unresponsive throughout. By comparison, the array join completes in under 200ms—it’s more than 800 times faster.

If you’re supporting IE7, array joins remain the best method for concatenating a large number of strings.

What about PHP? Tune in soon for the test results…

The Ultimate JavaScript Bundle: 2 books + 1 course

Buy now $39 Normally $117 - save 66%

Or get access to all SitePoint's Premium Content with a Learnable membership

Craig Buckler

Craig is a Director of OptimalWorks, a UK consultancy dedicated to building award-winning websites implementing standards, accessibility, SEO, and best-practice techniques.

More Posts - Website

{ 15 comments }

JamesFarrell September 27, 2010 at 7:38 pm

Who me or Florian?

JamesFarrell September 23, 2010 at 1:19 am

I attended a talk by Florian Loitsch, who works on the Google Chrome team and he warned against making assumptions like this because the browser’s Javascript engines may be optimised in unexpected way and this is a good example of that.

brothercake September 25, 2010 at 5:11 am

because the browser’s Javascript engines may be optimised in unexpected ways

Spoken like a man working on closed-source technologies!

extraneu September 22, 2010 at 2:46 am

What about preallocating the array?

Sam Foster September 15, 2010 at 11:38 pm

There’s a couple performance tests along these lines at jsperf.com, e.g.
http://jsperf.com/multiline-string/2 (I just forked in response to this article).

also:
http://jsperf.com/string-concatenation/3
http://jsperf.com/string-concatenation-test/2
..and others of varying utility.

This 2 articles might be of interest, on Sitepen’s blog, which provide some in-depth analysis on string building techniques in the browsers of the day:
http://www.sitepen.com/blog/2008/05/09/string-performance-an-analysis/
http://www.sitepen.com/blog/2008/06/09/string-performance-getting-good-performance-from-internet-explorer/

Vic September 15, 2010 at 9:54 pm

There has been a jsPerf test case for exactly this, since August: http://jsperf.com/concatenation-vs-array-join Might be useful to reference that since it automatically gathers the results per browser.

tjunghans September 15, 2010 at 6:55 pm

Ah yes, of course. I must have overlooked that. Which method would you go for, if you were developing a web app with crossbrowser support including IE7?
Go with the array join or use one of the methods above depending on the browser?
-TJ

Craig Buckler September 15, 2010 at 9:09 pm

It really depends whether you’ve got any large string operations. The examples above are useful for benchmarking, but you’re unlikely to concatenate 30,000 strings in a real application.

I’d suggest using whatever is practical. Test in IE7 early — if concatenation causes serious issues, you re-factor your code accordingly.

tjunghans September 15, 2010 at 7:59 am

Hi Craig
When I run the following block of code in Firebug’s Console (Firefox 3.6.9, Firebug 1.5.4) I get the best result for “join”. To be exact:
append: 105.222ms
concat: 136.778ms
join: 93.18ms
Any ideas why this may be? Do you get the same or similar results?
Cheers
TJ
console.clear();
var string = ”;
var i = 100000, j = 0;
console.profile(‘append’);
(function () {
j = i;
while(j > 0) {
string += ‘foo’;
–j;
}
}());
console.profileEnd(‘append’);
console.info(string.length);
string = ”;
console.profile(‘concat’);
(function () {
j = i;
while(j > 0) {
string = string.concat(‘foo’);
–j;
}
}());
console.profileEnd(‘concat’);
console.info(string.length);
string = [];
console.profile(‘join’);
(function () {
j = i;
while(j > 0) {
string.push(‘foo’);
–j;
}
}());
string = string.join(”);
console.profileEnd(‘join’);
console.info(string.length);

Craig Buckler September 15, 2010 at 6:50 pm

Hi tjunghans,

As mentioned above, array joins in Firefox appear to be marginally faster than concatenation operators. Your code shows that it’s 12ms quicker for a loop of 100,000 strings. My tests showed a difference of 2 or 3ms on 30,000 strings.

Array joins are normally faster in older interpreters because the bulk of the work is completed in one fast operation rather than many small ones. But it makes very little difference in newer browsers — they are probably compiling code or using more efficient memory handling techniques.

Oliver September 15, 2010 at 6:12 am

In your timings are you attempting to use the strings?

eg.
for (var i =0; i < …; i++) {
var s = makeString(); // or whatever
s[s.length - 1];
}

?

Craig Buckler September 15, 2010 at 6:34 pm

No — the timings are based on the code as written above. A date() object was created at either end of the code and the difference in ms was calculated.

brothercake September 14, 2010 at 8:01 pm

Nice tip – IE is certainly optimized in some strange ways.
There’s also a neat combination of join() and split() you can use to reduce repetitive data stored in arrays. For example – you have an array of file paths, but each path is substantially the same; so store the path, prefix, and name separately, and join them together at the last minute:
var path = ‘/images/flags/worldcup/qualifiers/’,
suffix = ‘.png’,
flags = ['brazil','cameroon','japan','spain'];

flags = (path + flags.join(suffix + ‘,’ + path) + suffix).split(‘,’);

ahallicks September 14, 2010 at 7:25 pm

2.5 minutes actually just blew my mind. That is ridiculous! Thanks for the info :-)

iFadey September 16, 2010 at 4:51 pm

You are right. 2.5 min is too much :P

Comments on this entry are closed.