Sind ES6-Vorlagenliterale schneller als die Verkettung von Zeichenfolgen?

79

Läuft die HTML-Codegenerierung in modernen Browsern messbar schneller, wenn in ES6 Zeichenfolgenverkettung oder Vorlagenliterale verwendet werden?

Zum Beispiel:

String-Verkettung

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

Vorlagenliteral

`<body>
  <article>
    <time datetime='${ date.toISOString() }'>${ date }</time>
  </article>
</body>`
Hurrymaplelad
quelle
2
Die Verkettung von Zeichenfolgen ist nicht langsam genug, um spürbare Verbesserungsmöglichkeiten zu schaffen. Beliebte Vorlagen wie Schnurrbart / Unterstrich / Lenker sind Dutzende bis Hunderte Male langsamer als Verkettung oder Vorlagenliterale.
Dandavis
1
Theoretisch gesehen (sofern der JS nicht kompiliert ist) wären Vorlagenliterale langsamer, da die 'Zeichenfolge' unabhängig von der Existenz des Platzhalters analysiert werden muss. Die JS-Engine würde das Vorlagenliteral einmal analysieren, sodass nachfolgende Verwendungen genauso schnell sind wie verkettete Zeichenfolgen. Dies bedeutet, dass der einzige Unterschied die Zeit ist, die benötigt wird, um das Vorlagenliteral einmal zu analysieren.
Precastic
Die Verkettung von Strings ist natürlich schneller. Es findet keine Analyse statt. Es wird nur die Länge der Zeichenfolge erhöht. Trotzdem würde ich Vorlagenliterale verwenden.
Banjocat
Ich habe dafür gestimmt, diese Frage zu schließen, da die Ergebnisse nicht schlüssig sind. 1. Die Geschwindigkeit wird auf dem Motor abhängen - zumindest kann es sein (und es ist zum Zeitpunkt des Schreibens) Unterschied zwischen Chrome und Firefox. 2. Die Geschwindigkeit hängt davon ab, wie Verkettung / Vorlagen verwendet werden und mit welchen Daten. 3. Diese können zwischen verschiedenen Versionen desselben Motors wechseln. 4. Microbenchmarking kann zu irreführenden Ergebnissen führen. 5. Der etwaige Geschwindigkeitsunterschied ist wahrscheinlich vernachlässigbar. Zum Zeitpunkt des Schreibens es ist .
VLAZ

Antworten:

83

Momentan scheint die Verkettung von Zeichenfolgen schneller zu sein: 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

Ich habe getestet, dass es auf Chrome 43.0.2334.0 Canary (64-Bit) ausgeführt wurde, das V8 4.3.31 mit #enable-javascript-harmonyaktiviertem Flag verwendet.

Als Referenz verwendet die neueste Version auf Node.js (0.12.0 zum Zeitpunkt des Schreibens) V8 3.28.73: https://raw.githubusercontent.com/joyent/node/master/ChangeLog

Ich bin sicher, dass alle möglichen Leistungsoptimierungen, die angewendet werden könnten, noch nicht angewendet wurden. Es ist daher vernünftig zu erwarten, dass die Leistung verbessert wird, wenn sich ES6 der Finalisierung nähert und diese Funktionen in den stabilen Zweig migriert werden.


Bearbeiten: Danke für die Kommentare @ user1329482, @ icl7126, Nicolai Borisik und FesterCluck. Nachdem diese Frage seit ungefähr zwei Jahren gestellt wurde, hat die Unterstützung für ES6-Browser erheblich zugenommen, und es wurden zahlreiche Leistungsoptimierungen durchgeführt. Hier sind einige Updates .

Bearbeiten: (Februar 2020) Aktualisiertes Chrome-Ergebnis basierend auf @ JorgeFuentesGonzález-Kommentaren und nachfolgender Bestätigung .

In Chrome (Stand 59.0.3035) sind ES6-Zeichenfolgenliterale schneller :

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

Update: In Chrome (Stand 79.0.3945) ist die Verkettung von Zeichenfolgen schneller ... Siehe Kommentare.

In Firefox (ab 57.0.0) sind ES6-Zeichenfolgenliterale schneller :

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

In Safari (ab 11.0.2) kommt es darauf an:

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

