¿Por qué la concatenación de cadenas es más rápida que la combinación de matrices?


Hoy he leído este hilo sobre la velocidad de concatenación de cadenas.

Sorprendentemente, la concatenación de cadenas fue la ganadora:

Http://jsben.ch/#/OJ3vo

El resultado fue opuesto a lo que pensaba. Además, hay muchos artículos sobre esto que explican de manera opuesta como esto.

Puedo adivinar que los navegadores están optimizados para encadenar concat en la última versión, pero ¿cómo lo hacen? Podemos decir que es ¿es mejor usar + al concatenar cadenas?

Actualización

Por lo tanto, en los navegadores modernos la concatenación de cadenas está optimizada, por lo que usar + signos es más rápido que usar join cuando desea concatenar cadenas.

Pero @Arthur señaló que join es más rápido si realmente quieres unir cadenas con un separador.

Author: Sanghyun Lee, 2011-09-04

8 answers

Las optimizaciones de cadenas del navegador han cambiado la imagen de concatenación de cadenas.

Firefox fue el primer navegador en optimizar la concatenación de cadenas. A partir de la versión 1.0, la técnica de matriz es en realidad más lenta que usar el operador plus en todos los casos. Otros navegadores también han optimizado la concatenación de cadenas, por lo que Safari, Opera, Chrome e Internet Explorer 8 también muestran un mejor rendimiento utilizando el operador plus. Internet Explorer antes de la versión 8 no tenía tal optimización, y por lo tanto la técnica de matriz es siempre más rápido que el operador plus.

- Escribiendo JavaScript eficiente: Capítulo 7-Sitios web aún más rápidos

El motor javascript V8 (utilizado en Google Chrome) utiliza este código para hacer la concatenación de cadenas:

// 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, "");
}

Por lo tanto, internamente lo optimizan creando un InternalArray (la variable parts), que luego se llena. La función StringBuilderConcat se llama con estas partes. Es rápido porque la función StringBuilderConcat es un código C++ muy optimizado. Es demasiado largo citar aquí, pero buscar en el runtime.cc archivo para RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat) para ver el código.

 136
Author: Daan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-04 15:18:59

Firefox es rápido porque usa algo llamado Ropes ( Ropes: una alternativa a Strings ). Una cuerda es básicamente un DAG, donde cada Nodo es una cadena.

Así que, por ejemplo, si hicieras a = 'abc'.concat('def'), el objeto recién creado se vería así. Por supuesto, esto no es exactamente como se ve en memoria, porque todavía necesita tener un campo para el tipo de cadena, la longitud y tal vez otro.

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

Y b = a.concat('123')

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

Así que en el caso más simple VM no tiene que hacer casi ningún trabajo. El único problema es que esto ralentiza un poco otras operaciones en la cadena resultante. También esto, por supuesto, reduce la sobrecarga de memoria.

Por otro lado, ['abc', 'def'].join('') normalmente solo asignaría memoria para diseñar la nueva cadena plana en memoria. (Tal vez esto debería ser optimizado)

 22
Author: evilpie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-17 04:03:09

Sé que este es un hilo viejo, pero su prueba es incorrecta. Estás haciendo output += myarray[i]; mientras que debería ser más como output += "" + myarray[i]; porque te has olvidado, que tienes que pegar elementos junto con algo. El código concat debería ser algo así como:

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

De esa manera, está haciendo dos operaciones en lugar de una debido a pegar elementos juntos.

Array.join() es más rápido.

 4
Author: Arthur,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-27 13:47:16

Los puntos de referencia allí son triviales. Concatenar los mismos tres ítems repetidamente será inlineado, los resultados probarán ser deterministas y memoizados, el manejador de basura estará simplemente tirando objetos de matriz (que serán casi nada en tamaño) y probablemente simplemente empujados y salidos de la pila debido a que no hay referencias externas y porque las cadenas nunca cambian. Estaría más impresionado si la prueba fuera un gran número de cadenas generadas aleatoriamente. Como en un concierto o dos de valor de cadena.

Array.únete a FTW!

 3
Author: Jeremy Moritz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-29 19:38:08

Yo diría que con cadenas es más fácil pre-asignar un buffer más grande. Cada elemento es de solo 2 bytes (si es UNICODE), por lo que incluso si eres conservador, puedes pre-asignar un búfer bastante grande para la cadena. Con arrays cada elemento es más "complejo", porque cada elemento es un Object, por lo que una implementación conservadora preasignará espacio para menos elementos.

Si intentas añadir un for(j=0;j<1000;j++) antes de cada for verás que (en chrome) la diferencia de velocidad se hace menor. En el end todavía era 1.5 x para la concatenación de cadenas, pero más pequeño que el 2.6 que era antes.

Y al tener que copiar los elementos, un carácter Unicode es probablemente más pequeño que una referencia a un objeto JS.

Tenga en cuenta que existe la posibilidad de que muchas implementaciones de motores JS tengan una optimización para matrices de un solo tipo que harían inútil todo lo que he escrito: -)

 2
Author: xanatos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-04 12:08:30

Esta prueba muestra la penalización de usar realmente una cadena hecha con concatenación de asignación vs hecha con matriz.método de unión. Mientras que la velocidad general de asignación sigue siendo el doble de rápido en Chrome v31, pero ya no es tan grande como cuando no se utiliza la cadena resultante.

 1
Author: srgstm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-18 08:01:27

Esto depende claramente de la implementación del motor javascript. Incluso para diferentes versiones de un motor puede obtener resultados significativamente diferentes. Usted debe hacer su propio punto de referencia para verificar esto.

Yo diría que String.concat tiene un mejor rendimiento en las versiones recientes de V8. Pero para Firefox y Opera, Array.join es un ganador.

 0
Author: Vanuan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-09 13:16:11

Mi conjetura es que, mientras que cada versión está llevando el costo de muchas concatenaciones, las versiones de unión están construyendo matrices además de eso.

 -1
Author: Marcelo Cantos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-04 11:55:20