¿Son los literales de plantilla ES6 más rápidos que la concatenación de cadenas?

82

¿La generación de código HTML se ejecuta considerablemente más rápido en los navegadores modernos cuando se usa concatenación de cadenas o literales de plantilla en ES6?

Por ejemplo:

Concatenación de cadenas

"<body>"+
  "<article>"+
    "<time datetime='" + date.toISOString() +"'>"+ date +"</time>"+
  "</article>"+
"</body>"

Plantilla literal

`<body>
  <article>
    <time datetime='${ date.toISOString() }'>${ date }</time>
  </article>
</body>`
hurrymaplelad
fuente
2
La concatenación de cadenas no es lo suficientemente lenta como para que haya un margen de mejora notable. Los templaters populares como bigote / subrayado / manillar son decenas a cientos de veces más lentos que la concatenación o los literales de plantilla.
dandavis
1
Hablando teóricamente (a menos que se compile el JS), los literales de plantilla serían más lentos ya que la 'cadena' necesita ser analizada independientemente de la existencia del marcador de posición. El motor JS analizaría el literal de la plantilla una vez para que los usos posteriores sean tan rápidos como las cadenas concatenadas; lo que significa que la única diferencia sería el tiempo necesario para analizar la plantilla literal una vez.
Precastic
La concatenación de cadenas, por supuesto, es más rápida. No se está realizando ningún análisis. Simplemente se suma a la longitud de la cuerda. Aún así, usaría literales de plantilla.
Banjocat
He votado para cerrar esta pregunta porque los resultados no son concluyentes. 1. La velocidad dependerá del motor - por lo menos no puede haber (y no es en el momento de escribir esto) diferencia entre Chrome y Firefox. 2. La velocidad dependerá de cómo se utilicen las concatenaciones / plantillas y con qué datos. 3. Estos pueden cambiar entre diferentes versiones del mismo motor. 4. El microbenchmarking puede producir resultados engañosos. 5. Es probable que la diferencia de velocidad, si la hay, sea insignificante. En el momento de escribir este artículo lo es .
VLAZ

Respuestas:

86

Por el momento, parece que la concatenación de cadenas es más rápida: http://jsperf.com/es6-string-literals-vs-string-concatenation

ES6 with variable                     19,992,512    ±5.21%    78% slower
String concatenation with variable    89,791,408    ±2.15%    fastest
ES6 with function                     461,358       ±3.12%    99% slower
String concatenation with function    503,255       ±1.77%    99% slower

Probé que se ejecutó en Chrome 43.0.2334.0 canary (64 bits), que usa V8 4.3.31, con la #enable-javascript-harmonybandera habilitada.

Como referencia, la última versión de Node.js (0.12.0 en el momento de escribir este artículo) usa V8 3.28.73: https://raw.githubusercontent.com/joyent/node/master/ChangeLog

Estoy seguro de que todavía no se han aplicado todas las posibles optimizaciones de rendimiento que podrían aplicarse, por lo que sería razonable esperar que el rendimiento mejore a medida que ES6 se acerca a la finalización y estas características se migran a la rama estable.


Editar: Gracias por los comentarios @ user1329482, @ icl7126, Nicolai Borisik y FesterCluck. Ahora que han pasado aproximadamente 2 años desde que se hizo esta pregunta, la compatibilidad con el navegador ES6 ha aumentado enormemente y se ha realizado una buena cantidad de optimización del rendimiento. Aquí hay algunas actualizaciones .

Editar: (febrero de 2020) Resultado actualizado de Chrome basado en comentarios de @ JorgeFuentesGonzález y posterior confirmación .

En Chrome (a partir de 59.0.3035), los literales de cadena ES6 son más rápidos :

ES6 with variable                     48,161,401       ±1.07%    fastest
String concatenation with variable    27,046,298       ±0.48%    44% slower
ES6 with function                     820,441          ±1.10%    98% slower
String concatenation with function    807,088          ±1.08%    98% slower

