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

San*_*Lee 108 javascript performance string-concatenation

今天,我读到了关于字符串连接速度的这个帖子.

令人惊讶的是,字符串连接是赢家:

http://jsben.ch/#/OJ3vo

结果与我的想法相反.此外,大约有这么多的文章,其解释相反像这样.

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

更新

因此,在现代浏览器中,字符串连接已经过优化,因此使用+符号比使用连接字符串join时要快.

@Arthur指出,join如果你真的想用分隔符连接字符串会更快.

Daa*_*aan 145

浏览器字符串优化已更改字符串连接图片.

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

- 编写高效的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, "");
}
Run Code Online (Sandbox Code Playgroud)

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

  • @gcb,不应该使用连接速度更快的唯一浏览器.95%的用户拥有FF和Chrome.我将针对95%的用例进行优化. (42认同)
  • 优化101:你应该瞄准最慢的目标!例如,`arr.join vs str +`,在chrome上得到(每秒操作次数)`25k/s vs 52k/s`.在firefox上你得到'76k/s vs 212k/s`.所以`str +`更快.但让我们看看其他浏览器.Opera提供43k/s vs 26k/s.IE给出`1300/s vs 1002/s`.走着瞧吧?*唯一*浏览器,NEED优化会更好地使用所有其他的慢速,根本不重要.所以,这些文章都没有理解任何有关性能的内容. (39认同)
  • @PaulDraper如果90%的用户使用快速浏览器,你选择的任何一个选项都会获得0.001s,但如果你选择惩罚其他用户超过0.001s,你的10%用户将获得2s ...决定清楚了.如果你看不到它,我很抱歉你编码的人. (7认同)
  • 较旧的浏览器最终会消失,但是有人回去转换所有这些数组连接的可能性不大.只要对当前用户不是一个主要的不便,最好为未来编码.在处理旧浏览器时,可能需要担心的是连接性能,这些都是比较重要的. (6认同)
  • 你离开了真正有趣的东西,该数组仅用于调用具有不同参数计数的Runtime_StringBuilderConcat.但真正的工作是在那里完成的. (4认同)
  • @gcb,您假设的示例是正确的,但手头的案例不匹配。Chrome 和 IE 不是 2000x 的差异;这是 25 倍的差异。如果您正在优化 7 秒的字符串连接,IE 使用 `+` 只会慢 2 秒。我原则上同意你的说法,但在这种情况下不同意。(另外仅供参考,我应该说 Chrome、FF 和 IE **8+** 是我的 95%。) (2认同)

evi*_*pie 23

Firefox速度很快,因为它使用的是Ropes(Ropes:Strings的替代品).绳索基本上只是一个DAG,其中每个节点都是一个字符串.

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

a = {
 nodeA: 'abc',
 nodeB: 'def'
}
Run Code Online (Sandbox Code Playgroud)

b = a.concat('123')

b = {
  nodeA: a, /* {
             nodeA: 'abc',
             nodeB: 'def'
          } */
  nodeB: '123'
}           
Run Code Online (Sandbox Code Playgroud)

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

另一方面['abc', 'def'].join(''),通常只需分配内存来在内存中布置新的字符串.(也许这应该优化)


小智 7

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

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));
Run Code Online (Sandbox Code Playgroud)

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


Art*_*hur 6

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

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

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

Array.join() 是比较快的。

  • 为什么我们需要这样写?我们已经在没有它的情况下将项目粘合到“输出”。 (2认同)