Чи швидші літерали шаблонів ES6, ніж конкатенація рядків?


82

Чи працює генерація HTML-коду помітно швидше в сучасних браузерах, коли використовується конкатенація рядків або літеральні шаблони в ES6?

Наприклад:

Конкатенація рядків

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

Шаблон буквальний

`<body>
  <article>
    <time datetime='${ date.toISOString() }'>${ date }</time>
  </article>
</body>`

2
Конкатенація рядків є недостатньо повільною, щоб можна було помітити поліпшення. Популярні шаблони, такі як вуса / підкреслення / кермо, у десятки-сотні разів повільніші, ніж конкатенація, або букви шаблонів будуть.
dandavis

1
Теоретично (якщо не скомпільовано JS), літерали шаблонів будуть повільнішими, оскільки "рядок" потрібно аналізувати незалежно від існування заповнювача. Механізм JS проаналізує літерал шаблону один раз, щоб подальше використання було таким же швидким, як об'єднані рядки; це означає, що єдиною різницею буде час, необхідний для синтаксичного аналізу літералу шаблону один раз.
Прекастичний

Конкатенація рядків, звичайно, швидша. Розбір не відбувається. Це просто додавання до довжини рядка. Все-таки я б використовував шаблонні літерали.
Banjocat

Я проголосував, щоб закрити це питання, оскільки результати не є остаточними. 1. Швидкість буде залежати від двигуна - принаймні між Chrome і Firefox може бути (і є на момент написання статті) різниця. 2. Швидкість буде залежати від того, як використовується конкатенація / шаблони та з якими даними. 3. Вони можуть змінюватися між різними версіями одного двигуна. 4. Мікробенчмаркінг може дати оманливі результати. 5. Різниця в швидкості, якщо така є, ймовірно, буде незначною. На момент написання вона є .
ВЛАЗ

Відповіді:


86

Здається, на даний момент конкатенація рядків відбувається швидше: 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

Я тестував, працював на каналі Chrome 43.0.2334.0 (64-розрядна версія), який використовує V8 4.3.31, з #enable-javascript-harmonyувімкненим прапором.

Для довідки, остання версія Node.js (0.12.0 на момент написання статті) використовує V8 3.28.73: https://raw.githubusercontent.com/joyent/node/master/ChangeLog

Я впевнений, що всі можливі оптимізації продуктивності, які можна було б застосувати, ще не застосовані, тому було б розумно очікувати, що продуктивність покращиться, оскільки ES6 наближається до завершення, а ці функції переходять до стабільної гілки.


Редагувати: Дякую за коментарі @ user1329482, @ icl7126, Nicolai Borisik та FesterCluck. Тепер, коли минуло близько 2 років з моменту того, як було задано це питання, підтримка браузера ES6 значно зросла, і відбулася значна кількість оптимізації продуктивності. Ось кілька оновлень .

Редагувати: (лютий 2020 р.) Оновлений результат Chrome на основі коментарів @ JorgeFuentesGonzález та подальшого підтвердження .

У Chrome (станом на 59.0.3035) рядкові літерали ES6 швидші :

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

Оновлення: У Chrome (станом на 79.0.3945) конкатенація рядків відбувається швидше ... Див. Коментарі.

У Firefox (станом на 57.0.0) строкові літерали ES6 швидші :

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

У Safari (станом на 11.0.2) це залежить:

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

При використанні рядка typecast рядкові літерали ES6 швидші . Однак під час виклику функції з літералу в цьому прикладі конкатенація рядків відбувається швидше .

Якщо ви дійсно хочете заглибитися і вам потрібно вичавити кожну краплю продуктивності із Safari, я б запропонував налаштувати тести, які перевіряють, чи неправильно введені змінні та кілька посилань у межах ефективності буквального ефекту.


4
Firefox 50 64bit - ES6 має таку ж швидкість, як конкатенація 1423 816 207 операцій / с.
icl7126

4
Інтерполяції Safari 9.0 ES6 швидше, ніж конкатенація, приблизно 34% Chrome 55, але інтерполяції ES6 набагато повільніші Firefox 50 на тій же швидкості
Микола Борисик

1
Рядки шаблонів тепер на порядок швидші, ніж об’єднання рядків. Див. Версію 14 даного jsperf, він є найбільш точним і неупередженим, який технічно можна отримати, зберігаючи аспекти функцій. Версія 17 є найменш упередженою, але нереальною.
FesterCluck

1
Передане вами посилання jsperf у "Ревізії 1" рядкові літерали все ще повільніше, ніж конкатенація рядків в останньому Chrome: u.teknik.io/nPmY8.png
Хорхе Фуентес Гонсалес

1
Файл результатів jsperf видалено. Завантажив цей без закінчення терміну дії: u.teknik.io/02OVr.png
Хорхе Фуентес Гонсалес

7

