为什么字符串连接比数组连接更快?

发布于 2024-12-03 00:57:29 字数 1185 浏览 0 评论 0 原文

今天,我阅读了关于字符串连接速度的此帖子

令人惊讶的是,字符串连接是获胜者:

http://jsben.ch/#/OJ3vo

结果与我的想法相反。此外,有很多关于此的文章对此进行了相反的解释,例如 this

我可以猜测浏览器在最新版本上已针对字符串 concat 进行了优化,但它们是如何做到的呢?我们是否可以说连接字符串时使用 + 更好?


更新

因此,在现代浏览器中,字符串连接已经过优化,因此当您想要连接时,使用 + 符号比使用 join 更快em> 字符串。

@Arthur 指出,如果您确实想加入<,加入会更快< /em> 带分隔符的字符串。


更新 - 2020
Chrome:数组 join 几乎快 2 倍 是 String concat + 请参阅:https://stackoverflow.com/a/54970240/984471

注意:

  • 数组join大字符串,code>会更好
  • 如果我们需要在最终输出中生成几个小字符串,最好使用字符串连接+,否则使用数组将需要在最后进行多次数组到字符串的转换,这会导致性能过载。

Today, I read this thread about the speed of string concatenation.

Surprisingly, string concatenation was the winner:

http://jsben.ch/#/OJ3vo

The result was opposite of what I thought. Besides, there are many articles about this which explain oppositely like this.

I can guess that browsers are optimized to string concat on the latest version, but how do they do that? Can we say that it is better to use + when concatenating strings?


Update

So, in modern browsers string concatenation is optimized so using + signs is faster than using join when you want to concatenate strings.

But @Arthur pointed out that join is faster if you actually want to join strings with a separator.


Update - 2020
Chrome: Array join is almost 2 times faster is String concat +
See: https://stackoverflow.com/a/54970240/984471

As a note:

  • Array join is better if you have large strings
  • If we need generate several small strings in final output, it is better to go with string concat +, as otherwise going with Array will need several Array to String conversions at the end which is performance overload.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

梦罢 2024-12-10 00:57:30

浏览器字符串优化改变了字符串连接图片。

Firefox 是第一个优化字符串连接的浏览器。从版本 1.0 开始,数组技术实际上在所有情况下都比使用加运算符慢。其他浏览器也优化了字符串连接,因此 Safari、Opera、Chrome 和 Internet Explorer 8 使用加号运算符也显示出更好的性能。 Internet Explorer 8 之前的版本没有这样的优化,因此数组技术总是比加号运算符更快。

编写高效的 JavaScript:第 7 章 –更快的网站

V8 javascript 引擎(在 Google Chrome 中使用)使用 此代码用于进行字符串连接:

// ECMA-262, section 15.5.4.6
function StringConcat() {
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
    throw MakeTypeError("called_on_null_or_undefined", ["String.prototype.concat"]);
  }
  var len = %_ArgumentsLength();
  var this_as_string = TO_STRING_INLINE(this);
  if (len === 1) {
    return this_as_string + %_Arguments(0);
  }
  var parts = new InternalArray(len + 1);
  parts[0] = this_as_string;
  for (var i = 0; i < len; i++) {
    var part = %_Arguments(i);
    parts[i + 1] = TO_STRING_INLINE(part);
  }
  return %StringBuilderConcat(parts, len + 1, "");
}

因此,他们在内部通过创建一个InternalArray(parts变量)来优化它,然后填充该数组。使用这些部分调用 StringBuilderConcat 函数。它速度很快,因为 StringBuilderConcat 函数是一些经过高度优化的 C++ 代码。此处引用的内容太长,请在 runtime.cc 文件中搜索RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat) 查看代码。

Browser string optimizations have changed the string concatenation picture.

Firefox was the first browser to optimize string concatenation. Beginning with version 1.0, the array technique is actually slower than using the plus operator in all cases. Other browsers have also optimized string concatenation, so Safari, Opera, Chrome, and Internet Explorer 8 also show better performance using the plus operator. Internet Explorer prior to version 8 didn’t have such an optimization, and so the array technique is always faster than the plus operator.

Writing Efficient JavaScript: Chapter 7 – Even Faster Websites

The V8 javascript engine (used in Google Chrome) uses this code to do string concatenation:

// ECMA-262, section 15.5.4.6
function StringConcat() {
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
    throw MakeTypeError("called_on_null_or_undefined", ["String.prototype.concat"]);
  }
  var len = %_ArgumentsLength();
  var this_as_string = TO_STRING_INLINE(this);
  if (len === 1) {
    return this_as_string + %_Arguments(0);
  }
  var parts = new InternalArray(len + 1);
  parts[0] = this_as_string;
  for (var i = 0; i < len; i++) {
    var part = %_Arguments(i);
    parts[i + 1] = TO_STRING_INLINE(part);
  }
  return %StringBuilderConcat(parts, len + 1, "");
}