Actualización: en Chrome (a partir de 79.0.3945), la concatenación de cadenas es más rápida ... Ver comentarios.

En Firefox (a partir de 57.0.0), los literales de cadena ES6 son más rápidos :

ES6 with variable                     1,924,610,984    ±0.50%    fastest
String concatenation with variable    1,876,993,458    ±0.79%    3% slower
ES6 with function                     539,762          ±5.04%    100% slower
String concatenation with function    546,030          ±5.88%    100% slower

En Safari (a partir de 11.0.2), depende:

ES6 with variable                     1,382,752,744    ±0.71%    fastest
String concatenation with variable    1,355,512,037    ±0.70%    2% slower
ES6 with function                     876,516          ±1.01%    100% slower
String concatenation with function    883,370          ±0.79%    100% slower

Cuando se usa una cadena encasillada, los literales de cadena ES6 son más rápidos . Sin embargo, cuando se llama a una función desde el literal, la concatenación de cadenas es más rápida en este ejemplo.

Si realmente desea profundizar y necesita exprimir hasta la última gota de rendimiento de Safari, sugeriría configurar pruebas que vean si / cómo se escribieron incorrectamente las variables y múltiples referencias dentro de un rendimiento de efecto literal.

Andrew Odri
fuente
4
Firefox 50 64bit - ES6 tiene la misma velocidad que la concatenación 1.423.816.207 Ops / s.
icl7126
4
Interpolaciones de Safari 9.0 ES6 más rápidas que la concatenación aproximadamente 34% Chrome 55 aún interpolaciones de ES6 mucho más lentas Firefox 50 la misma velocidad
Nikolai Borisik
1
Las cadenas de plantillas son ahora un orden de magnitud más rápidas que la concatenación de cadenas. Vea la versión 14 del jsperf dado, es el más preciso e imparcial que técnicamente se puede obtener mientras conserva los aspectos de las características. La versión 17 es menos sesgada pero poco realista.
FesterCluck
1
El enlace jsperf que pasó, en la "Revisión 1", los literales de cadena siguen siendo más lentos que la concatenación de cadenas en el último Chrome: u.teknik.io/nPmY8.png
Jorge Fuentes González
1
El archivo de resultados jsperf se eliminó. Subido este sin caducidad: u.teknik.io/02OVr.png
Jorge Fuentes González
7

Hice una prueba ingenua en node.js v6.0.0 y obtuve casi el mismo rendimiento . Dado que la prueba es tan ingenua, no crea demasiado en los números. Pero parece que el compilador JIT genera código muy optimizado hoy en día. Esto me permitió decidir preferir las plantillas a la concatenación para mis aplicaciones de nodo.

Como referencia, este es el código que utilicé:

'use strict'

function strConcat(i) {
    return 'abc' + i + 'def'
}

function strTemplate(i) {
    return `abc${i}def`
}

function run(strategy) {
    let before = new Date().getTime()
    let len = 0
    for ( let i = 0; i < 10000000; i+=1 ) {
        len += strategy(i).length
    }
    console.log(len + ' - ' + ((new Date().getTime()) - before) + 'ms')
}

console.log('strConcat')
run(strConcat)

console.log('strTemplate')
run(strTemplate)

Y el resultado fue:

strConcat
128888890 - 1904ms
strTemplate
128888890 - 1979ms

Solía lenasegurarme absolutamente de que el optimizador no optimiza todo el ciclo. De todos modos, sigue siendo una prueba muy sencilla. Quizás alguien pueda hacer uno más sofisticado.

analógico-nico
fuente
1
Ejecuté un punto de referencia similar y obtuve un resultado similar. Mi punto de referencia incluía más cadenas, algunas de las cuales eran más largas. La interpolación funciona ligeramente mejor que la concatenación.
rattray
2

Para una prueba simple con números aleatorios como cadena, ambos se acercan tanto en Chrome y FF

Pruebas en Chrome 58.0.3029 / Windows 10

Literales de cadena 2.996.883 ± 2,36% más rápido