Bei Verwendung einer typisierten Zeichenfolge sind ES6-Zeichenfolgenliterale schneller . Wenn Sie jedoch eine Funktion aus dem Literal aufrufen, ist die Verkettung von Zeichenfolgen in diesem Beispiel schneller .

Wenn Sie wirklich tief gehen und jeden Leistungsabfall aus Safari herausholen möchten, würde ich empfehlen, Tests einzurichten, die feststellen, ob / wie falsch eingegebene Variablen und mehrere Referenzen innerhalb einer Leistung mit wörtlichen Effekten liegen.

Andrew Odri
quelle
4
Firefox 50 64bit - ES6 hat die gleiche Geschwindigkeit wie die Verkettung 1.423.816.207 Ops / s.
icl7126
4
Safari 9.0 ES6 Interpolationen schneller als Verkettung etwa 34% Chrome 55 noch ES6 Interpolationen viel langsamer Firefox 50 die gleiche Geschwindigkeit
Nikolai Borisik
1
Vorlagenzeichenfolgen sind jetzt eine Größenordnung schneller als die Verkettung von Zeichenfolgen. Siehe Version 14 des angegebenen jsperf, es ist das genaueste und unvoreingenommenste, das man technisch erhalten kann, während die Funktionsaspekte beibehalten werden. Version 17 ist am wenigsten voreingenommen, aber unrealistisch.
FesterCluck
1
Der jsperf-Link, den Sie in "Revision 1" übergeben haben, ist immer noch langsamer als die Verkettung von Zeichenfolgen unter dem neuesten Chrome: u.teknik.io/nPmY8.png
Jorge Fuentes González
1
Die jsperf-Ergebnisdatei wurde gelöscht. Hochgeladen dieses ohne Ablaufdatum: u.teknik.io/02OVr.png
Jorge Fuentes González
6

Ich habe einen naiven Test auf node.js v6.0.0 durchgeführt und fast die gleiche Leistung erzielt . Da der Test so naiv ist, glauben Sie den Zahlen nicht zu viel. Aber es scheint, dass der JIT-Compiler heutzutage sehr optimierten Code generiert. Dadurch konnte ich entscheiden, Vorlagen für meine Knoten-Apps der Verkettung vorzuziehen.

Als Referenz ist dies der Code, den ich verwendet habe:

'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)

Und die Ausgabe war:

strConcat
128888890 - 1904ms
strTemplate
128888890 - 1979ms

Ich habe immer lendarauf geachtet, dass der Optimierer nicht die gesamte Schleife entfernt. Auf jeden Fall ist es immer noch ein sehr einfacher Test. Vielleicht kann jemand eine anspruchsvollere machen.

analog-nico
quelle
1
Ich habe einen ähnlichen Benchmark durchgeführt und ein ähnliches Ergebnis erzielt. Mein Benchmark umfasste mehr Saiten, von denen einige länger waren. Die Interpolation ist etwas besser als die Verkettung.
Rattray
2

Für einen einfachen Test mit Zufallszahlen als Zeichenfolge kommen sich beide in Chrome & FF so nahe

Testen in Chrome 58.0.3029 / Windows 10

String-Literale 2.996.883 ± 2,36% am ​​schnellsten

Betreiber (+) 3.054.078 ± 2,01% am schnellsten

Concat-Funktion 2.659.391 ± 2,35% 13% langsamer

Testen in Firefox 53.0.2 / Windows 10

String-Literale 1.923.835 ± 1,52% am schnellsten

Bediener (+) 1.948.503 ± 1,13% am schnellsten

Concat-Funktion 1.810.857 ± 1,81% 8% langsamer

Test hier bei jsperf

Dinesh Rajan
quelle
1

TL; DR

Die Verkettung ist schneller und gleichmäßiger in Bezug auf die Geschwindigkeit. Der Unterschied ist jedoch für 1 oder 2 Variablen sehr gering (unter 0,3 Sekunden für 100 Millionen Anrufe).

Bearbeiten

Nach dem zweiten Lauf scheint die Verkettung meistens die schnellere zu sein.


Daher wollte ich die Antwort von analog-nico erweitern, indem ich einen Test bereitstellte, der umfangreicher war und auch (ein bisschen) die Skalierbarkeit der beiden Funktionen untersuchte.