So, internally they optimize it by creating an InternalArray (the parts variable), which is then filled. The StringBuilderConcat function is called with these parts. It's fast because the StringBuilderConcat function is some heavily optimized C++ code. It's too long to quote here, but search in the runtime.cc file for RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat) to see the code.

惜醉颜 2024-12-10 00:57:30

Firefox 速度很快,因为它使用了名为 Ropes 的东西 (绳索:绳索的替代品)。绳子基本上就是一个 DAG,其中每个节点都是一根绳子。

例如,如果您执行 a = 'abc'.concat('def'),新创建的对象将如下所示。 当然,这并不完全是内存中的样子,因为您仍然需要一个字符串类型、长度和其他字段。

a = {
 nodeA: 'abc',
 nodeB: 'def'
}

并且 b = a.concat('123 ')

b = {
  nodeA: a, /* {
             nodeA: 'abc',
             nodeB: 'def'
          } */
  nodeB: '123'
}           

因此,在最简单的情况下,虚拟机几乎不需要做任何工作。唯一的问题是,这会稍微减慢对结果字符串的其他操作。这当然也减少了内存开销。

另一方面,['abc', 'def'].join('') 通常只会分配内存以将新字符串平铺在内存中。 (也许这个应该优化)

Firefox is fast because it uses something called Ropes (Ropes: an Alternative to Strings). A rope is basically just a DAG, where every Node is a string.

So for example, if you would do a = 'abc'.concat('def'), the newly created object would look like this. Of course this is not exactly how this looks like in memory, because you still need to have a field for the string type, length and maybe other.

a = {
 nodeA: 'abc',
 nodeB: 'def'
}

And b = a.concat('123')

b = {
  nodeA: a, /* {
             nodeA: 'abc',
             nodeB: 'def'
          } */
  nodeB: '123'
}           

So in the simplest case the VM has to do nearly no work. The only problem is that this slows down other operations on the resulting string a little bit. Also this of course reduces memory overhead.

On the other hand ['abc', 'def'].join('') would usually just allocate memory to lay out the new string flat in memory. (Maybe this should be optimized)

月亮坠入山谷 2024-12-10 00:57:30

对于大量数据,连接速度更快,因此问题表述不正确。

let result = "";
let startTime = new Date().getTime();
for (let i = 0; i < 2000000; i++) {
    result += "x";
}
console.log("concatenation time: " + (new Date().getTime() - startTime));

startTime = new Date().getTime();
let array = new Array(2000000);
for (let i = 0; i < 2000000; i++) {
    array[i] = "x";
}
result = array.join("");
console.log("join time: " + (new Date().getTime() - startTime));

在 Chrome 72.0.3626.119、Firefox 65.0.1、Edge 42.17134.1.0 上测试。
请注意,即使包含数组创建,它也会更快!

For large amount of data join is faster, so the question is stated incorrectly.

let result = "";
let startTime = new Date().getTime();
for (let i = 0; i < 2000000; i++) {
    result += "x";
}
console.log("concatenation time: " + (new Date().getTime() - startTime));

startTime = new Date().getTime();
let array = new Array(2000000);
for (let i = 0; i < 2000000; i++) {
    array[i] = "x";
}
result = array.join("");
console.log("join time: " + (new Date().getTime() - startTime));

Tested on Chrome 72.0.3626.119, Firefox 65.0.1, Edge 42.17134.1.0.
Note that it is faster even with the array creation included!

请帮我爱他 2024-12-10 00:57:30

我知道这是一个旧线程,但您的测试不正确。你正在做 output += myarray[i]; 而它应该更像 output += "" + myarray[i]; 因为你忘记了,你必须将物品与某些东西粘在一起。连接代码应该类似于:

var output = myarray[0];
for (var i = 1, len = myarray.length; i<len; i++){
    output += "" + myarray[i];
}

这样,由于将元素粘合在一起,您将执行两项操作而不是一项操作。

Array.join() 速度更快。

I know this is an old thread, but your test is incorrect. You are doing output += myarray[i]; while it should be more like output += "" + myarray[i]; because you've forgot, that you have to glue items together with something. The concat code should be something like:

var output = myarray[0];
for (var i = 1, len = myarray.length; i<len; i++){
    output += "" + myarray[i];
}

That way, you are doing two operations instead of one due to glueing elements together.