Operador (+) 3,054,078 ± 2.01% más rápido

Función Concat 2.659.391 ± 2,35% 13% más lenta

Pruebas en Firefox 53.0.2 / Windows 10

Literales de cadena 1.923.835 ± 1,52% más rápido

Operador (+) 1.948.503 ± 1,13% más rápido

Función Concat 1.810.857 ± 1,81% 8% más lenta

Prueba aquí en jsperf

Dinesh Rajan
fuente
1

TL; DR

La concatenación es más rápida y consistente con respecto a su velocidad. Pero la diferencia es muy pequeña para 1 o 2 variables (por debajo de 0,3 segundos para 100 millones de llamadas).

Editar

Después de la segunda ejecución, parece que la concatenación es principalmente la más rápida de las dos.


Entonces, quería expandir la respuesta de analog-nico proporcionando una prueba que era más extensa y también miraba (un poco) la escalabilidad de las dos funciones.

Código en pastebin

Decidí usar cuatro casos de prueba para cada función, con una variable al frente, una al final, una en el medio y dos variables en el medio. La configuración básica es la misma. Solo estoy usando 100,000,000 iteraciones de la función y estas iteraciones se ejecutan 100 veces. Usé los mismos mecanismos para evitar la optimización, es decir, obtener la suma de las longitudes de las cadenas resultantes y registrarla. También registré el tiempo necesario (para adivinar cuánto tiempo tomará) pero también lo guardé en una matriz.

Posteriormente, calculé la desviación promedio, mínima, máxima y estándar para cada método.

Aquí están los resultados:

{ 
  sum: { 
    t: { 
      start: 2072751, 
      mid: 2338476, 
      end: 2083695, 
      double: 2950287 
    },
    c: { 
      start: 2086059, 
      mid: 2345551, 
      end: 2074732, 
      double: 2922929 
    } 
  },
  avg: { 
    t: { 
      start: 20727.51,
      mid: 23384.76,
      end: 20836.95,
      double: 29502.87 
    },
    c: { 
      start: 20860.59,
      mid: 23455.51,
      end: 20747.32,
      double: 29229.29 
    } 
  },
  sd: {
    t: {
      start: 335.6251329981114,
      mid: 282.9490809315344,
      end: 286.2220947096852,
      double: 216.40844045461824 
    },
    c: {
      start: 255.4803356424913,
      mid: 221.48744862858484,
      end: 238.98242111084238,
      double: 209.9309074433776 
    } 
  },
  min: { 
    t: { 
      start: 20490, 
      mid: 23216, 
      end: 20588, 
      double: 29271 
    },
    c: { 
      start: 20660, 
      mid: 23258, 
      end: 20534, 
      double: 28985 
    } 
  },
  max: { 
    t: { 
      start: 23279, 
      mid: 25616, 
      end: 22887, 
      double: 30843 
    },
    c: { 
      start: 22603, 
      mid: 25062, 
      end: 22403, 
      double: 30536 
    } 
  } 
}

los valores en t-objects son para plantillas, los valores en c-objects son para concatenación. startsignifica que la variable está al principio, mid que está en el medio, end que está al final y double que hay dos variables. sumes la suma de las 100 carreras. avges la carrera promedio, lo que significa que lo es sum / 100. sd Aquí está la salida fácil, wikipedia (inglés simple) . miny maxson el valor mínimo y máximo de una ejecución, respectivamente.

Resultados

Parece que las plantillas son más rápidas para las variables individuales que no se encuentran al final de una cadena, considerando que el promedio es menor y el mínimo es menor. Si coloca una variable al final de una cadena o tiene varias variables en su cadena, la concatenación es más rápida.

Aunque el mínimo y el promedio de las plantillas son mejores que sus contrapartes de concatenación con respecto a las dos primeras condiciones, la desviación estándar es consistentemente peor. La diferencia parece reducirse con más variables (se necesitan más pruebas).