Code auf Pastebin

Ich entschied mich für vier Testfälle für jede Funktion, mit einer Variablen vorne, einer am Ende, einer in der Mitte und zwei Variablen in der Mitte. Die Grundeinstellung ist dieselbe. Ich verwende nur 100.000.000 Iterationen der Funktion und diese Iterationen werden 100 Mal ausgeführt. Ich habe dieselben Mechanismen verwendet, um eine Optimierung zu verhindern, nämlich die Summe der Längen der resultierenden Zeichenfolgen abzurufen und zu protokollieren. Ich habe auch die benötigte Zeit protokolliert (damit ich erraten kann, wie lange es dauern wird), sie aber auch in einem Array gespeichert.

Danach habe ich für jede Methode den Durchschnitt, das Minimum, das Maximum und die Standardabweichung berechnet.

Hier sind die Ergebnisse:

{ 
  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 
    } 
  } 
}

Werte in t-Objekten sind für Vorlagen, Werte in c-Objekten sind für die Verkettung. startbedeutet, dass sich die Variable am Anfang befindet, in der Mitte in der Mitte, am Ende am Ende und doppelt so viele Variablen. sumist die Summe aller 100 Läufe. avgist der durchschnittliche Lauf, was bedeutet, dass es ist sum / 100. sd Hier ist der einfache Ausweg, Wikipedia (einfaches Englisch) . minund maxsind der minimale bzw. maximale Wert eines Laufs.

Ergebnisse

Es scheint, dass Vorlagen für einzelne Variablen, die sich nicht am Ende einer Zeichenfolge befinden, schneller sind, wenn man bedenkt, dass der Durchschnitt niedriger und das Minimum niedriger ist. Wenn Sie eine Variable am Ende einer Zeichenfolge einfügen oder mehrere Variablen in Ihrer Zeichenfolge haben, ist die Verkettung schneller.

Obwohl sowohl das Minimum als auch der Durchschnitt der Vorlagen in Bezug auf die ersten beiden Bedingungen besser sind als ihre Gegenstücke zur Verkettung, ist die Standardabweichung durchweg schlechter. Der Unterschied scheint mit mehr Variablen zu schrumpfen (mehr Tests erforderlich).

Da die meisten Vorlagen wahrscheinlich nicht nur für eine Variable in einer Zeichenfolge verwendet werden, kann man sicher sagen, dass das Festhalten an der Verkettung zu einer besseren Leistung führt. Aber der Unterschied ist (zumindest für den Moment) sehr gering. Bei 100.000.000 (100 Millionen) Auswertungen mit zwei Variablen beträgt die Differenz lediglich 273,58 ms, etwa eine Viertelsekunde ...


Zweiter Lauf

Der zweite Lauf sieht etwas anders aus. Mit Ausnahme des Maximalwerts, der durchschnittlichen absoluten Abweichung und der Standardabweichung hat jede Messung bewiesen, dass die Verkettung schneller ist als Vorlagen.

Die drei genannten Messungen hatten niedrigere (also bessere) Werte für Vorlagen, wenn sich die Variable am Ende der Zeichenfolge befand oder wenn sich zwei Variablen in der Zeichenfolge befanden.

Hier sind die Ergebnisse:

{
  "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
    }
  }
}

Der Code ist hier

Armin
quelle
Ich führe derzeit eine neue Version meines Testskripts aus. Es enthält die absolute average meanund die median. Außerdem wird die Laufzeit mit 10 zu ersetzenden Variablen bewertet.
Armin
0

Ich denke, dass der obige Benchmark nicht nützlich ist. Das Ergebnis der Interpolation oder Verkettung wurde nicht verwendet. Ja, die Verkettung ist ziemlich schnell, da dort keine Zeichenfolge und die Ergebniszeichenfolge nur Links zu den übergeordneten Zeichenfolgen enthalten. Wenn Sie jedoch die Ergebniszeichenfolge ausprobieren oder mit einer anderen vergleichen, wird die Zeichenfolge in eine ebene Zeichenfolge serialisiert, und ja, es wird einige Zeit dauern. Daher könnte die Interpolation für die CPU- und Speichernutzung effektiver sein als die Verkettung in realen Fällen.

gev
quelle