Array.join() is faster.

雨后彩虹 2024-12-10 00:57:30

我想说,使用字符串可以更容易地预分配更大的缓冲区。每个元素只有 2 个字节(如果是 UNICODE),所以即使你很保守,你也可以为字符串预先分配一个相当大的缓冲区。对于数组,每个元素都更加“复杂”,因为每个元素都是一个对象,因此保守的实现将为较少的元素预先分配空间。

如果你尝试在每个 for 之前添加一个 for(j=0;j<1000;j++) 你会发现(在 chrome 下)速度差异变得更小。最终字符串连接仍然是 1.5 倍,但比之前的 2.6 小了。

并且必须复制元素,Unicode 字符可能比对 JS 对象的引用小。

请注意,许多 JS 引擎的实现可能对单类型数组进行了优化,这将使我编写的所有内容都毫无用处:-)

I would say that with strings it's easier to preallocate a bigger buffer. Each element is only 2 bytes (if UNICODE), so even if you are conservative, you can preallocate a pretty big buffer for the string. With arrays each element is more "complex", because each element is an Object, so a conservative implementation will preallocate space for less elements.

If you try to add a for(j=0;j<1000;j++) before each for you'll see that (under chrome) the difference in speed becomes smaller. In the end it was still 1.5x for the string concatenation, but smaller than the 2.6 that was before.

AND having to copy the elements, an Unicode character is probably smaller than a reference to a JS Object.

Be aware that there is the possibility that many implementations of JS engines have an optimization for single-type arrays that would make all I have written useless :-)

携君以终年 2024-12-10 00:57:30

那里的基准是微不足道的。重复连接相同的三个项目将被内联,结果将被证明是确定性的和记忆化的,垃圾处理程序将只是丢弃数组对象(其大小几乎为零),并且可能只是从堆栈中推送和弹出,因为没有外部引用,因为字符串永远不会改变。如果测试是大量随机生成的字符串,我会印象更深刻。
就像一两​​场演出的弦乐一样。

Array.join FTW!

The benchmarks there are trivial. Concatenating the same three items repeatedly will be inlined, the results will proven deterministic and memoized, the garbage handler will be just throwing away array objects (which will be next to nothing in size) and probably just pushed and popped off the stack due to no external references and because the strings never change. I would be more impressed if the test was a large number of randomly generated strings.
As in a gig or two's worth of strings.

Array.join FTW!

梦幻的味道 2024-12-10 00:57:30

最近 系列 PR “https://github.com/JedWatson/classnames” rel="nofollow noreferrer">classnames 包都表明字符串连接现在更快。由于两者都已经如此之快了,我认为阅读本文的任何人都不应该再为此烦恼。

A recent series of PRs in classnames packages all show that strings concat is now faster. As both are already so fast since forever, I think anyone reading this shouldn't bother with it anymore.

水中月 2024-12-10 00:57:30

此测试显示了实际使用字符串的惩罚使用赋值串联与使用 array.join 方法进行比较。虽然 Chrome v31 中的整体赋值速度仍然是两倍,但不再像不使用结果字符串时那么快。

This test shows the penalty of actually using a string made with assignment concatenation vs made with array.join method. While the overall speed of assignment is still twice as fast in Chrome v31 but it is no longer as huge as when not using the resultant string.

生来就爱笑 2024-12-10 00:57:30

截至 2021 年,在 Chrome 上,数组 push+join 对于 10^4 或 10^5 字符串大约慢 10 倍,但对于 10^6 字符串仅慢 1.2 倍。

尝试一下 https://jsben.ch/dhIy

As of 2021 on Chrome, array push+join is about 10x slower for 10^4 or 10^5 strings, but only 1.2x slower for 10^6 strings.

Try it on https://jsben.ch/dhIy

余厌 2024-12-10 00:57:30

这显然取决于 javascript 引擎的实现。即使对于一种引擎的不同版本,您也可以获得明显不同的结果。您应该做自己的基准测试来验证这一点。

我想说 String.concat 在最新版本的 V8 中具有更好的性能。但对于 Firefox 和 Opera 来说,Array.join 是赢家。

This clearly depends on the javascript engine implementation. Even for different versions of one engine you can get significally different results. You should do your own benchmark to verify this.

I would say that String.concat has better performance in the recent versions of V8. But for Firefox and Opera, Array.join is a winner.

折戟 2024-12-10 00:57:30

我的猜测是,虽然每个版本都承受许多串联的成本,但连接版本除此之外还构建数组。

My guess is that, while every version is wearing the cost of many concatenations, the join versions are building arrays in addition to that.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文