Я провів наївний тест на node.js v6.0.0 і отримав майже однакову продуктивність . Оскільки тест такий наївний, не варто вірити цифрам занадто. Але, схоже, компілятор JIT сьогодні генерує дуже оптимізований код. Це дозволить мені вирішити віддавати перевагу шаблонам перед конкатенацією для моїх вузлових програм.

Для довідки це код, який я використовував:

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

І результат був:

strConcat
128888890 - 1904ms
strTemplate
128888890 - 1979ms

Раніше я lenабсолютно переконувався, що оптимізатор не оптимізує весь цикл. У будь-якому випадку, це все ще дуже простий тест. Можливо, хтось може зробити більш вишуканий.


1
Я провів подібний орієнтир і отримав подібний результат. Мій орієнтир включав більше рядків, деякі з яких були довшими. Інтерполяція виконує трохи краще, ніж конкатенація.
rattray

2

Для простого тесту з випадковими числами у вигляді рядка обидва вони наближаються до Chrome & FF

Тестування в Chrome 58.0.3029 / Windows 10

Рядкові літерали найшвидше 2996883 ± 2,36%

Оператор (+) 3 054 078 ± 2,01% найшвидший

Функція Concat 2659391 ± 2,35% на 13% повільніше

Тестування у Firefox 53.0.2 / Windows 10

Рядкові літерали 1 923 835 ± 1,52% найшвидше

Оператор (+) 1 948 503 ± 1,13% найшвидший

Функція Concat 1,810,857 ± 1,81% на 8% повільніше

Перевірте тут на jsperf


1

TL; DR

Конкатенація швидша та послідовніша щодо швидкості. Але різниця дуже мала для 1 або 2 змінних (нижче .3 секунди для 100 мільйонів дзвінків).

Редагувати

Після другого запуску здається, що об'єднання в основному відбувається швидше з двох.


Отже, я хотів розширити відповідь analog-nico, надавши більш обширний тест, який також (трохи) розглядав масштабованість двох функцій.

Код на пастебіні

Я вирішив використовувати чотири тестові приклади для кожної функції, маючи змінну спереду, одну в кінці, одну посередині та дві змінні посередині. Базова настройка однакова. Я просто використовую 100 000 000 ітерацій функції, і ці ітерації виконуються 100 разів. Я використовував ті самі механізми для запобігання оптимізації, а саме отримання суми довжин результуючих рядків та реєстрацію в ньому. Я також зафіксував необхідний час (щоб я здогадався, скільки часу це займе), але також записав його в масив.

Потім я розрахував середнє, мінімальне, максимальне та стандартне відхилення для кожного методу.

Ось результати:

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

значення в t-об'єкти призначені для шаблонів, значення в c-об'єкти призначені для об'єднання. startозначає, що змінна знаходиться на початку, в середині - в середині, в кінці - у кінці, а вдвічі - дві змінні. sum- це сума всіх 100 прогонів. avgце середній пробіг, тобто він є sum / 100. sd Ось найпростіший вихід, wikipedia (проста англійська) . minі maxє мінімальним та максимальним значенням циклу відповідно.

Результати

Здається, шаблони швидші для одиничних змінних, які не розташовані в кінці рядка, враховуючи, що середнє значення нижче, а мінімальне нижче. Якщо ви поміщаєте змінну в кінець рядка або маєте кілька змінних у своєму рядку, конкатенація відбувається швидше.

Незважаючи на те, що мінімальний, а також середнє значення шаблонів є кращими, ніж їх аналоги для конкатенації щодо перших двох умов, стандартне відхилення стабільно гірше. Здається, різниця зменшується з більшою кількістю змінних (потрібно більше тестів).

Оскільки більшість шаблонів, ймовірно, не будуть використовуватися лише для однієї змінної в рядку, варто лише сказати, що дотримання конкатенації дає кращу продуктивність. Але різниця (принаймні наразі) дуже незначна. При оцінках 100 000 000 (100 мільйонів) з двома змінними різниця становить лише 273,58 мс, приблизно чверть секунди ...


Другий запуск

Другий запуск виглядає дещо інакше. За винятком максимального значення, середнього абсолютного відхилення та стандартного відхилення, кожне вимірювання підтверджує, що конкатенація швидша, ніж шаблони.

Три згадані вимірювання мали нижчі (отже, кращі) значення для шаблонів, коли змінна знаходилася в кінці рядка або коли в рядку було дві змінні.

Ось результати:

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

Код тут


Зараз я запускаю нову версію тестового сценарію. Він включає в себе absolute average meanі median. Він також порівняє час виконання з 10 змінними, які потрібно замінити.
Армін

0

Я думаю, що наведений вище показник не є корисним. Результат інтерполяції або конкатенації не використовується. Так що так, конкатенація відбувається досить швидко, оскільки жоден рядок, що справляється там, і рядок результату мають лише посилання на батьківські рядки. Але якщо ви спробуєте результатний рядок або порівняєте з іншим, рядок буде серіалізований до площинного рядка, і, так, це займе деякий час. Отже, інтерполяція може бути ефективнішою для використання центрального процесора та пам'яті, ніж об’єднання в реальних випадках.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.