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

今天,我读了关于字符串串联速度的 这根线

Surprisingly, string concatenation was the winner:

http://jsben.ch/#/OJ3vo

结果与我的想法相反,而且有很多文章像 这个一样对此进行了相反的解释。

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?


更新

因此,在现代浏览器中,字符串串联是优化的,所以当你想要使用 连接字符串时,使用 +符号比使用 join更快。

但是如果你实际上想使用带分隔符的 加入字符串,那么 @ 亚瑟指出join速度会更快。


更新-2020年
数组 join几乎是 2 times faster是字符串连接 + 参见: < a href = “ https://stackoverflow. com/a/54970240/984471”> https://stackoverflow.com/a/54970240/984471

值得注意的是:

  • 如果使用 large strings,则数组 join更好
  • 如果我们需要在最终输出中生成 several small strings,最好使用字符串 concat +,否则使用 Array 将需要在最后进行几个 Array 到 String 的转换,这会导致性能超负荷。

100753 次浏览

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

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

如果你尝试在每个 for之前添加一个 for(j=0;j<1000;j++),你会看到(铬下)速度的差异变得更小。最后,字符串串联仍然是1.5 x,但比之前的2.6小。

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

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

浏览器字符串优化更改了字符串串联图片。

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

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

V8 javascript 引擎(在 Google Chrome 中使用)使用 this code进行字符串串联:

// 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 + + 代码。在这里引用它太长了,但是在 运行时间 CC文件中搜索 RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat)以查看代码。

Firefox 之所以快,是因为它使用了一种叫做 Rope (绳子: 弦的替代品)的东西。攀登基本上只是一个 DAG,其中每个节点是一个字符串。

例如,如果执行 a = 'abc'.concat('def'),新创建的对象将类似于

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

And b = a.concat('123')

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

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

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)

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

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

这个测试 显示了实际使用由赋值连接生成的字符串与使用 array.join 方法生成的字符串的代价。虽然 Chrome v31中的整体分配速度仍然是原来的两倍,但是不再像不使用合成字符串时那么快了。

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

Array.join FTW!

我知道这是一个老线程,但你的测试是不正确的。你正在做的 output += myarray[i];,而它应该更像 output += "" + myarray[i];,因为你忘了,你必须胶水项目一起的东西。Concat 代码应该是这样的:

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

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

Array.join()更快。

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

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上测试。 注意,即使包含数组创建,它也更快!

到2021年,在 Chrome 上,数组 push + join 对于10 ^ 4或10 ^ 5个字符串要慢10倍,但对于10 ^ 6个字符串只慢1.2倍。

试试 https://jsben.ch/dhIy频道