Dado que la mayoría de las plantillas probablemente no se utilizarán para una sola variable en una cadena, conviene decir que ceñirse a la concatenación produce un mejor rendimiento. Pero la diferencia es (al menos por ahora) muy marginal. En 100,000,000 (100 millones) evaluaciones con dos variables, la diferencia es simplemente 273,58 ms, aproximadamente un cuarto de segundo ...


Segunda ejecución

La segunda ejecución se ve algo diferente. Excepto por el valor máximo, la desviación absoluta promedio y la desviación estándar, cada medición demostró que la concatenación es más rápida que las plantillas.

Las tres medidas mencionadas tenían valores más bajos (por lo tanto mejores) para las plantillas cuando la variable estaba al final de la cadena o cuando había dos variables en la cadena.

Aquí están los resultados:

{
  "sum": {
    "t": {
      "start": 1785103,
      "mid": 1826679,
      "end": 1719594,
      "double": 2110823,
      "many": 4153368
    },
    "c": {
      "start": 1720260,
      "mid": 1799579,
      "end": 1716883,
      "double": 2097473,
      "many": 3836265
    }
  },
  "avg": {
    "t": {
      "start": 17851.03,
      "mid": 18266.79,
      "end": 17195.94,
      "double": 21108.23,
      "many": 41533.68
    },
    "c": {
      "start": 17202.6,
      "mid": 17995.79,
      "end": 17168.83,
      "double": 20974.73,
      "many": 38362.65
    }
  },
  "sd": {
    "t": {
      "start": 858.7857061572462,
      "mid": 886.0941856823124,
      "end": 786.5366719994689,
      "double": 905.5376950188214,
      "many": 1744.9005638144542
    },
    "c": {
      "start": 599.0468429096342,
      "mid": 719.1084521127534,
      "end": 935.9367719563112,
      "double": 991.5642274204934,
      "many": 1465.1116774840066
    }
  },
  "aad": {
    "t": {
      "start": 579.1207999999996,
      "mid": 576.5628000000003,
      "end": 526.8268,
      "double": 586.9651999999998,
      "many": 1135.9432000000002
    },
    "c": {
      "start": 467.96399999999966,
      "mid": 443.09220000000016,
      "end": 551.1318000000008,
      "double": 610.2321999999999,
      "many": 1020.1310000000003
    }
  },
  "min": {
    "t": {
      "start": 16932,
      "mid": 17238,
      "end": 16387,
      "double": 20016,
      "many": 39327
    },
    "c": {
      "start": 16477,
      "mid": 17137,
      "end": 16226,
      "double": 19863,
      "many": 36424
    }
  },
  "max": {
    "t": {
      "start": 23310,
      "mid": 24102,
      "end": 21258,
      "double": 26883,
      "many": 49103
    },
    "c": {
      "start": 19328,
      "mid": 23203,
      "end": 22859,
      "double": 26875,
      "many": 44352
    }
  },
  "median": {
    "t": {
      "start": 17571,
      "mid": 18062,
      "end": 16974,
      "double": 20874,
      "many": 41171.5
    },
    "c": {
      "start": 16893.5,
      "mid": 18213,
      "end": 17016.5,
      "double": 20771,
      "many": 38849
    }
  }
}

El codigo esta aqui

Armin
fuente
Actualmente estoy ejecutando una nueva versión de mi script de prueba. Incluye el absolute average meany el median. También compara el tiempo de ejecución con 10 variables para reemplazar.
Armin
0

Creo que el punto de referencia anterior no es útil. No se ha utilizado el resultado de la interpolación o la concatenación. Así que sí, la concatenación es bastante rápida, porque ninguna cadena de copia allí y la cadena de resultado solo tienen enlaces a las cadenas principales. Pero si prueba la cadena de resultados o la compara con otra, la cadena se serializará en la cadena plana y, sí, llevará algún tiempo. Por lo tanto, la interpolación podría ser más efectiva para el uso de la CPU y la memoria que la concatenación en casos reales.

gev
fuente